/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Maze.java
 * Routing tool: Maze routing
 * Original C Code written by Glen M. Lawson
 * Translated to Java by Steven M. Rubin, Sun Microsystems.
 *
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 *
 * 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 3 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.routing;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.HierarchyEnumerator;
import com.sun.electric.database.hierarchy.Nodable;
import com.sun.electric.database.hierarchy.View;
import com.sun.electric.database.network.Netlist;
import com.sun.electric.database.network.Network;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.Geometric;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.EditWindow_;
import com.sun.electric.database.variable.UserInterface;
import com.sun.electric.database.variable.VarContext;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.DRCTemplate;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Generic;
import com.sun.electric.tool.Job;
import com.sun.electric.tool.JobException;
import com.sun.electric.tool.drc.DRC;
import com.sun.electric.tool.user.User;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.FixpTransform;
import com.sun.electric.util.math.GenMath;

/**
 * Class to do maze routing (single wire at a time).
 */
public class Maze
{
	/** bit width of long word */		private static final int SRMAXLAYERS = 64;
	/** maximum size of maze */			private static final int MAXGRIDSIZE = 1000;
	/** max grid points to "excavate" for initial grid access from a port */	private static final int BLOCKAGELIMIT =  10;

	/** draw only on vertical layer */	private static final int HORILAYER = 0;
	/** draw all layers */				private static final int VERTLAYER = 1;
	/** draw all layers */				private static final int ALLLAYERS = 2;

	// common bit masks
	/** grid set (permanent) */			private static final int SR_GSET   = 0x80;
	/** is a wavefront point */			private static final int SR_GWAVE  = 0x20;
	/** is a port */					private static final int SR_GPORT  = 0x10;

	// for maze cells
	/** mask 4 bits */					private static final int SR_GMASK  = 0x0F;
	/** maximum mark value */			private static final int SR_GMAX   = 15;
	/** start value */					private static final int SR_GSTART = 1;

	// miscellaneous defines, return codes from expandWavefront
	private static final int SRSUCCESS  = 0;
	private static final int SRERROR    = 1;
	private static final int SRROUTED   = 2;
	private static final int SRBLOCKED  = 3;
	private static final int SRUNROUTED = 4;

    /** EditingPreferences */                       private final EditingPreferences ep;
	/** the arc used for vertical wires */			private ArcProto mazeVertWire;
	/** the arc used for horizontal wires */		private ArcProto mazeHorizWire;
	/** the pin used to join arcs */				private NodeProto mazeSteinerNode;
	/** the layer used for vertical wires */		private Layer mazeVertLayer;
	/** the layer used for horizontal wires */		private Layer mazeHorizLayer;
	/** half the width of wires (for DRC bloat) */	private double mazeBloat;
	/** the routing region with all data */			private SRREGION theRegion = null;
	/** The netlist for the cell being routed */	private Netlist netList;
	/** Space around net to build search grid */	private int mazeBoundary = 20;

	static class SRDIRECTION {}
	SRDIRECTION SRVERTPREF = new SRDIRECTION();
	SRDIRECTION SRHORIPREF = new SRDIRECTION();
	SRDIRECTION SRALL = new SRDIRECTION();

	static class SRPTYPE {}
	SRPTYPE SRPFIXED = new SRPTYPE();
	SRPTYPE SRPROUTED = new SRPTYPE();

	/**
	 * Defines a routing region to the router
	 */
	static class SRREGION
	{
		/** lower bound of the region */	int        lx, ly;
		/** upper bound of the region */	int        hx, hy;
		/** the array of layers */			SRLAYER [] layers;
		/** the list of nets */				SRNET      nets;

		SRREGION()
		{
			layers = new SRLAYER[SRMAXLAYERS];
		}
	};

	static class SRLAYER
	{
												byte     [] hused, vused;
		/** the layer index (sort order) */		int         index;
		/** the layer mask */					long        mask;
		/** translation value for grid */		int         transx, transy;
		/** the width and height of the grid */	int         wid, hei;
		/** bounds of the current maze */		int         lx, ly, hx, hy;
		/** the two dimensional grid array */	byte   [][] grids;
		/** up/down pointer to next layer */	SRLAYER     up, down;
		/** allowed direction of routes */		SRDIRECTION dir;
	};

	/**
	 * Defines a net in a region. Note that no existing segments of a net is predefined.
	 * Only ports are allowed. Not all nets in region need to be defined.
	 */
	static class SRNET
	{
		/** route state flag */				boolean  routed;
		/** the Network object */			Network  eNet;
		/** the parent region */			SRREGION region;
		/** the list of ports on the net */	SRPORT   ports;
		/** the list of paths */			SRPATH   paths;
		/** the last path in the list */	SRPATH   lastpath;
		/** next in the list of nets */		SRNET    next;
	};

	/**
	 * Defines a port on a net. Bounds of the port are in grid units.
	 * An index of 0 means all layers, 1 = 1st layer, 2 = 2nd layer, 4 - 3rd layer, etc.
	 */
	static class SRPORT
	{
		/** the port index */					int       index;
		/** the layer mask */					long      layers;
		/** true center of the port */			double    cX, cY;
		/** bounds of the port */				int       lx, ly, hx, hy;
		/** where this port comes from */		PortInst  pi;
		/** the master (connected) port */		SRPORT    master;
		/** the list of connected paths */		SRPATH    paths;
		/** the last path in the list */		SRPATH    lastpath;
		/** the current wave front */			SRWAVEPT  wavefront;
		/** the parent net */					SRNET     net;
		/** next in the list of ports */		SRPORT    next;
	};

	static class SRPATH
	{
		/** end points of the path */		int []   x, y;
		/** end pt in world units */		double []   wx, wy;
		/** the layer of the path */		SRLAYER  layer;
		/** end style of the path */		boolean [] end;
		/** the type of path */				SRPTYPE  type;
		/** the port path is attached to */	SRPORT   port;
		/** next in the list of paths */	SRPATH   next;

		SRPATH()
		{
			x = new int[2];
			y = new int[2];
			wx = new double[2];
			wy = new double[2];
			end = new boolean[2];
		}
	};

	/**
	 * routing data types
	 */
	static class SRWAVEPT
	{
		/** x y location of the point */		int      x, y;
		/** the layer for the point */			SRLAYER  layer;
		/** the port for this point */			SRPORT   port;
		/** next/prev in the list of points */	SRWAVEPT next, prev;
	};

	/************************************* TOP-LEVEL CONTROL CODE *************************************/

	public static void mazeRoute()
	{
		UserInterface ui = Job.getUserInterface();
		Cell cell = ui.needCurrentCell();
		if (cell == null) return;
		EditWindow_ wnd = ui.getCurrentEditWindow_();
		if (wnd == null) return;

		Netlist netList = cell.getNetlist();
		if (netList == null)
		{
			System.out.println("Sorry, a deadlock aborted routing (network information unavailable).  Please try again");
			return;
		}
		Set<Network> nets = wnd.getHighlightedNetworks();

		// turn the set of Nets back into a list of Arcs to route
		List<ArcInst> arcsToRoute = new ArrayList<ArcInst>();
		for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
		{
			ArcInst ai = it.next();
			if (ai.getProto() != Generic.tech().unrouted_arc) continue;
			Network net = netList.getNetwork(ai, 0);
			if (nets.contains(net))
			{
				arcsToRoute.add(ai);
				nets.remove(net);
			}
		}

		// turn off highlighting
		wnd.clearHighlighting();
		wnd.finishedHighlighting();

		new MazeRouteJob(cell, arcsToRoute);
	}

	private static class MazeRouteJob extends Job
	{
		private Cell cell;
		private List<ArcInst> arcs;

		protected MazeRouteJob(Cell cell, List<ArcInst> arcs)
		{
			super("Maze Route", Routing.getRoutingTool(), Job.Type.CHANGE, null, null, Job.Priority.USER);
			this.cell = cell;
			this.arcs = arcs;
			startJob();
		}

        @Override
		public boolean doIt() throws JobException
		{
			Maze router = new Maze(getEditingPreferences());
			router.routeSelected(cell, arcs);
			return true;
		}
	}

    public Maze(EditingPreferences ep) {
        this.ep = ep;
    }

	/**
	 * This is the public interface for Maze Routing when done in batch mode.
	 * It replaces the selected unrouted arcs with routed geometry
	 * @param cell the cell to be Maze-routed.
	 */
	public void routeSelected(Cell cell, List<ArcInst> arcsToRoute)
	{
		if (arcsToRoute.size() == 0)
		{
			netList = cell.getNetlist();
			Set<Network> nets = new HashSet<Network>();
			for (Iterator<ArcInst> it = cell.getArcs(); it.hasNext();)
			{
				ArcInst ai = it.next();
				if (ai.getProto() != Generic.tech().unrouted_arc) continue;
				Network net = netList.getNetwork(ai, 0);
				if (nets.contains(net)) continue;
				arcsToRoute.add(ai);
				nets.add(net);
			}
		}

		// now route each arc
		for(ArcInst ai : arcsToRoute)
		{
			// reacquire the netlist for the current configuration
			netList = cell.getNetlist();

			// route the unrouted arc
			Network net = netList.getNetwork(ai, 0);
			Map<Network,ArcInst[]> arcMap = null;
			if (cell.getView() != View.SCHEMATIC) arcMap = netList.getArcInstsByNetwork();
			if (routeNet(net, arcMap)) continue;
		}
	}

