gt: 							if (yGrab == yE) yArea *= (yEndPos-yE);
context: /* -*- tab-width: 4 -*-
   *
   * Electric(tm) VLSI Design System
   *
   * File: VectorDrawing.java
   *
   * Copyright (c) 2005 Sun Microsystems and 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 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 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.
   */
  package com.sun.electric.tool.user.redisplay;
  
  import com.sun.electric.Main;
  import com.sun.electric.database.geometry.DBMath;
  import com.sun.electric.database.geometry.EGraphics;
  import com.sun.electric.database.geometry.GenMath;
  import com.sun.electric.database.geometry.Orientation;
  import com.sun.electric.database.geometry.Poly;
  import com.sun.electric.database.geometry.GenMath.MutableDouble;
  import com.sun.electric.database.hierarchy.Cell;
  import com.sun.electric.database.hierarchy.Export;
  import com.sun.electric.database.id.CellId;
  import com.sun.electric.database.text.TextUtils;
  import com.sun.electric.database.topology.NodeInst;
  import com.sun.electric.database.variable.TextDescriptor;
  import com.sun.electric.database.variable.VarContext;
  import com.sun.electric.technology.Layer;
  import com.sun.electric.technology.Technology;
  import com.sun.electric.tool.Job;
  import com.sun.electric.tool.user.User;
  
  import java.awt.Color;
  import java.awt.Dimension;
  import java.awt.Point;
  import java.awt.Rectangle;
  import java.awt.geom.AffineTransform;
  import java.awt.geom.Point2D;
  import java.awt.geom.Rectangle2D;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  /**
   * Class to do rapid redraw by caching the vector coordinates of all objects.
   */
  class VectorDrawing
  {
  	private static final boolean TAKE_STATS = false;
  	private static final boolean DEBUGIMAGES = false;
  	private static final int MAXGREEKSIZE = 25;
  	private static final int SCALE_SH = 20;
  
  	/** the rendering object */								private PixelDrawing offscreen;
  	/** the window scale */									private float scale;
  	/** the window scale */									private float scale_;
  	/** the window scale and pan factor */					private float factorX, factorY;
  	private int factorX_, factorY_;
  	private int scale_int;
  	/** true if "peeking" and expanding to the bottom */	private boolean fullInstantiate;
  	/** A List of NodeInsts to the cell being in-place edited. */private List<NodeInst> inPlaceNodePath;
  	/** The current cell being in-place edited. */			private Cell inPlaceCurrent;
  	/** time that rendering started */						private long startTime;
  	/** true if the user has been told of delays */			private boolean takingLongTime;
  	/** true to stop rendering */							private boolean stopRendering;
  	/** the half-sizes of the window (in pixels) */			private int szHalfWidth, szHalfHeight;
  	/** the screen clipping */								private int screenLX, screenHX, screenLY, screenHY;
  	/** statistics */										private int boxCount, tinyBoxCount, lineBoxCount, lineCount, polygonCount;
  	/** statistics */										private int crossCount, textCount, circleCount, arcCount;
  	/** statistics */										private int subCellCount, tinySubCellCount;
  	/** the threshold of object sizes */					private float maxObjectSize;
      /** true to use cell greeking images */                 private boolean useCellGreekingImages;
  	/** the threshold of text sizes */						private float maxTextSize;
  	/** the maximum cell size above which no greeking */	private float maxCellSize;
  
  	/** temporary objects (saves allocation) */				private Point tempPt1 = new Point(), tempPt2 = new Point();
  	/** temporary objects (saves allocation) */				private Point tempPt3 = new Point();
  	/** temporary object (saves allocation) */				private Rectangle tempRect = new Rectangle();
  
  	/** the object that draws the rendered screen */		private static VectorDrawing topVD;
  	/** location for debugging icon displays */				private static int debugXP, debugYP;
  
  	// ************************************* TOP LEVEL *************************************
  
  	/**
  	 * Constructor creates a VectorDrawing object for a given EditWindow.
  	 * @param wnd the EditWindow associated with this VectorDrawing.
  	 */
  	public VectorDrawing(boolean useCellGreekingImages)
  	{
          this.useCellGreekingImages = useCellGreekingImages;
  	}
  
  	/**
  	 * Main entry point for drawing a cell.
  	 * @param offscreen offscreen buffer
  	 * @param scale edit window scale
  	 * @param offset the offset factor for this window
  	 * @param cell the cell to draw
  	 * @param fullInstantiate true to draw all the way to the bottom of the hierarchy.
  	 * @param inPlaceNodePath a List of NodeInsts to the cell being in-place edited
  	 * @param screenLimit the area in the cell to display (null to show all).
  	 */
  	public void render(PixelDrawing offscreen, double scale, Point2D offset, Cell cell, boolean fullInstantiate,
  		List<NodeInst> inPlaceNodePath, Cell inPlaceCurrent, Rectangle screenLimit,
  		VarContext context, double greekSizeLimit, double greekCellSizeLimit)
  	{
  		// see if any layers are being highlighted/dimmed
  		this.offscreen = offscreen;
  		offscreen.highlightingLayers = false;
  		for(Iterator<Layer> it = Technology.getCurrent().getLayers(); it.hasNext(); )
  		{
  			Layer layer = it.next();
  			if (PixelDrawing.lv.isHighlighted(layer))
  			{
  				offscreen.highlightingLayers = true;
  				break;
  			}
  		}
  
  		// set size limit
  		Dimension sz = offscreen.getSize();
  		this.scale = (float)scale;
  		scale_ = (float)(scale/DBMath.GRID);
  		maxObjectSize = (float)greekSizeLimit / this.scale;
  		maxTextSize = (float)(maxObjectSize / PixelDrawing.dp.globalTextScale);
  		double screenArea = sz.getWidth()/scale * sz.getHeight()/scale;
  		maxCellSize = (float)(greekCellSizeLimit * screenArea);
  
  		// statistics
  		startTime = System.currentTimeMillis();
  		long initialUsed = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  		takingLongTime = false;
  		boxCount = tinyBoxCount = lineBoxCount = lineCount = polygonCount = 0;
  		crossCount = textCount = circleCount = arcCount = 0;
  		subCellCount = tinySubCellCount = 0;
  
  		// draw recursively
  		this.fullInstantiate = fullInstantiate;
  		this.inPlaceNodePath = inPlaceNodePath;
  		this.inPlaceCurrent = inPlaceCurrent;
  		szHalfWidth = sz.width / 2;
  		szHalfHeight = sz.height / 2;
  		screenLX = 0;   screenHX = sz.width;
  		screenLY = 0;   screenHY = sz.height;
  		factorX = (float)(offset.getX()*DBMath.GRID - szHalfWidth/scale_);
  		factorY = (float)(offset.getY()*DBMath.GRID + szHalfHeight/scale_);
  		factorX_ = (int)factorX;
  		factorY_ = (int)factorY;
  		scale_int = (int)(scale_ * (1 << SCALE_SH));
  		if (screenLimit != null)
  		{
  			screenLX = screenLimit.x;
  			if (screenLX < 0) screenLX = 0;
  			screenHX = screenLimit.x + screenLimit.width;
  			if (screenHX >= sz.width) screenHX = sz.width-1;
  			screenLY = screenLimit.y;
  			if (screenLY < 0) screenLY = 0;
  			screenHY = screenLimit.y + screenLimit.height;
  			if (screenHY >= sz.height) screenHY = sz.height-1;
  		}
  
  		// draw the screen, starting with the top cell
  		stopRendering = false;
  		try
  		{
  			VectorCache.VectorCell topVC = drawCell(cell, Orientation.IDENT, context);
  			topVD = this;
  			render(topVC, 0, 0, context, 0);
  			drawList(0, 0, topVC.getTopOnlyShapes(), 0, false);
  		} catch (AbortRenderingException e)
  		{
  		}
  		topVD = null;
  
  		if (takingLongTime)
  		{
  			Main.setBusyCursor(false);
  			System.out.println("Done");
  		}
  
  		if (TAKE_STATS && Job.getDebug())
  		{
  			long curUsed = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  			long memUsed = curUsed - initialUsed;
  			long renderTime = System.currentTimeMillis() - startTime;
  			System.out.println("Time to render: "+TextUtils.getElapsedTime(renderTime) + "    Memory Used: "+ memUsed);
  			System.out.println("   Rendered "+boxCount+" boxes ("+tinyBoxCount+" tiny, "+lineBoxCount+" lines), "+
  				lineCount+" lines, "+polygonCount+" polys, "+crossCount+" crosses, "+
  				textCount+" texts, "+circleCount+" circles, "+arcCount+" arcs, "+
  				subCellCount+" subcells ("+tinySubCellCount+" tiny)");
  		}
  	}
  
  	/**
  	 * Main entry point for drawing a tech menu entry.
  	 * @param offscreen offscreen buffer
  	 * @param scale edit window scale
  	 * @param offset the offset factor for this window
  	 * @param shapes shapes of tech menu
  	 */
  	public void render(PixelDrawing offscreen, double scale, Point2D offset, VectorCache.VectorBase[] shapes)
  	{
  		// set colors to use
  		PixelDrawing.textGraphics = PixelDrawing.textGraphics.withColor(PixelDrawing.gp.getColor(User.ColorPrefType.TEXT));
  
  		// see if any layers are being highlighted/dimmed
  		this.offscreen = offscreen;
  
  		// set size limit
  		Dimension sz = offscreen.getSize();
  		this.scale = (float)scale;
  		scale_ = (float)(scale/DBMath.GRID);
  
  		// draw recursively
  		szHalfWidth = sz.width / 2;
  		szHalfHeight = sz.height / 2;
  		screenLX = 0;   screenHX = sz.width;
  		screenLY = 0;   screenHY = sz.height;
  		factorX = (float)(offset.getX()*DBMath.GRID - szHalfWidth/scale_);
  		factorY = (float)(offset.getY()*DBMath.GRID + szHalfHeight/scale_);
  		factorX_ = (int)factorX;
  		factorY_ = (int)factorY;
  		scale_int = (int)(scale_ * (1 << SCALE_SH));
  
  		// draw the screen, starting with the top cell
  		try
  		{
  			List<VectorCache.VectorBase> shapeList = Arrays.asList(shapes);
  			drawList(0, 0, shapeList, 0, true);
  		} catch (AbortRenderingException e)
  		{
  		}
  	}
  
  	/**
  	 * Class to define a signal to abort rendering.
  	 */
  	class AbortRenderingException extends Exception {}
  
  	/**
  	 * Method to request that the current rendering be aborted because it must be restarted.
  	 *
  	 */
  	public void abortRendering()
  	{
  		stopRendering = true;
  	}
  
  	/**
  	 * Method to recursively render a cached cell.
  	 * @param vc the cached cell to render
  	 * @param oX the X offset for rendering the cell (in database grid coordinates).
  	 * @param oY the Y offset for rendering the cell (in database grid coordinates).
  	 * @param context the VarContext for this point in the rendering.
  	 * @param level: 0=top-level cell in window; 1=low level cell; -1=greeked cell.
  	 */
  	private void render(VectorCache.VectorCell vc, int oX, int oY, VarContext context, int level)
  		throws AbortRenderingException
  	{
  		// render main list of shapes
  		drawList(oX, oY, vc.shapes, level, false);
  
  		// now render subcells
  		Cell cell = VectorCache.theCache.database.getCell(vc.vcg.cellId);
  		for(VectorCache.VectorSubCell vsc : vc.subCells)
  		{
  			if (stopRendering) throw new AbortRenderingException();
  //			NodeInst ni = cell.getNodeById(vsc.n.nodeId);
  //			Cell subCell = (Cell)ni.getProto();
              Cell subCell = cell.getDatabase().getCell((CellId)vsc.n.protoId);
  			subCellCount++;
  
  			// get instance location
  			int soX = vsc.offsetX + oX;
  			int soY = vsc.offsetY + oY;
  			VectorCache.VectorCell subVC = VectorCache.theCache.findVectorCell(vsc.subCellId, vc.orient.concatenate(vsc.n.orient));
  			gridToScreen(subVC.lX + soX, subVC.hY + soY, tempPt1);
  			gridToScreen(subVC.hX + soX, subVC.lY + soY, tempPt2);
  			int lX = tempPt1.x;
  			int lY = tempPt1.y;
  			int hX = tempPt2.x;
  			int hY = tempPt2.y;
  
  			// see if the subcell is clipped
  			if (hX < screenLX || lX >= screenHX) continue;
  			if (hY < screenLY || lY >= screenHY) continue;
  
  			// see if the cell is too tiny to draw
  			if (subVC.vcg.cellMinSize < maxObjectSize)
  			{
  				Orientation thisOrient = vsc.n.orient;
  				Orientation recurseTrans = vc.orient.concatenate(thisOrient);
  				VarContext subContext = context.push(cell, vsc.n);
  				VectorCache.VectorCell subVC_ = drawCell(subCell, recurseTrans, subContext);
  				assert subVC_ == subVC;
                  makeGreekedImage(subVC);
  
  				int fadeColor = getFadeColor(subVC, subContext);
  				drawTinyBox(lX, hX, lY, hY, fadeColor, subVC);
  				tinySubCellCount++;
  				continue;
  			}
  
  			// see if drawing "down in place"
  			boolean onPathDown = false;
  			if (inPlaceNodePath != null)
  			{
  				for(NodeInst niOnPath : inPlaceNodePath)
  				{
  					if (niOnPath.getProto().getId() == vsc.subCellId)
  					{
  						onPathDown = true;
  						break;
  					}
  				}
  			}
  
  			// see if cell contents should be drawn
              boolean isExpanded = cell.isExpanded(vsc.n.nodeId);
  			boolean expanded = isExpanded || fullInstantiate;
  
  			// if not expanded, but viewing this cell in-place, expand it
  			if (!expanded && onPathDown) expanded = true;
  
  			if (expanded)
  			{
  				Orientation thisOrient = vsc.n.orient;
  				Orientation recurseTrans = vc.orient.concatenate(thisOrient);
  				VarContext subContext = context.push(cell, vsc.n);
  				VectorCache.VectorCell subVC_ = drawCell(subCell, recurseTrans, subContext);
  				assert subVC_ == subVC;
  
  				// expanded cells may be replaced with greeked versions (not icons)
  				if (!subCell.isIcon())
  				{
  					// may also be "tiny" if all features in the cell are tiny
  					boolean allFeaturesTiny = subVC.maxFeatureSize > 0 && subVC.maxFeatureSize < maxObjectSize &&
  						subVC.vcg.cellArea < maxCellSize && isContentsTiny(subCell, subVC, recurseTrans, context);
  
  					// may also be "tiny" if the cell is smaller than the greeked image
  					boolean smallerThanGreek = useCellGreekingImages && hX-lX <= MAXGREEKSIZE && hY-lY <= MAXGREEKSIZE;
  					if (allFeaturesTiny || smallerThanGreek)
  					{
                          makeGreekedImage(subVC);
  						int fadeColor = getFadeColor(subVC, context);
  						drawTinyBox(lX, hX, lY, hY, fadeColor, subVC);
  						tinySubCellCount++;
  						continue;
  					}
  				}
  
  				int subLevel = level;
  				if (subLevel == 0) subLevel = 1;
  				render(subVC, soX, soY, subContext, subLevel);
  			} else
  			{
  				// now draw with the proper line type
  				int[] op = subVC.outlinePoints;
  				int p1x = op[0] + soX;
  				int p1y = op[1] + soY;
  				int p2x = op[2] + soX;
  				int p2y = op[3] + soY;
  				int p3x = op[4] + soX;
  				int p3y = op[5] + soY;
  				int p4x = op[6] + soX;
  				int p4y = op[7] + soY;
  				gridToScreen(p1x, p1y, tempPt1);   gridToScreen(p2x, p2y, tempPt2);
  				offscreen.drawLine(tempPt1, tempPt2, null, PixelDrawing.instanceGraphics, 0, false);
  				gridToScreen(p2x, p2y, tempPt1);   gridToScreen(p3x, p3y, tempPt2);
  				offscreen.drawLine(tempPt1, tempPt2, null, PixelDrawing.instanceGraphics, 0, false);
  				gridToScreen(p3x, p3y, tempPt1);   gridToScreen(p4x, p4y, tempPt2);
  				offscreen.drawLine(tempPt1, tempPt2, null, PixelDrawing.instanceGraphics, 0, false);
  				gridToScreen(p1x, p1y, tempPt1);   gridToScreen(p4x, p4y, tempPt2);
  				offscreen.drawLine(tempPt1, tempPt2, null, PixelDrawing.instanceGraphics, 0, false);
  
  				// draw the instance name
  				if (PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.INSTANCE))
  				{
  					tempRect.setBounds(lX, lY, hX-lX, hY-lY);
  					TextDescriptor descript = vsc.n.protoDescriptor;
  					offscreen.drawText(tempRect, Poly.Type.TEXTBOX, descript, subCell.describe(false), null, PixelDrawing.textGraphics, false);
  				}
  			}
  			if (level == 0 || onPathDown || inPlaceCurrent == cell)
  				drawPortList(vsc, subVC, soX, soY, expanded, onPathDown);
  		}
  	}
  
  	/**
  	 * Method to draw a list of cached shapes.
  	 * @param oX the X offset to draw the shapes (in database grid coordinates).
  	 * @param oY the Y offset to draw the shapes (in database grid coordinates).
  	 * @param shapes the List of shapes (VectorBase objects).
  	 * @param level: 0=top-level cell in window; 1=low level cell; -1=greeked cell.
  	 * @param forceVisible true to force all layers to be drawn (regardless of user settings)
  	 */
  	private void drawList(int oX, int oY, List<VectorCache.VectorBase> shapes, int level, boolean forceVisible)
  		throws AbortRenderingException
  	{
  		// render all shapes in reverse order (because PixelDrawing don't overwrite opaque layers)
  		for (int k = shapes.size() - 1; k >= 0; k--)
  		{
  			VectorCache.VectorBase vb = shapes.get(k);
  			if (stopRendering) throw new AbortRenderingException();
  
  			// get visual characteristics of shape
  			Layer layer = vb.layer;
  			boolean dimmed = false;
  			if (layer != null)
  			{
  				if (level < 0)
  				{
  					// greeked cells ignore cut and implant layers
  					Layer.Function fun = layer.getFunction();
  					if (fun.isContact() || fun.isWell() || fun.isSubstrate()) continue;
  				}
  				if (!forceVisible) {
                      if (!PixelDrawing.lv.isVisible(layer)) continue;
                      dimmed = !PixelDrawing.lv.isHighlighted(layer);
                  }
              }
  			byte [][] layerBitMap = null;
              EGraphics graphics = vb.graphicsOverride;
              if (graphics == null && layer != null)
                  graphics = PixelDrawing.gp.getGraphics(layer);
  			if (graphics != null)
  			{
  				int layerNum = graphics.getTransparentLayer() - 1;
  				if (layerNum < offscreen.numLayerBitMaps) layerBitMap = offscreen.getLayerBitMap(layerNum);
  			}
  
  			// handle each shape
  			if (vb instanceof VectorCache.VectorManhattan)
  			{
  				boxCount++;
  				VectorCache.VectorManhattan vm = (VectorCache.VectorManhattan)vb;
  
  				double maxSize = maxObjectSize*DBMath.GRID;
  				int fadeCol = -1;
  				if (layer != null)
  				{
  					Layer.Function fun = layer.getFunction();
  					if (fun.isImplant() || fun.isSubstrate())
  					{
  						// well and substrate layers are made smaller so that they "greek" sooner
  						if (!vm.pureLayer)
  							maxSize *= 10;
  					} else if (graphics != null) {
  						fadeCol = graphics.getRGB();
  					}
  				}
  				for (int i = 0; i < vm.coords.length; i += 4) {
  					int c1X = vm.coords[i];
  					int c1Y = vm.coords[i+1];
  					int c2X = vm.coords[i+2];
  					int c2Y = vm.coords[i+3];
  					long dX = c2X - c1X;
  					long dY = c2Y - c1Y;
  					if (dX < maxSize || dY < maxSize)
  					{
  						if (fadeCol < 0) continue;
  						if (dX < maxSize && dY < maxSize)
  						{
  							// both dimensions tiny: just draw a dot
  							gridToScreen(c1X+oX, c1Y+oY, tempPt1);
  							int x = tempPt1.x;
  							int y = tempPt1.y;
  							if (x < screenLX || x >= screenHX) continue;
  							if (y < screenLY || y >= screenHY) continue;
  							offscreen.drawPoint(x, y, null, fadeCol);
  							tinyBoxCount++;
  						} else
  						{
  							// one dimension tiny: draw a line
  							gridToScreen(c1X+oX, c2Y+oY, tempPt1);
  							gridToScreen(c2X+oX, c1Y+oY, tempPt2);
                              assert tempPt1.x <= tempPt2.x && tempPt1.y <= tempPt2.y;
  //                            if (!(tempPt1.x <= tempPt2.x && tempPt1.y <= tempPt2.y))
  //                            {
  //                                System.out.println("Error: too small box?. Check X:" + tempPt1.x + " with " + tempPt2.x);
  //                                System.out.println("Error: too small box?. Check Y:" + tempPt1.y + " with " + tempPt2.y);
  //                            }
                              int lX = tempPt1.x;
  							int hX = tempPt2.x;
  							int lY = tempPt1.y;
  							int hY = tempPt2.y;
  							if (hX < screenLX || lX >= screenHX) continue;
  							if (hY < screenLY || lY >= screenHY) continue;
  							drawTinyBox(lX, hX, lY, hY, fadeCol, null);
  							lineBoxCount++;
  						}
  						continue;
  					}
  
  					// determine coordinates of rectangle on the screen
  					gridToScreen(c1X+oX, c2Y+oY, tempPt1);
  					gridToScreen(c2X+oX, c1Y+oY, tempPt2);
  					assert tempPt1.x <= tempPt2.x && tempPt1.y <= tempPt2.y;
  					int lX = tempPt1.x;
  					int hX = tempPt2.x;
  					int lY = tempPt1.y;
  					int hY = tempPt2.y;
  
  					// reject if completely off the screen
  					if (hX < screenLX || lX >= screenHX) continue;
  					if (hY < screenLY || lY >= screenHY) continue;
  
  					// clip to screen
  					if (lX < screenLX) lX = screenLX;
  					if (hX >= screenHX) hX = screenHX-1;
  					if (lY < screenLY) lY = screenLY;
  					if (hY >= screenHY) hY = screenHY-1;
  
  					// draw the box
  					offscreen.drawBox(lX, hX, lY, hY, layerBitMap, graphics, dimmed);
  				}
  			} else if (vb instanceof VectorCache.VectorLine)
  			{
  				lineCount++;
  				VectorCache.VectorLine vl = (VectorCache.VectorLine)vb;
  
  				// determine coordinates of line on the screen
  				gridToScreen(vl.fX+oX, vl.fY+oY, tempPt1);
  				gridToScreen(vl.tX+oX, vl.tY+oY, tempPt2);
  
  				// clip and draw the line
  				offscreen.drawLine(tempPt1, tempPt2, layerBitMap, graphics, vl.texture, dimmed);
  			} else if (vb instanceof VectorCache.VectorPolygon)
  			{
  				polygonCount++;
  				VectorCache.VectorPolygon vp = (VectorCache.VectorPolygon)vb;
  				Point [] intPoints = new Point[vp.points.length];
  				for(int i=0; i<vp.points.length; i++)
  				{
  					intPoints[i] = new Point();
  					gridToScreen(vp.points[i].x+oX, vp.points[i].y+oY, intPoints[i]);
  				}
  				Point [] clippedPoints = GenMath.clipPoly(intPoints, screenLX, screenHX-1, screenLY, screenHY-1);
  				offscreen.drawPolygon(clippedPoints, layerBitMap, graphics, dimmed);
  			} else if (vb instanceof VectorCache.VectorCross)
  			{
  				crossCount++;
  				VectorCache.VectorCross vcr = (VectorCache.VectorCross)vb;
  				gridToScreen(vcr.x+oX, vcr.y+oY, tempPt1);
  				int size = 5;
  				if (vcr.small) size = 3;
  				offscreen.drawLine(new Point(tempPt1.x-size, tempPt1.y), new Point(tempPt1.x+size, tempPt1.y), null, graphics, 0, dimmed);
  				offscreen.drawLine(new Point(tempPt1.x, tempPt1.y-size), new Point(tempPt1.x, tempPt1.y+size), null, graphics, 0, dimmed);
  			} else if (vb instanceof VectorCache.VectorText)
  			{
  				VectorCache.VectorText vt = (VectorCache.VectorText)vb;
  				switch (vt.textType)
  				{
  					case VectorCache.VectorText.TEXTTYPEARC:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.ARC)) continue;
  						break;
  					case VectorCache.VectorText.TEXTTYPENODE:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.NODE)) continue;
  						break;
  					case VectorCache.VectorText.TEXTTYPECELL:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.CELL)) continue;
  						break;
  					case VectorCache.VectorText.TEXTTYPEEXPORT:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.EXPORT)) continue;
  						break;
  					case VectorCache.VectorText.TEXTTYPEANNOTATION:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.ANNOTATION)) continue;
  						break;
  					case VectorCache.VectorText.TEXTTYPEINSTANCE:
  						if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.INSTANCE)) continue;
  						break;
  				}
  				if (vt.height < maxTextSize) continue;
  
  				String drawString = vt.str;
  				int lX = vt.bounds.x;
  				int lY = vt.bounds.y;
  				int hX = lX + vt.bounds.width;
  				int hY = lY + vt.bounds.height;
  				gridToScreen(lX + oX, hY + oY, tempPt1);
  				gridToScreen(hX + oX, lY + oY, tempPt2);
  				lX = tempPt1.x;
  				lY = tempPt1.y;
  				hX = tempPt2.x;
  				hY = tempPt2.y;
  //				int lX, hX, lY, hY;
  //				if (tempPt1.x < tempPt2.x) { lX = tempPt1.x;   hX = tempPt2.x; } else
  //					{ lX = tempPt2.x;   hX = tempPt1.x; }
  //				if (tempPt1.y < tempPt2.y) { lY = tempPt1.y;   hY = tempPt2.y; } else
  //					{ lY = tempPt2.y;   hY = tempPt1.y; }
  
  				// for ports, switch between the different port display methods
  //				if (vt.textType == VectorCache.VectorText.TEXTTYPEPORT)
  //				{
  //					int portDisplayLevel = User.getPortDisplayLevel();
  //					Color portColor = vt.e.getBasePort().getPortColor();
  //					if (vt.ni.isExpanded()) portColor = textColor;
  //					if (portColor != null) portGraphics.setColor(portColor);
  //					int cX = (lX + hX) / 2;
  //					int cY = (lY + hY) / 2;
  //					if (portDisplayLevel == 2)
  //					{
  //						// draw port as a cross
  //						int size = 3;
  //						offscreen.drawLine(new Point(cX-size, cY), new Point(cX+size, cY), null, portGraphics, 0, false);
  //						offscreen.drawLine(new Point(cX, cY-size), new Point(cX, cY+size), null, portGraphics, 0, false);
  //						crossCount++;
  //						continue;
  //					}
  //
  //					// draw port as text
  //					if (portDisplayLevel == 1) drawString = vt.e.getShortName(); else
  //						drawString = vt.e.getName();
  //					graphics = portGraphics;
  //					layerBitMap = null;
  //					lX = hX = cX;
  //					lY = hY = cY;
  //				} else
  				if (vt.textType == VectorCache.VectorText.TEXTTYPEEXPORT && vt.basePort != null)
  				{
  					if (!PixelDrawing.lv.isVisible(vt.basePort.getParent())) continue;
                      graphics = PixelDrawing.textGraphics;
  					int exportDisplayLevel = PixelDrawing.gp.exportDisplayLevel;
  					if (exportDisplayLevel == 2)
  					{
  						// draw export as a cross
  						int cX = (lX + hX) / 2;
  						int cY = (lY + hY) / 2;
  						int size = 3;
  						offscreen.drawLine(new Point(cX-size, cY), new Point(cX+size, cY), null, graphics, 0, false);
  						offscreen.drawLine(new Point(cX, cY-size), new Point(cX, cY+size), null, graphics, 0, false);
  						crossCount++;
  						continue;
  					}
  
  					// draw export as text
  					if (exportDisplayLevel == 1)
  						drawString = Export.getShortName(drawString);
  					layerBitMap = null;
  				}
  
  				textCount++;
  				tempRect.setBounds(lX, lY, hX-lX, hY-lY);
  				offscreen.drawText(tempRect, vt.style, vt.descript, drawString, layerBitMap, graphics, dimmed);
  			} else if (vb instanceof VectorCache.VectorCircle)
  			{
  				circleCount++;
  				VectorCache.VectorCircle vci = (VectorCache.VectorCircle)vb;
  				gridToScreen(vci.cX+oX, vci.cY+oY, tempPt1);
  				gridToScreen(vci.eX+oX, vci.eY+oY, tempPt2);
  				switch (vci.nature)
  				{
  					case 0: offscreen.drawCircle(tempPt1, tempPt2, layerBitMap, graphics, dimmed);        break;
  					case 1: offscreen.drawThickCircle(tempPt1, tempPt2, layerBitMap, graphics, dimmed);   break;
  					case 2: offscreen.drawDisc(tempPt1, tempPt2, layerBitMap, graphics, dimmed);          break;
  				}
  			} else if (vb instanceof VectorCache.VectorCircleArc)
  			{
  				arcCount++;
  				VectorCache.VectorCircleArc vca = (VectorCache.VectorCircleArc)vb;
  				gridToScreen(vca.cX+oX, vca.cY+oY, tempPt1);
  				gridToScreen(vca.eX1+oX, vca.eY1+oY, tempPt2);
  				gridToScreen(vca.eX2+oX, vca.eY2+oY, tempPt3);
  				offscreen.drawCircleArc(tempPt1, tempPt2, tempPt3, vca.thick, layerBitMap, graphics, dimmed);
  			}
  		}
  	}
  
  	/**
  	 * Method to draw a list of cached port shapes.
  	 * @param oX the X offset to draw the shapes (in database grid coordinates).
  	 * @param oY the Y offset to draw the shapes (in database grid coordinates).
  	 * @param expanded true to draw a list on expanded instance.
  	 * @param onPathDown true if this level of hierarchy is the current one in "down-in-place" editing.
  	 */
  	private void drawPortList(VectorCache.VectorSubCell vsc, VectorCache.VectorCell subVC_,
  		int oX, int oY, boolean expanded, boolean onPathDown)
  			throws AbortRenderingException
  	{
  		if (!PixelDrawing.gp.isTextVisibilityOn(TextDescriptor.TextType.PORT)) return;
  		// render all shapes
  		List<VectorCache.VectorCellExport> portShapes = subVC_.vcg.getPortShapes();
  		int[] portCenters = subVC_.getPortCenters();
  		assert portShapes.size()*2 == portCenters.length;
  		for (int i = 0; i < portShapes.size(); i++) {
  			VectorCache.VectorCellExport vce = portShapes.get(i);
  			if (stopRendering) throw new AbortRenderingException();
  
  			// get visual characteristics of shape
  			if (!onPathDown && vsc.shownPorts.get(vce.getChronIndex())) continue;
  			if (vce.height < maxTextSize) continue;
  
  			int cX = portCenters[i*2];
  			int cY = portCenters[i*2 + 1];
  			gridToScreen(cX + oX, cY + oY, tempPt1);
  			cX = tempPt1.x;
  			cY = tempPt1.y;
  
  			int portDisplayLevel = PixelDrawing.gp.portDisplayLevel;
              EGraphics portGraphics = expanded ? PixelDrawing.textGraphics : offscreen.getPortGraphics(vce.getBasePort());
  			if (portDisplayLevel == 2)
  			{
  				// draw port as a cross
  				int size = 3;
  				offscreen.drawLine(new Point(cX-size, cY), new Point(cX+size, cY), null, portGraphics, 0, false);
  				offscreen.drawLine(new Point(cX, cY-size), new Point(cX, cY+size), null, portGraphics, 0, false);
  				crossCount++;
  				continue;
  			}
  
  			// draw port as text
  			boolean shortName = portDisplayLevel == 1;
  			String drawString = vce.getName(shortName);
  
  			textCount++;
  			tempRect.setBounds(cX, cY, 0, 0);
  			offscreen.drawText(tempRect, vce.style, vce.descript, drawString, null, portGraphics, false);
  		}
  	}
  
  	/**
  	 * Method to convert a database grid coordinate to screen coordinates.
  	 * @param dbX the X coordinate (in database grid units).
  	 * @param dbY the Y coordinate (in database grid units).
  	 * @param result the Point in which to store the screen coordinates.
  	 */
  	private void gridToScreen(int dbX, int dbY, Point result)
  	{
  		if (false) {
  			result.x = ((dbX - factorX_) * scale_int) >> SCALE_SH;
  			result.y = ((factorY_ - dbY) * scale_int) >> SCALE_SH;
  		} else {
  			double scrX = (dbX - factorX) * scale_;
  			double scrY = (factorY - dbY) * scale_;
  			result.x = (int)(scrX >= 0 ? scrX + 0.5 : scrX - 0.5);
  			result.y = (int)(scrY >= 0 ? scrY + 0.5 : scrY - 0.5);
  		}
  	}
  
  	/**
  	 * Method to draw a tiny box on the screen in a given color.
  	 * Done when the object is too small to draw in full detail.
  	 * @param lX the low X coordinate of the box.
  	 * @param hX the high X coordinate of the box.
  	 * @param lY the low Y coordinate of the box.
  	 * @param hY the high Y coordinate of the box.
  	 * @param col the color to draw.
  	 */
  	private void drawTinyBox(int lX, int hX, int lY, int hY, int col, VectorCache.VectorCell greekedCell)
  	{
  		if (lX < screenLX) lX = screenLX;
  		if (hX >= screenHX) hX = screenHX-1;
  		if (lY < screenLY) lY = screenLY;
  		if (hY >= screenHY) hY = screenHY-1;
  		if (useCellGreekingImages)
  		{
  			if (greekedCell != null && greekedCell.fadeImageColors != null)
  			{
  				int backgroundColor = PixelDrawing.gp.getColor(User.ColorPrefType.BACKGROUND).getRGB();
  				int backgroundRed = (backgroundColor >> 16) & 0xFF;
  				int backgroundGreen = (backgroundColor >> 8) & 0xFF;
  				int backgroundBlue = backgroundColor & 0xFF;
  
  				// render the icon properly with scale
  				int greekWid = greekedCell.fadeImageWid;
  				int greekHei = greekedCell.fadeImageHei;
  				int wid = hX - lX;
  				int hei = hY - lY;
  				float xInc = greekWid / (float)wid;
  				float yInc = greekHei / (float)hei;
  				float yPos = 0;
  				for(int y=0; y<hei; y++)
  				{
  					float yEndPos = yPos + yInc;
  					int yS = (int)yPos;
  					int yE = (int)yEndPos;
  
  					float xPos = 0;
  					for(int x=0; x<wid; x++)
  					{
  						float xEndPos = xPos + xInc;
  						int xS = (int)xPos;
  						int xE = (int)xEndPos;
  
  						float r = 0, g = 0, b = 0;
  						float totalArea = 0;
  						for(int yGrab = yS; yGrab <= yE; yGrab++)
  						{
  							if (yGrab >= greekHei) continue;
  							float yArea = 1;
  							if (yGrab == yS) yArea = (1 - (yPos - yS));
  