	/**
	 * Method to reroute a network.
	 * @param net the network to route.
	 * @return true on error.
	 */
	private boolean routeNet(Network net, Map<Network,ArcInst[]> arcMap)
	{
		// get extent of net and mark nodes and arcs on it
		HashSet<ArcInst> arcsToDelete = new HashSet<ArcInst>();
		HashSet<NodeInst> nodesToDelete = new HashSet<NodeInst>();
		List<Connection> netEnds = Routing.findNetEnds(net, arcMap, arcsToDelete, nodesToDelete, netList, true);
		int count = netEnds.size();
		if (count == 0) return false;
		if (count != 2)
		{
			System.out.println("Error: Network " + net.describe(false) + " has " + count +
				" ends, but can only route nets with 2 ends");
			return true;
		}

		// determine bounds of this networks
		Cell cell = net.getParent();
		Rectangle2D routingBounds = null;
		for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
		{
			ArcInst ai = it.next();
			Network aNet = netList.getNetwork(ai, 0);
			if (aNet != net) continue;
			Rectangle2D arcBounds = ai.getBounds();
			if (routingBounds == null)
			{
				routingBounds = new Rectangle2D.Double(arcBounds.getMinX(), arcBounds.getMinY(), arcBounds.getWidth(), arcBounds.getHeight());
			} else
			{
				Rectangle2D.union(routingBounds, arcBounds, routingBounds);
			}
		}
		if (routingBounds == null)
		{
			System.out.println("Internal error: no bounding area for routing");
			return true;
		}

		// determine arc to route
		ArcProto routingArc = User.getUserTool().getCurrentArcProto();
		if (routingArc == Generic.tech().unrouted_arc) routingArc = null;
		if (routingArc != null)
		{
			// see if the default arc can be used to route
			for(int i=0; i<count; i++)
			{
				Connection con = netEnds.get(i);
				boolean found = con.getPortInst().getPortProto().getBasePort().connectsTo(routingArc);
				if (!found) { routingArc = null;   break; }
			}
		}

		// if current arc cannot run, look for any that can
		if (routingArc == null)
		{
			// check out all arcs for use in this route
			HashSet<ArcProto> arcsUsed = new HashSet<ArcProto>();
			for(int i=0; i<count; i++)
			{
				Connection con = netEnds.get(i);
				ArcProto [] connections = con.getPortInst().getPortProto().getBasePort().getConnections();
				for(int j = 0; j < connections.length; j++)
				{
					ArcProto ap = connections[j];
					if (ap.getTechnology() == Generic.tech()) continue;
					arcsUsed.add(ap);
				}
			}
			for(Iterator<Technology> it = Technology.getTechnologies(); it.hasNext(); )
			{
				Technology tech = it.next();
				if (tech == Generic.tech()) continue;
				for(Iterator<ArcProto> aIt = tech.getArcs(); aIt.hasNext(); )
				{
					ArcProto ap = aIt.next();
					if (!arcsUsed.contains(ap)) continue;
					boolean allFound = true;
					for(int i=0; i<count; i++)
					{
						Connection con = netEnds.get(i);
						boolean found = con.getPortInst().getPortProto().getBasePort().connectsTo(ap);
						if (!found) { allFound = false;   break; }
					}
					if (allFound)
					{
						routingArc = ap;
						break;
					}
				}
				if (routingArc != null) break;
			}
		}
		if (routingArc == null)
		{
			System.out.println("Cannot find wire to route");
			return true;
		}
		mazeVertWire = routingArc;
		mazeHorizWire = routingArc;
		mazeSteinerNode = routingArc.findPinProto();
        mazeVertLayer = mazeVertWire.getLayer(0);
        mazeHorizLayer = mazeHorizWire.getLayer(0);
//		Iterator<ArcLayer> it = mazeVertWire.getArcLayers();
//		mazeVertLayer = it.next().getLayer();
//		it = mazeHorizWire.getArcLayers();
//		mazeHorizLayer = it.next().getLayer();
		mazeBloat = 0;
		double wid = 10, len = 100;
		DRCTemplate rule = DRC.getSpacingRule(mazeVertLayer, null, mazeVertLayer, null, false, 0, wid, len);
		if (rule != null) mazeBloat = rule.getValue(0) + mazeVertWire.getDefaultLambdaBaseWidth(ep)/2;

		// now create the routing region
		int lx = (int)routingBounds.getMinX();
		int hx = (int)routingBounds.getMaxX();
		int ly = (int)routingBounds.getMinY();
		int hy = (int)routingBounds.getMaxY();
		SRREGION region = defineRegion(cell, net, lx, ly, hx, hy, arcsToDelete, nodesToDelete);
		if (region == null) return true;

		// create the net in the region
		SRNET srnet = addNet(region, net);
		if (srnet == null)
		{
			System.out.println("Could not allocate internal net");
			return true;
		}

		// add the ports to the net
		for(int i=0; i<count; i++)
		{
			Connection con = netEnds.get(i);
			PortInst pi = con.getPortInst();
			double cXD = con.getLocation().getX();
			double cYD = con.getLocation().getY();
			SRPORT fsp = addPort(srnet, determineDir(pi.getNodeInst(), cXD, cYD), cXD, cYD, pi);
			if (fsp == null)
			{
				System.out.println("Port could not be defined");
				return true;
			}
		}
//dumpLayer("BEFORE ROUTING", region, 0xFF);

		// do maze routing
		if (routeANet(srnet))
		{
			System.out.println("Could not route net " + srnet.eNet.describe(false));
			return true;
		}

		// extract paths to create arcs
		if (extractPaths(cell, srnet))
		{
			System.out.println("Could not create paths");
			return true;
		}

		// remove marked networks
		for(ArcInst ai : arcsToDelete)
		{
			ai.kill();
		}
        cell.killNodes(nodesToDelete);

		return false;
	}

	/************************************* CODE TO TRAVERSE THE MAZE BUFFER *************************************/

	private boolean routeANet(SRNET net)
	{
		// initialize all layers and ports for this route
		boolean ret = false;
		for (int index = 0; index < SRMAXLAYERS; index++)
		{
			SRLAYER layer = net.region.layers[index];
			if (layer != null)
			{
				// unused bounds
				layer.lx = layer.wid; layer.hx = -1;
				layer.ly = layer.hei; layer.hy = -1;

				// now set the vertical and horizontal preference
				int count = 0;
				for (int x = 0; x < layer.wid; x++)
				{
					if ((layer.vused[x] & SR_GSET) != 0)
					{
						if (count != 0)
						{
							for (int i = 1, prio = count>>1; i <= count; i++, prio--)
								layer.vused[x-i] = (byte)Math.abs(prio);
							count = 0;
						}
					} else count++;
				}

				// get the remainder
				if (count != 0)
				{
					for (int i = 1, prio = count>>1; i < count; i++)
					{
						layer.vused[layer.wid-i] = (byte)Math.abs(prio);
						if (--prio == 0 && (count&1) == 0) prio = -1;
					}
				}

				// now horizontal tracks
				count = 0;
				for (int y = 0; y < layer.hei; y++)
				{
					if ((layer.hused[y] & SR_GSET) != 0)
					{
						if (count != 0)
						{
							for (int i = 1, prio = count>>1; i <= count; i++, prio--)
								layer.hused[y-i] = (byte)Math.abs(prio);
							count = 0;
						}
					} else count++;
				}

				// and the remainder ...
				if (count != 0)
				{
					for (int i = 1, prio = count>>1; i < count; i++)
					{
						layer.hused[layer.hei-i] = (byte)Math.abs(prio);
						if (--prio == 0 && (count&1) == 0) prio = -1;
					}
				}
			}
		}

		// prepare each port for routing
		int pCount = 0;
		for (SRPORT port = net.ports; port != null; port = port.next, pCount++)
		{
			createWavefront(port);
		}

		// now begin routing until all ports merged
		int code = SR_GSTART;
		do
		{
			if (++code > SR_GMAX) code = SR_GSTART;
			int blocked = 0;
			int status = 0;
			SRPORT port = null;
			for (port = net.ports; port != null; port = port.next)
			{
				// if part of other wavefront, get the next one
				if (port.master != null) continue;

				// expand the wavefront
				status = expandWavefront(port, code);
				if (status == SRERROR) return true;
				if (status == SRROUTED) break;
				if (status == SRBLOCKED) blocked++;
			}

			// check for successful routing
			if (port != null && status == SRROUTED)
			{
				// now clear routing region and restart expansion
				clearMaze(net);
				if (--pCount > 1)
				{
					// prepare each port for routing
					for (port = net.ports; port != null; port = port.next)
						createWavefront(port);
				}
				code = SR_GSTART;
			} else
			{
				// check for blocked net
				if (blocked == pCount)
				{
					ret = true;
					clearMaze(net);
					break;
				}
			}
		} while (pCount > 1);

		// move all the port paths to the net
		for (SRPORT port = net.ports; port != null; port = port.next)
		{
			if (net.lastpath == null)
			{
				net.paths = port.paths;
				if (net.paths != null) net.lastpath = port.lastpath;
			} else
			{
				net.lastpath.next = port.paths;
				if (net.lastpath.next != null) net.lastpath = port.lastpath;
			}
			port.paths = null;
			port.lastpath = null;
		}
		if (!ret) net.routed = true;
		return ret;
	}

	private void createWavefront(SRPORT port)
	{
		SRPORT master = port.master;
		if (master == null) master = port;

		// first assign each layer of the port as wavefront points
		for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
		{
			if ((mask & port.layers) != 0)
			{
				SRLAYER layer = port.net.region.layers[index];
				if (layer == null) continue;

				// convert to grid points
				int lx = getGridX(port.lx, layer);   int ly = getGridY(port.ly, layer);
				int hx = getGridX(port.hx, layer);   int hy = getGridY(port.hy, layer);
				if (lx >= layer.wid || hx < 0 || ly >= layer.hei || hy < 0) continue;

				// clip to window
				if (lx < 0) lx = 0;
				if (hx >= layer.wid) hx = layer.wid - 1;
				if (ly < 0) ly = 0;
				if (hy >= layer.hei) hy = layer.hei - 1;

				/*
				 * added detection of immediate blockage ... smr
				 */
				boolean onEdge = false;
				for (int x = lx; x <= hx; x++)
				{
					for (int y = ly; y <= hy; y++)
					{
						addWavePoint(master, layer, x, y, SR_GSTART);
						if (x < layer.wid-1 && layer.grids[x+1][y] == 0) onEdge = true;
						if (x > 0 && layer.grids[x-1][y] == 0) onEdge = true;
						if (y < layer.hei-1 && layer.grids[x][y+1] == 0) onEdge = true;
						if (y > 0 && layer.grids[x][y-1] == 0) onEdge = true;
					}
				}
				if (!onEdge)
				{
					// port is inside of blocked area: search for opening
					int cx = (lx + hx) / 2;
					int cy = (ly + hy) / 2;
					PrimitivePort prP = port.pi.getPortProto().getBasePort();
					int angRange = prP.getAngleRange();
					int ang = prP.getAngle();
					NodeInst ni = port.pi.getNodeInst();
					ang += (ni.getAngle()+5) / 10;
					if (ni.isMirroredAboutXAxis() != ni.isMirroredAboutYAxis()) { ang = 270 - ang; if (ang < 0) ang += 360; }
					if (angleDiff(ang, 0) <= angRange)
					{
						// port faces right
						for(int spread=1; spread<BLOCKAGELIMIT; spread++)
						{
							if (hx+spread >= layer.wid) break;
							if (layer.grids[hx+spread][cy] == 0) { onEdge = true;   break; }
							layer.grids[hx+spread][cy] = 0;
						}
					}
					if (angleDiff(ang, 90) <= angRange)
					{
						// port faces up
						for(int spread=1; spread<BLOCKAGELIMIT; spread++)
						{
							if (hy+spread >= layer.hei) break;
							if (layer.grids[cx][hy+spread] == 0) { onEdge = true;   break; }
							layer.grids[cx][hy+spread] = 0;
						}
					}
					if (angleDiff(ang, 180) <= angRange)
					{
						// port faces left
						for(int spread=1; spread<BLOCKAGELIMIT; spread++)
						{
							if (lx-spread < 0) break;
							if (layer.grids[lx-spread][cy] == 0) { onEdge = true;   break; }
							layer.grids[lx-spread][cy] = 0;
						}
					}
					if (angleDiff(ang, 270) <= angRange)
					{
						// port faces down
						for(int spread=1; spread<BLOCKAGELIMIT; spread++)
						{
							if (ly-spread < 0) break;
							if (layer.grids[cx][ly-spread] == 0) { onEdge = true;   break; }
							layer.grids[cx][ly-spread] = 0;
						}
					}
					if (!onEdge)
					{
						System.out.println("Node " + ni.describe(false) + ", port " +
							port.pi.getPortProto().getName() + " is blocked");
						return;
					}
				}
			}
		}

		// now assign the paths of the port
		for (SRPATH path = port.paths; path != null; path = path.next)
		{
			// note paths are always in the working area
			if (path.x[0] == path.x[1])
			{
				// vertical path
				int dy = -1;
				if (path.y[0] < path.y[1]) dy = 1;
				for (int x = path.x[0], y = path.y[0];
					(dy < 0) ? y >= path.y[1] : y <= path.y[1]; y += dy)
				{
					addWavePoint(master, path.layer, x, y, SR_GSTART);
				}
			} else if (path.y[0] == path.y[1])
			{
				// horizontal path
				int dx = -1;
				if (path.x[0] < path.x[1]) dx = 1;
				for (int y = path.y[0], x = path.x[0];
					(dx < 0) ? x >= path.x[1] : x <= path.x[1]; x += dx)
				{
					addWavePoint(master, path.layer, x, y, SR_GSTART);
				}
			} else
			{
				// a 45 degree path, note assume x,y difference is equal
				int dx = -1;
				if (path.x[0] < path.x[1]) dx = 1;
				int dy = -1;
				if (path.y[0] < path.y[1]) dy = 1;

				for (int x = path.x[0], y = path.y[0];
					(dx < 0) ? x >= path.x[1] : x <= path.x[1]; x += dx, y += dy)
				{
					addWavePoint(master, path.layer, x, y, SR_GSTART);
				}
			}
		}
	}

	/**
	 * routing commands
	 */
	private void addWavePoint(SRPORT port, SRLAYER layer, int x, int y, int code)
	{
		SRWAVEPT wavePt = new SRWAVEPT();
		wavePt.x = x;
		wavePt.y = y;

		// set the grid
		layer.grids[x][y] = (byte)((layer.grids[x][y] & ~SR_GMASK) | code | SR_GWAVE);
		wavePt.layer = layer;

		// set maze bounds
		if (layer.lx > x) layer.lx = x;
		if (layer.hx < x) layer.hx = x;
		if (layer.ly > y) layer.ly = y;
		if (layer.hy < y) layer.hy = y;

		wavePt.prev = null;
		if (port.master != null)
		{
			wavePt.port = port.master;
			wavePt.next = port.master.wavefront;
			port.master.wavefront = wavePt;
		} else
		{
			wavePt.port = port;
			wavePt.next = port.wavefront;
			port.wavefront = wavePt;
		}
		if (wavePt.next != null)
			wavePt.next.prev = wavePt;
	}

	private int angleDiff(int ang1, int ang2)
	{
		int diff = Math.abs(ang1 - ang2);
		if (diff > 180) diff = 360 - diff;
		return diff;
	}

	private int expandWavefront(SRPORT port, int code)
	{
		// begin expansion of all wavepts
		// disconnect wavepts from the port
		SRWAVEPT wavePt = port.wavefront;
		if (wavePt == null) return SRBLOCKED;

		SRWAVEPT next = null;
		int status = SRSUCCESS;
		boolean found = false;
		int bx = 0, by = 0;
		SRLAYER bLayer = null;
		SRWAVEPT bWavePt = new SRWAVEPT();
		for (wavePt = port.wavefront; wavePt != null ; wavePt = next)
		{
			boolean connected = false;
			SRLAYER layer = wavePt.layer;
			if (layer.dir == SRALL || layer.dir == SRHORIPREF)
			{
				// try horizontal route
				int x = wavePt.x + 1;
				if (x != layer.wid)
				{
					status = examinePoint(port, layer, x, wavePt.y, code);
					if (status == SRROUTED)
					{
						// "bWavePt" used in proper order
						if (!found || (layer.hused[bWavePt.y] & SR_GSET) != 0 ||
							((layer.hused[wavePt.y] & SR_GSET) == 0 &&
							layer.hused[wavePt.y] < layer.hused[bWavePt.y]))
						{
							bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
							bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
							bx = x;  by = wavePt.y;
							bLayer = layer;
						}
						found = true;
						connected = true;
					}
				}
				if (!connected && (x = wavePt.x - 1) >= 0)
				{
					status = examinePoint(port, layer, x, wavePt.y, code);
					if (status == SRROUTED)
					{
						if (!found ||
							(layer.hused[bWavePt.y] & SR_GSET) != 0  ||
							((layer.hused[wavePt.y] & SR_GSET) == 0 &&
							layer.hused[wavePt.y] < layer.hused[bWavePt.y]))
						{
							bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
							bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
							bx = x; by = wavePt.y;
							bLayer = layer;
						}
						found = true;
						connected = true;
					}
				}
			}
			if (layer.dir == SRALL || layer.dir == SRVERTPREF)
			{
				// try vertical route
				int y = wavePt.y + 1;
				if (!connected && y != layer.hei)
				{
					status = examinePoint(port, layer, wavePt.x, y, code);
					if (status == SRROUTED)
					{
						if (!found ||
							(layer.vused[bWavePt.x] & SR_GSET) != 0  ||
							((layer.vused[wavePt.x] & SR_GSET) == 0 &&
							layer.vused[wavePt.x] < layer.vused[bWavePt.x]))
						{
							bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
							bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
							bx = wavePt.x; by = y;
							bLayer = layer;
						}
						found = true;
						connected = true;
					}
				}
				if (!connected && (y = wavePt.y - 1) >= 0)
				{
					status = examinePoint(port, layer, wavePt.x, y, code);
					if (status == SRROUTED)
					{
						if (!found ||
							(layer.vused[bWavePt.x] & SR_GSET) != 0  ||
							((layer.vused[wavePt.x] & SR_GSET) == 0 &&
							layer.vused[wavePt.x] < layer.vused[bWavePt.x]))
						{
							bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
							bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
							bx = wavePt.x; by = y;
							bLayer = layer;
						}
						found = true;
						connected = true;
					}
				}
			}
			if (!connected && layer.up != null)
			{
				// try via up
				status = examinePoint(port, layer.up, wavePt.x, wavePt.y, code);
				if (status == SRROUTED)
				{
					if (!found)
					{
						bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
						bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
						bx = wavePt.x; by = wavePt.y;
						bLayer = layer.up;
					}
					found = true;
					connected = true;
				}
			}
			if (!connected && layer.down != null)
			{
				// try via down
				status = examinePoint(port, layer.down, wavePt.x, wavePt.y, code);
				if (status == SRROUTED)
				{
					if (!found)
					{
						bWavePt.x = wavePt.x;   bWavePt.y = wavePt.y;
						bWavePt.layer = wavePt.layer;   bWavePt.port = wavePt.port;
						bx = wavePt.x; by = wavePt.y;
						bLayer = layer.down;
					}
					found = true;
					connected = true;
				}
			}
			next = wavePt.next;

			// now release this wavept
			if (wavePt.prev == null)
			{
				port.wavefront = wavePt.next;
				if (wavePt.next != null)
					wavePt.next.prev = null;
			} else
			{
				wavePt.prev.next = wavePt.next;
				if (wavePt.next != null)
					wavePt.next.prev = wavePt.prev;
			}

			// set the grid point to a core point
			if (!connected) layer.grids[wavePt.x][wavePt.y] &= ~SR_GWAVE;
		}

		if (found)
			return initPath(port, bLayer, bWavePt, bx, by);

		if (port.wavefront == null) return SRBLOCKED;
		return SRSUCCESS;
	}

	private int initPath(SRPORT port, SRLAYER layer, SRWAVEPT wavePt, int x, int y)
	{
		// search for others
		SRPORT target = null;
		SRWAVEPT tWavePt = null;
		for (target = port.net.ports; target != null; target = target.next)
		{
			if (target == port || target.master != null) continue;
			tWavePt = searchWavefront(target, layer, x, y);
			if (tWavePt != null) break;
		}
		if (target == null) return SRERROR;

		/* now move the target's paths to the master. This is done to retain the
		 * original path creation order (port out), and also insures the existance
		 * of the arc in t-junction connections
		 */
		if (port.lastpath != null)
		{
			if ((port.lastpath.next = target.paths) != null)
				port.lastpath = target.lastpath;
		} else
		{
			// this should never happen
			port.paths = target.paths;
			port.lastpath = target.lastpath;
		}
		target.paths = null;
		target.lastpath = null;

		// connect the port with target
		SRPATH path = null;
		if (wavePt.layer == tWavePt.layer)
		{
			path = getPath(port, layer, false, false, wavePt.x, wavePt.y, tWavePt.x, tWavePt.y);
		}

		// now create paths to each target point
		if (findPaths(wavePt, path) != SRSUCCESS) return SRERROR;
		if (findPaths(tWavePt, path) != SRSUCCESS) return SRERROR;

		// now set the target master
		target.master = port;

		// now scan through all ports and change target as master to port
		for (SRPORT sport = port.net.ports; sport != null; sport = sport.next)
		{
			if (sport.master == target) sport.master = port;
		}

		// now move the rest of the paths to the master port
		if (port.lastpath != null)
		{
			if ((port.lastpath.next = target.paths) != null)
				port.lastpath = target.lastpath;
		} else
		{
			// this should never happen
			port.paths = target.paths;
			port.lastpath = target.lastpath;
		}
		target.paths = null;
		target.lastpath = null;

		return SRROUTED;
	}

	/**
	 * Method to find the path through the maze to the target point.
	 * Will merge the starting point path with the first internal path if possible.
	 */
	private int findPaths(SRWAVEPT wavePt, SRPATH path)
	{
		// Start scan from the first point
		int sx = wavePt.x;
		int sy = wavePt.y;

		SRLAYER layer = wavePt.layer;
		int code = layer.grids[sx][sy] & SR_GMASK;
		if (code == SR_GSTART) code = SR_GMAX;
			else code--;
		int pStart = 0;
		int ex = 0, ey = 0;
		for(;;)
		{
			int dx = 0, dy = 0;

			// scan around the point
			for(;;)
			{
				if (pStart == 1)
				{
					// always try to jump layer after the first path
					// now try jumping layers
					if (layer.up != null)
					{
						ex = sx; ey = sy;
						int status = testPoint(layer.up.grids[ex][ey], code);
						if (status == SRROUTED) return SRSUCCESS;
						if (status == SRSUCCESS)
						{
							layer = layer.up;
							if (code == SR_GSTART) code = SR_GMAX;
								else code--;
							pStart = 2;
							continue;
						}
					}
					if (layer.down != null)
					{
						ex = sx; ey = sy;
						int status = testPoint(layer.down.grids[ex][ey], code);
						if (status == SRROUTED) return SRSUCCESS;
						if (status == SRSUCCESS)
						{
							layer = layer.down;
							if (code == SR_GSTART) code = SR_GMAX;
								else code--;
							pStart = 2;
							continue;
						}
					}
				}
				if (layer.dir == SRALL || layer.dir == SRHORIPREF)
				{
					// try right first
					if ((ex = sx + 1) != layer.wid)
					{
						ey = sy;
						int status = testPoint(layer.grids[ex][ey], code);
						if (status == SRROUTED)
						{
							// check for common original path
							if (path != null && path.layer == layer &&
								path.y[0] == path.y[1] && path.y[0] == ey &&
									Math.max(path.x[0], path.x[1]) == sx)
							{
								if (path.x[0] == sx)
								{
									path.x[0] = ex;
									path.wx[0] = getWorldX(ex, layer);
								} else
								{
									path.x[1] = ex;
									path.wx[1] = getWorldX(ex, layer);
								}
								setLine(path.layer, SR_GPORT | SR_GSET,
									path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
								return SRSUCCESS;
							}
							getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
							return SRSUCCESS;
						} else if (status == SRSUCCESS)
						{
							dx = 1;
							break;
						}
					}
				}
				if (layer.dir == SRALL || layer.dir == SRVERTPREF)
				{
					// try down first
					if ((ey = sy - 1) >= 0)
					{
						ex = sx;
						int status = testPoint(layer.grids[ex][ey], code);
						if (status == SRROUTED)
						{
							// check for common original path
							if (path != null && path.layer == layer &&
								path.x[0] == path.x[1] && path.x[0] == ex &&
									Math.min(path.y[0], path.y[1]) == sy)
							{
								if (path.y[0] == sy)
								{
									path.y[0] = ey;
									path.wy[0] = getWorldY(ey, layer);
								} else
								{
									path.y[1] = ey;
									path.wy[1] = getWorldY(ey, layer);
								}
								setLine(path.layer, SR_GPORT | SR_GSET,
									path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
								return SRSUCCESS;
							}
							getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
							return SRSUCCESS;
						} else if (status == SRSUCCESS)
						{
							dy = -1;
							break;
						}
					}
				}
				if (layer.dir == SRALL || layer.dir == SRHORIPREF)
				{
					// try left
					if ((ex = sx - 1) >= 0)
					{
						ey = sy;
						int status = testPoint(layer.grids[ex][ey], code);
						if (status == SRROUTED)
						{
							// check for common original path
							if (path != null && path.layer == layer &&
								path.y[0] == path.y[1] && path.y[0] == ey &&
									Math.min(path.x[0], path.x[1]) == sx)
							{
								if (path.x[0] == sx)
								{
									path.x[0] = ex;
									path.wx[0] = getWorldX(ex, layer);
								} else
								{
									path.x[1] = ex;
									path.wx[1] = getWorldX(ex, layer);
								}
								setLine(path.layer, SR_GPORT | SR_GSET,
									path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
								return SRSUCCESS;
							}
							getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
							return SRSUCCESS;
						} else if (status == SRSUCCESS)
						{
							dx = -1;
							break;
						}
					}
				}
				if (layer.dir == SRALL || layer.dir == SRVERTPREF)
				{
					// try up
					if ((ey = sy + 1) != layer.hei)
					{
						ex = sx;
						int status = testPoint(layer.grids[ex][ey], code);
						if (status == SRROUTED)
						{
							// check for common original path
							if (path != null && path.layer == layer &&
								path.x[0] == path.x[1] && path.x[0] == ex &&
									Math.max(path.y[0], path.y[1]) == sy)
							{
								if (path.y[0] == sy)
								{
									path.y[0] = ey;
									path.wy[0] = getWorldY(ey, layer);
								} else
								{
									path.y[1] = ey;
									path.wy[1] = getWorldY(ey, layer);
								}
								setLine(path.layer, SR_GPORT | SR_GSET,
									path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
								return SRSUCCESS;
							}
							getPath(wavePt.port, layer, false, true, sx, sy, ex, ey);
							return SRSUCCESS;
						} else if (status == SRSUCCESS)
						{
							dy = 1;
							break;
						}
					}
				}

				// now try jumping layers
				if (pStart == 0)
				{
					if (layer.up != null)
					{
						ex = sx; ey = sy;
						int status = testPoint(layer.up.grids[ex][ey], code);
						if (status == SRROUTED) return SRSUCCESS;
						if (status == SRSUCCESS)
						{
							layer = layer.up;
							if (code == SR_GSTART) code = SR_GMAX;
								else code--;
							continue;
						}
					}
					if (layer.down != null)
					{
						ex = sx; ey = sy;
						int status = testPoint(layer.down.grids[ex][ey], code);
						if (status == SRROUTED) return SRSUCCESS;
						if (status == SRSUCCESS)
						{
							layer = layer.down;
							if (code == SR_GSTART) code = SR_GMAX;
								else code--;
							continue;
						}
					}
				}

				// could not start route, just return
				return SRERROR;
			}

			// set path started
			pStart = 1;

			// now continue scan until the end of the path
			for(;;)
			{
				if (code == SR_GSTART) code = SR_GMAX;
					else code--;
				if (dx != 0)
				{
					// horizontal scan
					int nx = ex + dx;
					int ny = ey;
					int status = testPoint(layer.grids[nx][ny], code);
					if (status == SRROUTED)
					{
						// check for common original path
						if (path != null && path.layer == layer &&
							path.y[0] == path.y[1] && path.y[0] == sy &&
							((dx < 0 && Math.min(path.x[0], path.x[1]) == sx) ||
							(dx > 0 && Math.max(path.x[0], path.x[1]) == sx)))
						{
							if (path.x[0] == sx)
							{
								path.x[0] = nx;
								path.wx[0] = getWorldX(nx, layer);
							} else
							{
								path.x[1] = nx;
								path.wx[1] = getWorldX(nx, layer);
							}
							setLine(path.layer, SR_GPORT | SR_GSET,
								path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
							return SRSUCCESS;
						}
						getPath(wavePt.port, layer, false, true, sx, sy, nx, ny);
						return SRSUCCESS;
					} else if (status == SRSUCCESS)
					{
						ex = nx;
						continue;
					}
				}
				if (dy != 0)
				{
					// veritical scan
					int nx = ex;
					int ny = ey + dy;
					int status = testPoint(layer.grids[nx][ny], code);
					if (status == SRROUTED)
					{
						// check for common original path
						if (path != null && path.layer == layer &&
							path.x[0] == path.x[1] && path.x[0] == sx &&
							((dy < 0 && Math.min(path.y[0], path.y[1]) == sy) ||
							(dy > 0 && Math.max(path.y[0], path.y[1]) == sy)))
						{
							if (path.y[0] == sy)
							{
								path.y[0] = ny;
								path.wy[0] = getWorldY(ny, layer);
							} else
							{
								path.y[1] = ny;
								path.wy[1] = getWorldY(ny, layer);
							}
							setLine(path.layer, SR_GPORT | SR_GSET,
								path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
							return SRSUCCESS;
						}
						getPath(wavePt.port, layer, false, true, sx, sy, nx, ny);
						return SRSUCCESS;
					} else if (status == SRSUCCESS)
					{
						ey = ny;
						continue;
					}
				}

				// end of the path, add and break loop

				// check for common original path
				if (path != null && path.layer == layer &&
					// horizontal path check
					((sy == ey && path.y[0] == path.y[1] && path.y[0] == ey &&
					(path.x[0] == sx || path.x[1] == sx)) ||
					// vertical path check
					(sx == ex && path.x[0] == path.x[1] && path.x[0] == ex &&
					(path.y[0] == sy || path.y[1] == sy))))
				{
					// vertical path ?
					if (sx == ex)
					{
						if (sy == path.y[0])
						{
							path.y[0] = ey;
							path.wy[0] = getWorldY(ey, layer);
						} else
						{
							path.y[1] = ey;
							path.wy[1] = getWorldY(ey, layer);
						}
					}

					// horizontal path ?
					else
					{
						if (sx == path.x[0])
						{
							path.x[0] = ex;
							path.wx[0] = getWorldX(ex, layer);
						} else
						{
							path.x[1] = ex;
							path.wx[1] = getWorldX(ex, layer);
						}
					}

					setLine(path.layer, SR_GPORT | SR_GSET,
						path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);
					path = null;
				} else
				{
					getPath(wavePt.port, layer, false, false, sx, sy, ex, ey);
				}
				sx = ex;
				sy = ey;
				break;
			}
		}
	}

	private int testPoint(int pt, int code)
	{
		// don't check other wavefront points
		if ((pt & SR_GWAVE) == 0)
		{
			// check for permanent grid object (blockage or port)
			if ((pt & SR_GSET) != 0)
			{
				// check for port
				if ((pt & SR_GPORT) != 0 && (pt & SR_GMASK) == code) return SRROUTED;
			} else
			{
				// not permanent, check for matching code
				if ((pt & SR_GMASK) == code) return SRSUCCESS;
			}
		}
		return SRUNROUTED;
	}

	private SRPATH getPath(SRPORT port, SRLAYER layer, boolean e1, boolean e2, int x1, int y1, int x2, int y2)
	{
		SRPATH path = new SRPATH();

		path.x[0] = x1; path.y[0] = y1;
		path.x[1] = x2; path.y[1] = y2;
		path.end[0] = e1; path.end[1] = e2;
		path.layer = layer;
		path.port = port;
		path.type = SRPROUTED;

		path.wx[0] = getWorldX(x1, layer); path.wy[0] = getWorldY(y1, layer);
		path.wx[1] = getWorldX(x2, layer); path.wy[1] = getWorldY(y2, layer);

		// insert the path at the end of the list
		path.next = null;
		if (port.lastpath == null) port.paths = path; else
			port.lastpath.next = path;
		port.lastpath = path;

		// now draw it
		setLine(path.layer, SR_GPORT | SR_GSET,
			path.wx[0], path.wy[0], path.wx[1], path.wy[1], true);

		return path;
	}

	private int examinePoint(SRPORT port, SRLAYER layer, int x, int y, int code)
	{
		// point is set
		if ((layer.grids[x][y] & SR_GWAVE) != 0)
		{
			// look for common point in this wavefront
			if (searchWavefront(port, layer, x, y) == null)
			{
				return SRROUTED;
			}
		} else if (layer.grids[x][y] == 0)
		{
			// point is not set
			addWavePoint(port, layer, x, y, code);
			return SRSUCCESS;
		}
		return SRBLOCKED;
	}

	private SRWAVEPT searchWavefront(SRPORT port, SRLAYER layer, int x, int y)
	{
		// scans port's wavefront for common point
		for (SRWAVEPT wavept = port.wavefront; wavept != null; wavept = wavept.next)
		{
			if (wavept.layer == layer && wavept.x == x && wavept.y == y) return wavept;
		}
		return null;
	}

	private void clearMaze(SRNET net)
	{
		// clear each region, and reset bounds
		for (int index = 0; index < SRMAXLAYERS; index++)
		{
			SRLAYER layer = net.region.layers[index];
			if (layer != null)
			{
				int mask = ~(SR_GMASK | SR_GWAVE);
				for (int x = layer.lx; x <= layer.hx; x++)
				{
					for (int y = layer.ly; y <= layer.hy; y++)
					{
						layer.grids[x][y] = (byte)(layer.grids[x][y] & mask);
					}
				}
				layer.lx = layer.wid; layer.ly = layer.hei;
				layer.hx = -1; layer.hy = -1;
			}
		}
		for (SRPORT port = net.ports; port != null; port = port.next)
		{
			port.wavefront = null;
		}
		return;
	}

	/************************************* CODE TO CREATE THE MAZE BUFFER *************************************/

	private SRREGION defineRegion(Cell cell, Network net, int lX, int lY, int hX, int hY, Set arcsToDelete, Set nodesToDelete)
	{
		// determine routing region bounds
		lX = lX - mazeBoundary;
		hX = hX + mazeBoundary;
		lY = lY - mazeBoundary;
		hY = hY + mazeBoundary;

		// allocate region and layers
		SRREGION region = getRegion(lX, lY, hX, hY);
		if (region == null)
		{
			System.out.println("Could not allocate routing region (" + lX + "<=X<=" + hX + " " + lY + "<=Y<=" + hY + ")");
			return null;
		}

		Rectangle2D searchBounds = new Rectangle2D.Double(lX, lY, hX-lX, hY-lY);
		Visitor wcVisitor = new Visitor(searchBounds, region, net);
		HierarchyEnumerator.enumerateCell(cell, VarContext.globalContext, wcVisitor);
		return region;
	}

	private class Visitor extends HierarchyEnumerator.Visitor
    {
		private Rectangle2D searchBounds;
		private SRREGION region;
		private int notThisNetID;

		public Visitor(Rectangle2D searchBounds, SRREGION region, Network net)
        {
			this.searchBounds = searchBounds;
			this.region = region;
			notThisNetID = net.getNetIndex();
        }

        public boolean enterCell(HierarchyEnumerator.CellInfo info) { return true; }

        public void exitCell(HierarchyEnumerator.CellInfo info)
        {
			Cell cell = info.getCell();
			Netlist nl = info.getNetlist();
			FixpTransform trans = info.getTransformToRoot();
			for(Iterator<ArcInst> it = cell.getArcs(); it.hasNext(); )
			{
				ArcInst ai = it.next();
				Network net = nl.getNetwork(ai, 0);
				int netID = info.getNetID(net);
				if (netID == notThisNetID) continue;
				Rectangle2D arcBounds = ai.getBounds();
				Rectangle2D bounds = new Rectangle2D.Double(arcBounds.getMinX(), arcBounds.getMinY(),
					arcBounds.getWidth(), arcBounds.getHeight());
				DBMath.transformRect(bounds, trans);
				if (bounds.intersects(searchBounds))
					drawArcInst(ai, trans, region);
			}
        }

        public boolean visitNodeInst(Nodable no, HierarchyEnumerator.CellInfo info)
        {
			FixpTransform trans = info.getTransformToRoot();
			Netlist nl = info.getNetlist();
			NodeInst ni = no.getNodeInst();
			Rectangle2D nodeBounds = ni.getBounds();
			Rectangle2D bounds = new Rectangle2D.Double(nodeBounds.getMinX(), nodeBounds.getMinY(),
				nodeBounds.getWidth(), nodeBounds.getHeight());
			DBMath.transformRect(bounds, trans);
			if (!bounds.intersects(searchBounds)) return false;

			if (!ni.isCellInstance())
			{
				PrimitiveNode pNp = (PrimitiveNode)ni.getProto();
				Technology tech = pNp.getTechnology();
				Poly [] nodeInstPolyList = tech.getShapeOfNode(ni, true, true, null);
				for(int i=0; i<nodeInstPolyList.length; i++)
				{
					Poly poly = nodeInstPolyList[i];
					PortProto pp = poly.getPort();
					Network net = nl.getNetwork(no, pp, 0);
					if (net != null)
					{
						int netID = info.getNetID(net);
						if (netID == notThisNetID) continue;
					}

					poly.transform(trans);
					drawPoly(poly, region, ALLLAYERS);
				}
			}
            return true;
        }
    }

	/**
	 * Method to draw an arcinst.  Returns indicator of what else needs to
	 * be drawn.  Returns negative if display interrupted
	 */
	private void drawArcInst(ArcInst ai, FixpTransform trans, SRREGION region)
	{
		// get the polygons of the arcinst, force line for path generation?
		if (ai.getProto() == Generic.tech().unrouted_arc) return;
		Technology tech = ai.getProto().getTechnology();
		Poly [] polys = tech.getShapeOfArc(ai);
		int total = polys.length;
		for (int j = 0; j < total; j++)
		{
			// generate a polygon
			Poly poly = polys[j];

			// transform the polygon
			poly.transform(trans);

			// draw the polygon
			Point2D [] points = poly.getPoints();
			if (points[0].getX() == points[1].getX())
			{
				drawPoly(poly, region, VERTLAYER);
			} else
			{
				if (points[0].getY() == points[1].getY())
					drawPoly(poly, region, HORILAYER); else
						drawPoly(poly, region, ALLLAYERS);
			}
		}
	}

	private SRPORT addPort(SRNET net, int layers, double cX, double cY, PortInst pi)
	{
		SRPORT port = new SRPORT();
		port.cX = cX;
		port.cY = cY;
		port.lx = (int)cX;   port.hx = (int)cX;
		port.ly = (int)cY;   port.hy = (int)cY;
		port.layers = layers;
		port.wavefront = null;

		for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
		{
			if (layers != 0 & mask != 0 && net.region.layers[index] != null)
			{
				setBox(net.region.layers[index], SR_GPORT | SR_GSET,
					port.lx, port.ly, port.hx, port.hy, true);
			}
		}

		// link into net
		port.next = null;
		port.master = null;
		port.paths = null;
		port.lastpath = null;
		port.net = net;
		port.pi = pi;
		SRPORT lPort = net.ports;
		int index = 0;
		if (lPort == null)
		{
			net.ports = port;
		} else
		{
			index = 1;
			while (lPort.next != null) { index++;   lPort = lPort.next; }
			lPort.next = port;
		}
		port.index = index;
		return port;
	}

	private int determineDir(NodeInst ni, double cX, double cY)
	{
		if (ni == null) return 3;

		// get the center of the NODEINST
		double nCX = ni.getTrueCenterX();
		double nCY = ni.getTrueCenterY();

		// center, all edges
		if (nCX == cX && nCY == cY) return 3; // all layers
		double dX = ni.getBounds().getMaxX() - nCX;
		double dY = ni.getBounds().getMaxY() - nCY;
		double pDX = Math.abs(cX - nCX);
		double pDY = Math.abs(cY - nCY);

		/* consider a point on a triangle, if left/right the seq center, port,
		 * upper left/right edge will be counter-clockwise, if top/bottom the seq.
		 * will be clock-wise :
		 * x1 * y2 + x2 * y3 + x3 * y1 - y1 * x2 - y2 * x3 - y3 * x1 == abs(area)
		 * where area < 0 == clockwise, area > 0 == counter-clockwise
		 */
		double area = pDX * dY - pDY * dX;

		if (area > 0.0) return 1;		// horizontal
		if (area < 0.0) return 2;		// vertical
		return 3;						// corner, all layers
	}

	/**
	 * routing definition functions
	 */
	private SRNET addNet(SRREGION region, Network eNet)
	{
		SRNET srNet = new SRNET();

		srNet.routed = false;
		srNet.eNet = eNet;
		srNet.ports = null;
		srNet.paths = null;
		srNet.lastpath = null;
		srNet.region = region;

		// link into region list
		srNet.next = region.nets;
		region.nets = srNet;

		return srNet;
	}

	/**
	 * Method to write polys into the maze buffer.
	 */
	private void drawPoly(Poly obj, SRREGION region, int layer)
	{
		// now draw the polygon
		if (obj.getLayer() != mazeVertLayer && obj.getLayer() != mazeHorizLayer) return;
		Point2D [] points = obj.getPoints();
		if (obj.getStyle() == Poly.Type.CIRCLE || obj.getStyle() == Poly.Type.THICKCIRCLE || obj.getStyle() == Poly.Type.DISC)
		{
			double radius = points[0].distance(points[1]);
			Rectangle2D circleBounds = new Rectangle2D.Double(points[0].getX()-radius, points[0].getY()-radius, radius*2, radius*2);
			drawBox(circleBounds, layer, region);
			return;
		}
		if (obj.getStyle() == Poly.Type.CIRCLEARC || obj.getStyle() == Poly.Type.THICKCIRCLEARC)
		{
			// arcs at [i] points [1+i] [2+i] clockwise
			if (points.length == 0) return;
			if ((points.length % 3) != 0) return;
			for (int i = 0; i < points.length; i += 3)
			{
				Point2D si = GenMath.computeArcCenter(points[i], points[i+1], points[i+2]);
				drawLine(points[i+1], si, layer, region);
				drawLine(si, points[i+2], layer, region);
			}
			return;
		}

		if (obj.getStyle() == Poly.Type.FILLED || obj.getStyle() == Poly.Type.CLOSED)
		{
			Rectangle2D objBounds = obj.getBox();
			if (objBounds != null)
			{
				drawBox(objBounds, layer, region);
				return;
			}
			for (int i = 1; i < points.length; i++)
				drawLine(points[i-1], points[i], layer, region);

			// close the region
			if (points.length > 2)
				drawLine(points[points.length-1], points[0], layer, region);
			return;
		}

		if (obj.getStyle() == Poly.Type.OPENED || obj.getStyle() == Poly.Type.OPENEDT1 ||
			obj.getStyle() == Poly.Type.OPENEDT1 || obj.getStyle() == Poly.Type.OPENEDT3)
		{
			Rectangle2D objBounds = obj.getBox();
			if (objBounds != null)
			{
				drawBox(objBounds, layer, region);
				return;
			}

			for (int i = 1; i < points.length; i++)
				drawLine(points[i-1], points[i], layer, region);
			return;
		}

		if (obj.getStyle() == Poly.Type.VECTORS)
		{
			for (int i = 1; i < points.length; i += 2)
				drawLine(points[i-1], points[i], layer, region);
			return;
		}
	}

	private void drawBox(Rectangle2D box, int layer, SRREGION region)
	{
		int lX = (int)Math.floor(box.getMinX()-mazeBloat);
		int hX = (int)Math.ceil(box.getMaxX()+mazeBloat);
		int lY = (int)Math.floor(box.getMinY()-mazeBloat);
		int hY = (int)Math.ceil(box.getMaxY()+mazeBloat);
		if (layer == HORILAYER || layer == ALLLAYERS)
			setBox(region.layers[HORILAYER], SR_GSET, lX, lY, hX, hY, false);
		if (layer == VERTLAYER || layer == ALLLAYERS)
			setBox(region.layers[VERTLAYER], SR_GSET, lX, lY, hX, hY, false);
	}

	private void drawLine(Point2D from, Point2D to, int layer, SRREGION region)
	{
		double wX1 = from.getX();
		double wY1 = from.getY();
		double wX2 = to.getX();
		double wY2 = to.getY();
		if (layer == HORILAYER || layer == ALLLAYERS)
			setLine(region.layers[HORILAYER], SR_GSET, wX1, wY1, wX2, wY2, false);
		if (layer == VERTLAYER || layer == ALLLAYERS)
			setLine(region.layers[VERTLAYER], SR_GSET, wX1, wY1, wX2, wY2, false);
	}

	private void setLine(SRLAYER layer, int type, double wX1, double wY1, double wX2, double wY2, boolean orMode)
	{
		// convert to grid coordinates
		int [] x = new int[2];
		int [] y = new int[2];
		x[0] = getGridX((int)wX1, layer);   x[1] = getGridX((int)wX2, layer);
		y[0] = getGridY((int)wY1, layer);   y[1] = getGridY((int)wY2, layer);
		int lx = 1, hx = 0;
		if (wX1 < wX2) { lx = 0; hx = 1; }
		int ly = 1, hy = 0;
		if (wY1 < wY2) { ly = 0; hy = 1; }

		// do obvious clip if completely outside of an edge
		if (x[hx] < 0 || x[lx] >= layer.wid || y[hy] < 0 || y[ly] >= layer.hei) return;

		// clip x
		if (x[lx] < 0)
		{
			y[lx] -= (y[hx] - y[lx]) * x[lx] / (x[hx] - x[lx]);
			x[lx] = 0;
		}
		if (x[hx] >= layer.wid)
		{
			y[hx] -= (y[hx] - y[lx]) * (x[hx] - (layer.wid-1)) / (x[hx] - x[lx]);
			x[hx] = layer.wid - 1;
		}

		// now clip y
		if (y[ly] < 0)
		{
			x[ly] -= (x[hy] - x[ly]) * y[ly] / (y[hy] - y[ly]);
			y[ly] = 0;
		}
		if (y[hy] >= layer.hei)
		{
			x[hy] -= (x[hy] - x[ly]) * (y[hy] - (layer.hei - 1)) / (y[hy] - y[ly]);
			y[hy] = layer.hei - 1;
		}

		// use Bresenham's algorithm to set intersecting grid points
		int dX = x[hx] - x[lx];
		int dY = y[hy] - y[ly];
		if (dY < dX)
		{
			// for 0 <= dY <= dX
			int e = (dY<<1) - dX;
			int yi = y[lx];
			int diff = 1;
			if (y[hx] < y[lx]) diff = -1;
			for (int xi = x[lx]; xi <= x[hx]; xi++)
			{
				setPoint(layer, type, xi, yi, orMode);
				if (e > 0)
				{
					yi += diff;
					e = e + (dY<<1) - (dX<<1);
				} else e = e + (dY<<1);
			}
		} else
		{
			// for 0 <= dX < dY
			int e = (dX<<1) - dY;
			int xi = x[ly];
			int diff = 1;
			if (x[hy] < x[ly]) diff = -1;
			for (int yi = y[ly]; yi <= y[hy]; yi++)
			{
				setPoint(layer, type, xi, yi, orMode);
				if (e > 0)
				{
					xi += diff;
					e = e + (dX<<1) - (dY<<1);
				} else e = e + (dX<<1);
			}
		}
	}

	private void setBox(SRLAYER layer, int type, int wX1, int wY1, int wX2, int wY2, boolean orMode)
	{
		int lX = getGridX(wX1, layer);   int lY = getGridY(wY1, layer);
		int hX = getGridX(wX2, layer);   int hY = getGridY(wY2, layer);
		if (lX > hX) { int x = lX; lX = hX; hX = x; }
		if (lY > hY) { int y = lY; lY = hY; hY = y; }

		if (hX < 0 || lX >= layer.wid || hY < 0 || lY >= layer.hei) return;

		// clip (simple orthogonal)
		if (lX < 0) lX = 0;
		if (hX >= layer.wid) hX = layer.wid - 1;
		if (lY < 0) lY = 0;
		if (hY >= layer.hei) hY = layer.hei - 1;

		// now fill the box
		for (int x = lX; x <= hX; x++)
			for (int y = lY; y <= hY; y++)
				setPoint(layer, type, x, y, orMode);
	}

	/**
	 * drawing function
	 */
	private void setPoint(SRLAYER layer, int type, int x, int y, boolean orMode)
	{
		if (orMode)
		{
			layer.grids[x][y] |= type;
			layer.vused[x] |= type;
			layer.hused[y] |= type;
		} else
		{
			layer.grids[x][y] = (byte)type;
			layer.vused[x] = (byte)type;
			layer.hused[y] = (byte)type;
		}
	}

	/** general control commands */
	private SRREGION getRegion(int wLX, int wLY, int wHX, int wHY)
	{
		if (wLX > wHX || wLY > wHY) return null;

		if (theRegion == null)
		{
			theRegion = new SRREGION();
			for (int index = 0; index < SRMAXLAYERS; index++)
				theRegion.layers[index] = null;
			theRegion.nets = null;
		} else
		{
			cleanoutRegion(theRegion);
		}

		// now set bounds
		theRegion.lx = wLX;
		theRegion.hx = wHX;
		theRegion.ly = wLY;
		theRegion.hy = wHY;

		SRLAYER hlayer = addLayer(theRegion, HORILAYER, SRHORIPREF);
		if (hlayer == null)
		{
			System.out.println("Could not allocate horizontal layer");
			return null;
		}
		SRLAYER vlayer = addLayer(theRegion, VERTLAYER, SRVERTPREF);
		if (vlayer == null)
		{
			System.out.println("Could not allocate vertical layer");
			return null;
		}

		return theRegion;
	}

	private void cleanoutRegion(SRREGION region)
	{
		region.nets = null;
	}

	private SRLAYER addLayer(SRREGION region, int index, SRDIRECTION direction)
	{
		// check for common index
		SRLAYER layer = region.layers[index];
		if (layer == null)
		{
			// allocate and initialize the layer
			layer = new SRLAYER();
			region.layers[index] = layer;
			layer.grids = null;
			layer.vused = null;
			layer.hused = null;
		}

		layer.index = index;
		layer.mask = 1<<index;
		layer.dir = direction;

		// determine the actual bounds of the world
		// round low bounds up, high bounds down
		int lX = region.lx - 1;
		int lY = region.ly - 1;
		int hX = region.hx + 1;
		int hY = region.hy + 1;

		// translate the region lx, ly into grid units
		layer.wid = (hX - lX) + 1;
		layer.hei = (hY - lY) + 1;
		layer.transx = lX;
		layer.transy = lY;

		// sensibility check
		if (layer.wid > MAXGRIDSIZE || layer.hei > MAXGRIDSIZE)
		{
			System.out.println("This route is too large to solve (limit is " + MAXGRIDSIZE + "x" + MAXGRIDSIZE +
				" grid, this is " + layer.wid + "x" + layer.hei + ")");
			return null;
		}

		// check that the hx, hy of grid is in bounds of region
		if (getWorldX(layer.wid - 1, layer) > region.hx) layer.wid--;
		if (getWorldY(layer.hei - 1, layer) > region.hy) layer.hei--;

		// now allocate a grid array
		layer.grids = new byte[layer.wid][];
		layer.vused = new byte[layer.wid];
		for (int x = 0; x < layer.wid; x++)
		{
			// get address for a column of grid points
			layer.grids[x] = new byte[layer.hei];

			// clear all points
			for (int y = 0; y < layer.hei; y++)
				layer.grids[x][y] = 0;

			// clear the V-used flags
			layer.vused[x] = 0;
		}

		// clear the H-used flags
		layer.hused = new byte[layer.hei];
		for (int y = 0; y < layer.hei; y++) layer.hused[y] = 0;

		// set up/down pointers
		layer.up = layer.down = null;
		if (index != 0)
		{
			SRLAYER alayer = region.layers[index-1];
			if (alayer != null)
			{
				layer.down = alayer;
				alayer.up = layer;
			}
		}
		if (index < SRMAXLAYERS - 1)
		{
			SRLAYER alayer = region.layers[index+1];
			if (alayer != null)
			{
				layer.up = alayer;
				alayer.down = layer;
			}
		}

		return layer;
	}

	private int getGridX(int Mv, SRLAYER Ml) { return Mv - Ml.transx; }

	private int getGridY(int Mv, SRLAYER Ml) { return Mv - Ml.transy; }

	private int getWorldX(int Mv, SRLAYER Ml) { return Mv + Ml.transx; }

	private int getWorldY(int Mv, SRLAYER Ml) { return Mv + Ml.transy; }

	/************************************* CODE TO CREATE RESULTS IN THE CIRCUIT *************************************/

	/**
	 * routing grid database methods
	 */
	private boolean extractPaths(Cell parent, SRNET net)
	{
		// adjust paths to account for precise port location
		double fX = 0, fY = 0;
		for (SRPATH path = net.paths; path != null; path = path.next)
		{
			if (path.type == SRPFIXED) continue;
			SRPORT port = path.port;
			fX = path.wx[0];   fY = path.wy[0];
			double oFX = fX, oFY = fY;
			if (path.end[0] && port.pi != null)
			{
//				fX = port.cX;   fY = port.cY;
//				if (fX != oFX || fY != oFY)
//				{
//					adjustPath(net.paths, path, 0, fX-oFX, fY-oFY);
//				}
				Poly portPoly = port.pi.getPoly();
				Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
				if (closest.getX() != oFX || closest.getY() != oFY)
					adjustPath(net.paths, path, 0, closest.getX()-oFX, closest.getY()-oFY);
			} else
			{
				ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
				List<PortInst> portInstList = findPort(parent, fX, fY, ap, net, true);
				if (portInstList.size() > 0)
				{
					PortInst pi = portInstList.get(0);
					Poly portPoly = pi.getPoly();
					Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
					if (closest.getX() != oFX || closest.getY() != oFY)
						adjustPath(net.paths, path, 0, closest.getX()-oFX, closest.getY()-oFY);
				}
			}

			fX = oFX = path.wx[1];   fY = oFY = path.wy[1];
			if (path.end[1] && port.pi != null)
			{
//				fX = port.cX;   fY = port.cY;
//				if (fX != oFX || fY != oFY)
//				{
//					adjustPath(net.paths, path, 1, fX-oFX, fY-oFY);
//				}
				Poly portPoly = port.pi.getPoly();
				Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
				if (closest.getX() != oFX || closest.getY() != oFY)
					adjustPath(net.paths, path, 1, closest.getX()-oFX, closest.getY()-oFY);
			} else
			{
				ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
				List<PortInst> portInstList = findPort(parent, fX, fY, ap, net, true);
				if (portInstList.size() > 0)
				{
					PortInst pi = portInstList.get(0);
					Poly portPoly = pi.getPoly();
					Point2D closest = portPoly.closestPoint(new Point2D.Double(fX, fY));
					if (closest.getX() != oFX || closest.getY() != oFY)
						adjustPath(net.paths, path, 1, closest.getX()-oFX, closest.getY()-oFY);
				}
			}
		}

		// now do the routing
		for (SRPATH path = net.paths; path != null; path = path.next)
		{
			if (path.type == SRPFIXED) continue;
			ArcProto ap = path.layer.index != 0 ? mazeVertWire : mazeHorizWire;
			SRPORT port = path.port;

			// create arc between the end points
			List<PortInst> fromPortInstList = null;
			fX = path.wx[0];   fY = path.wy[0];
			if (path.end[0] && port.pi != null)
			{
				fromPortInstList = new ArrayList<PortInst>();
				fromPortInstList.add(port.pi);
			} else
			{
				fromPortInstList = findPort(parent, fX, fY, ap, net, false);
				if (fromPortInstList.size() == 0)
				{
					// create the from pin
					double xS = mazeSteinerNode.getDefWidth(ep);
					double yS = mazeSteinerNode.getDefHeight(ep);
					NodeInst ni = NodeInst.makeInstance(mazeSteinerNode, ep, new Point2D.Double(fX, fY), xS, yS, parent);
					if (ni == null)
					{
						System.out.println("Could not create pin");
						return true;
					}
					fromPortInstList.add(ni.getPortInst(0));
				}
			}

			List<PortInst> toPortInstList = null;
			double tX = path.wx[1];   double tY = path.wy[1];
			if (path.end[1] && port.pi != null)
			{
				toPortInstList = new ArrayList<PortInst>();
				toPortInstList.add(port.pi);
			} else
			{
				toPortInstList = findPort(parent, tX, tY, ap, net, false);
				if (toPortInstList.size() == 0)
				{
					// create the from pin
					double xS = mazeSteinerNode.getDefWidth(ep);
					double yS = mazeSteinerNode.getDefHeight(ep);
					NodeInst ni = NodeInst.makeInstance(mazeSteinerNode, ep, new Point2D.Double(tX, tY), xS, yS, parent);
					if (ni == null)
					{
						System.out.println("Could not create pin");
						return true;
					}
					toPortInstList.add(ni.getPortInst(0));
				}
			}

			// now connect (note only nodes for now, no bus like connections)
			if (fromPortInstList.size() > 0 && toPortInstList.size() > 0)
			{
				// now make the connection (simple wire to wire for now)
				PortInst fPi = fromPortInstList.get(0);
				PortInst tPi = toPortInstList.get(0);
				ArcInst ai = ArcInst.makeInstance(ap, ep, fPi, tPi, new Point2D.Double(fX, fY), new Point2D.Double(tX, tY), null);
//				ArcInst ai = ArcInst.makeInstanceFull(ap, ap.getDefaultLambdaFullWidth(), fPi, tPi, new Point2D.Double(fX, fY), new Point2D.Double(tX, tY), null);
				if (ai == null)
				{
					System.out.println("Could not create path (arc)");
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Method to recursively adjust paths to account for port positions that may not
	 * be on the grid.
	 */
	private void adjustPath(SRPATH paths, SRPATH thisPath, int end, double dX, double dY)
	{
		if (dX != 0)
		{
			double formerThis = thisPath.wx[end];
			double formerOther = thisPath.wx[1-end];
			thisPath.wx[end] += dX;
			if (formerThis == formerOther)
			{
				double formerX = thisPath.wx[1-end];
				double formerY = thisPath.wy[1-end];
				thisPath.wx[1-end] += dX;
				for (SRPATH opath = paths; opath != null; opath = opath.next)
				{
					if (opath.wx[0] == formerX && opath.wy[0] == formerY)
					{
						adjustPath(paths, opath, 0, dX, 0);
						break;
					}
					if (opath.wx[1] == formerX && opath.wy[1] == formerY)
					{
						adjustPath(paths, opath, 1, dX, 0);
						break;
					}
				}
			}
		}

		if (dY != 0)
		{
			double formerThis = thisPath.wy[end];
			double formerOther = thisPath.wy[1-end];
			thisPath.wy[end] += dY;
			if (formerThis == formerOther)
			{
				double formerX = thisPath.wx[1-end];
				double formerY = thisPath.wy[1-end];
				thisPath.wy[1-end] += dY;
				for (SRPATH opath = paths; opath != null; opath = opath.next)
				{
					if (opath.wx[0] == formerX && opath.wy[0] == formerY)
					{
						adjustPath(paths, opath, 0, 0, dY);
						break;
					}
					if (opath.wx[1] == formerX && opath.wy[1] == formerY)
					{
						adjustPath(paths, opath, 1, 0, dY);
						break;
					}
				}
			}
		}
	}

	/**
	 * Method to locate the PortInsts corresponding to
	 * a direct intersection with the given point.
	 * @param cell the cell to search
	 * @param x X coordinate of the point to examine.
	 * @param y Y coordinate of the point to examine.
	 * @param ap the arc used to connect port (must match pp)
	 */
	private List<PortInst> findPort(Cell cell, double x, double y, ArcProto ap, SRNET srnet, boolean forceFind)
	{
		List<PortInst> portInstList = new ArrayList<PortInst>();
		Point2D searchPoint = new Point2D.Double(x, y);
		double bestDist = 0;
		PortInst closestPi = null;
		Rectangle2D searchBounds = new Rectangle2D.Double(x-0.5, y-0.5, 1, 1);
		for(Iterator<Geometric> sea = cell.searchIterator(searchBounds); sea.hasNext(); )
		{
			Geometric geom = sea.next();

			if (geom instanceof NodeInst)
			{
				// now locate a portproto
				NodeInst ni = (NodeInst)geom;
				for(Iterator<PortInst> it = ni.getPortInsts(); it.hasNext(); )
				{
					PortInst pi = it.next();
					Poly portPoly = pi.getPoly();
					if (portPoly.isInside(searchPoint))
					{
						// check if port connects to arc ...*/
						if (pi.getPortProto().getBasePort().connectsTo(ap))
						{
							portInstList.add(pi);
						}
					} else
					{
						double dist = portPoly.polyDistance(new Rectangle2D.Double(x, y, 0, 0));

						// LINTED "bestDist" used in proper order
						if (closestPi == null || dist < bestDist)
						{
							bestDist = dist;
							closestPi = pi;
						}
					}
				}
			}
		}

		if (portInstList.size() == 0 && forceFind && closestPi != null && bestDist < 1)
		{
			portInstList.add(closestPi);
		}
		return portInstList;
	}

//	/**
//	 * Debugging code to show the maze.
//	 */
//	private void dumpLayer(String message, SRREGION region, int layers)
//	{
//		// scan for the first layer
//		SRLAYER layer = null;
//		for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
//		{
//			if ((mask & layers) == 0) continue;
//			layer = region.layers[index];
//			if (layer != null) break;
//		}
//		int hei = layer.hei;
//		int wid = layer.wid;
//		System.out.println("====================== " + message + " ======================");
//		if (wid >= 100)
//		{
//			System.out.print("   ");
//			for (int x = 0; x < wid; x++) System.out.print((x / 100));
//			System.out.println();
//		}
//		System.out.print("   ");
//		for (int x = 0; x < wid; x++) System.out.print((x / 10 % 10));
//		System.out.println();
//		System.out.print("   ");
//		for (int x = 0; x < wid; x++) System.out.print((x % 10));
//		System.out.println();
//		for (int y = hei-1; y >= 0; y--)
//		{
//			String row = "" + y;
//			while (row.length() < 3) row = " " + row;
//			System.out.print(row);
//			for (int x = 0; x < wid; x++)
//			{
//				char gpt = ' ';
//				for (int index = 0, mask = 1; index < SRMAXLAYERS; index++, mask = mask<<1)
//				{
//					if ((mask & layers) == 0) continue;
//					layer = region.layers[index];
//					if (layer == null) continue;
//					if ((layer.grids[x][y] & SR_GSET) != 0)
//					{
//						if ((layer.grids[x][y] & SR_GPORT) != 0) gpt = 'P'; else
//							gpt = '.';
//					} else
//					{
//						if ((layer.grids[x][y] & SR_GWAVE) != 0) gpt = 'W'; else
//							if (layer.grids[x][y] != 0)
//								gpt = (char)('A' + (layer.grids[x][y] & SR_GMASK) - SR_GSTART);
//					}
//				}
//				System.out.print(gpt);
//			}
//			System.out.println("");
//		}
//		System.out.println("");
//	}

}
