/* -*- 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));
if (yGrab == yE) yArea *= (yEndPos-yE);
for(int xGrab = xS; xGrab <= xE; xGrab++)
{
if (xGrab >= greekWid) continue;
int index = xGrab + yGrab*greekedCell.fadeImageWid;
if (greekedCell.fadeImageColors==null || index >= greekedCell.fadeImageColors.length)
continue;
int value = greekedCell.fadeImageColors[index];
int red = (value >> 16) & 0xFF;
int green = (value >> 8) & 0xFF;
int blue = value & 0xFF;
float area = yArea;
if (xGrab == xS) area *= (1 - (xPos - xS));
if (xGrab == xE) area *= (xEndPos-xE);
if (area <= 0) continue;
r += red * area;
g += green * area;
b += blue * area;
totalArea += area;
}
}
if (totalArea > 0)
{
int red = (int)(r / totalArea);
if (red > 255) red = 255;
int green = (int)(g / totalArea);
if (green > 255) green = 255;
int blue = (int)(b / totalArea);
if (blue > 255) blue = 255;
if (Math.abs(backgroundRed-red) > 2 || Math.abs(backgroundGreen-green) > 2 ||
Math.abs(backgroundBlue-blue) > 2)
{
offscreen.drawPoint(lX+x, lY+y, null, (red << 16) | (green << 8) | blue);
}
}
xPos = xEndPos;
}
yPos = yEndPos;
}
if (DEBUGIMAGES)
{
for(int y=0; y<greekedCell.fadeImageHei; y++)
{
for(int x=0; x<greekedCell.fadeImageWid; x++)
{
int valToSet = greekedCell.fadeImageColors[x+y*greekedCell.fadeImageWid];
topVD.offscreen.drawPoint(greekedCell.fadeOffsetX+x+1, greekedCell.fadeOffsetY+y+1, null, valToSet);
}
topVD.offscreen.drawPoint(greekedCell.fadeOffsetX, greekedCell.fadeOffsetY+y+1, null, 0);
topVD.offscreen.drawPoint(greekedCell.fadeOffsetX+greekedCell.fadeImageWid+1, greekedCell.fadeOffsetY+y+1, null, 0);
}
for(int x=0; x<greekedCell.fadeImageWid; x++)
{
topVD.offscreen.drawPoint(greekedCell.fadeOffsetX+x, greekedCell.fadeOffsetY, null, 0);
topVD.offscreen.drawPoint(greekedCell.fadeOffsetX+x, greekedCell.fadeOffsetY+greekedCell.fadeImageHei+1, null, 0);
}
}
return;
}
}
// no greeked image: just use the greeked color
for(int y=lY; y<=hY; y++)
{
for(int x=lX; x<=hX; x++)
offscreen.drawPoint(x, y, null, col);
}
}
/**
* Method to determine whether a cell has tiny contents.
* Recursively examines the cache of this and all subcells to see if the
* maximum feature sizes are all below the global threshold "maxObjectSize".
* @param cell the Cell in question.
* @param vc the cached representation of the cell.
* @param trans the Orientation of the cell.
* @return true if the cell has all tiny contents.
*/
private boolean isContentsTiny(Cell cell, VectorCache.VectorCell vc, Orientation trans, VarContext context)
throws AbortRenderingException
{
if (vc.maxFeatureSize > maxObjectSize) return false;
for(VectorCache.VectorSubCell vsc : vc.subCells)
{
boolean isExpanded = cell.isExpanded(vsc.n.nodeId);
// NodeInst ni = cell.getNodeById(vsc.n.nodeId);
VectorCache.VectorCell subVC = VectorCache.theCache.findVectorCell(vsc.subCellId, vc.orient.concatenate(vsc.n.orient));
if (isExpanded || fullInstantiate)
{
Orientation thisOrient = vsc.n.orient;
Orientation recurseTrans = trans.concatenate(thisOrient);
VarContext subContext = context.push(cell, vsc.n);
Cell subCell = VectorCache.theCache.database.getCell(vsc.subCellId);
VectorCache.VectorCell subVC_ = drawCell(subCell, recurseTrans, subContext);
assert subVC_ == subVC;
boolean subCellTiny = isContentsTiny(subCell, subVC, recurseTrans, subContext);
if (!subCellTiny) return false;
continue;
}
if (subVC.vcg.cellMinSize > maxObjectSize) return false;
}
return true;
}
private void makeGreekedImage(VectorCache.VectorCell subVC)
throws AbortRenderingException
{
if (subVC.fadeImage) return;
if (!useCellGreekingImages) return;
// determine size and scale of greeked cell image
Rectangle2D cellBounds = subVC.vcg.bounds;
Rectangle2D ownBounds = new Rectangle2D.Double(cellBounds.getMinX(), cellBounds.getMinY(), cellBounds.getWidth(), cellBounds.getHeight());
AffineTransform trans = subVC.orient.rotateAbout(0, 0);
DBMath.transformRect(ownBounds, trans);
double greekScale = MAXGREEKSIZE / ownBounds.getHeight();
if (ownBounds.getWidth() > ownBounds.getHeight())
greekScale = MAXGREEKSIZE / ownBounds.getWidth();
int lX = (int)Math.floor(cellBounds.getMinX()*greekScale);
int hX = (int)Math.ceil(cellBounds.getMaxX()*greekScale);
int lY = (int)Math.floor(cellBounds.getMinY()*greekScale);
int hY = (int)Math.ceil(cellBounds.getMaxY()*greekScale);
if (hX <= lX) hX = lX + 1;
int greekWid = hX - lX;
if (hY <= lY) hY = lY + 1;
int greekHei = hY - lY;
Rectangle screenBounds = new Rectangle(lX, lY, greekWid, greekHei);
// construct the offscreen buffers for the greeked cell image
PixelDrawing offscreen = new PixelDrawing(greekScale, screenBounds);
Point2D cellCtr = new Point2D.Double(ownBounds.getCenterX(), ownBounds.getCenterY());
VectorDrawing subVD = new VectorDrawing(useCellGreekingImages);
subVC.fadeOffsetX = debugXP;
subVC.fadeOffsetY = debugYP;
debugXP += MAXGREEKSIZE + 5;
if (topVD != null)
{
if (debugXP + MAXGREEKSIZE+2 >= topVD.offscreen.getSize().width)
{
debugXP = 0;
debugYP += MAXGREEKSIZE + 5;
}
}
// set rendering information for the greeked cell image
subVD.offscreen = offscreen;
subVD.screenLX = 0; subVD.screenHX = greekWid;
subVD.screenLY = 0; subVD.screenHY = greekHei;
subVD.szHalfWidth = greekWid / 2;
subVD.szHalfHeight = greekHei / 2;
subVD.maxObjectSize = 0;
subVD.maxTextSize = 0;
subVD.scale = (float)greekScale;
subVD.scale_ = (float)(greekScale/DBMath.GRID);
subVD.factorX = (float)(cellCtr.getX()*DBMath.GRID - subVD.szHalfWidth/subVD.scale_);
subVD.factorY = (float)(cellCtr.getY()*DBMath.GRID + subVD.szHalfHeight/subVD.scale_);
subVD.factorX_ = (int)subVD.factorX;
subVD.factorY_ = (int)subVD.factorY;
subVD.scale_int = (int)(subVD.scale_ * (1 << SCALE_SH));
subVD.fullInstantiate = true;
subVD.takingLongTime = true;
// render the greeked cell
subVD.offscreen.clearImage(null);
subVD.render(subVC, 0, 0, VarContext.globalContext, -1);
subVD.offscreen.composite(null);
// remember the greeked cell image
int[] img = offscreen.getOpaqueData();
subVC.fadeImageWid = greekWid;
subVC.fadeImageHei = greekHei;
subVC.fadeImageColors = new int[subVC.fadeImageWid * subVC.fadeImageHei];
int i = 0;
for(int y=0; y<subVC.fadeImageHei; y++)
{
for(int x=0; x<subVC.fadeImageWid; x++)
{
int value = img[i];
subVC.fadeImageColors[i++] = value & 0xFFFFFF;
}
}
subVC.fadeImage = true;
}
/**
* Method to determine the "fade" color for a cached cell.
* Fading is done when the cell is too tiny to draw (or all of its contents are too tiny).
* Instead of drawing the cell contents, the entire cell is painted with the "fade" color.
* @param vc the cached cell.
* @return the fade color (an integer with red/green/blue).
*/
private int getFadeColor(VectorCache.VectorCell vc, VarContext context)
throws AbortRenderingException
{
if (vc.hasFadeColor) return vc.fadeColor;
// examine all shapes
Map<Layer,MutableDouble> layerAreas = new HashMap<Layer,MutableDouble>();
gatherContents(vc, layerAreas, context);
// now compute the color
Set<Layer> keys = layerAreas.keySet();
double totalArea = 0;
for(Layer layer : keys)
{
MutableDouble md = layerAreas.get(layer);
totalArea += md.doubleValue();
}
double r = 0, g = 0, b = 0;
if (totalArea != 0)
{
for(Layer layer : keys)
{
MutableDouble md = layerAreas.get(layer);
double portion = md.doubleValue() / totalArea;
EGraphics desc = PixelDrawing.gp.getGraphics(layer);
Color col = desc.getColor();
r += col.getRed() * portion;
g += col.getGreen() * portion;
b += col.getBlue() * portion;
}
}
if (r < 0) r = 0; if (r > 255) r = 255;
if (g < 0) g = 0; if (g > 255) g = 255;
if (b < 0) b = 0; if (b > 255) b = 255;
vc.fadeColor = (((int)r) << 16) | (((int)g) << 8) | (int)b;
vc.hasFadeColor = true;
return vc.fadeColor;
}
/**
* Helper method to recursively examine a cached cell and its subcells and compute
* the coverage of each layer.
* @param vc the cached cell to examine.
* @param layerAreas a HashMap of all layers and the areas they cover.
*/
private void gatherContents(VectorCache.VectorCell vc, Map<Layer,MutableDouble> layerAreas, VarContext context)
throws AbortRenderingException
{
for(VectorCache.VectorBase vb : vc.shapes)
{
Layer layer = vb.layer;
if (layer == null) continue;
Layer.Function fun = layer.getFunction();
if (fun.isImplant() || fun.isSubstrate()) continue;
// handle each shape
double area = 0;
if (vb instanceof VectorCache.VectorManhattan)
{
VectorCache.VectorManhattan vm = (VectorCache.VectorManhattan)vb;
for (int i = 0; i < vm.coords.length; i += 4) {
double c1X = vm.coords[i];
double c1Y = vm.coords[i + 1];
double c2X = vm.coords[i + 2];
double c2Y = vm.coords[i + 3];
area += (c1X-c2X) * (c1Y-c2Y);
}
} else if (vb instanceof VectorCache.VectorPolygon)
{
VectorCache.VectorPolygon vp = (VectorCache.VectorPolygon)vb;
area = GenMath.getAreaOfPoints(vp.points);
} else if (vb instanceof VectorCache.VectorCircle)
{
VectorCache.VectorCircle vci = (VectorCache.VectorCircle)vb;
double radius = new Point2D.Double(vci.cX, vci.cY).distance(new Point2D.Double(vci.eX, vci.eY));
area = radius * radius * Math.PI;
}
if (area == 0) continue;
MutableDouble md = layerAreas.get(layer);
if (md == null)
{
md = new MutableDouble(0);
layerAreas.put(layer, md);
}
md.setValue(md.doubleValue() + area);
}
Cell cell = VectorCache.theCache.database.getCell(vc.vcg.cellId);
for(VectorCache.VectorSubCell vsc : vc.subCells)
{
VectorCache.VectorCellGroup vcg = VectorCache.theCache.findCellGroup(vsc.subCellId);
VectorCache.VectorCell subVC = vcg.getAnyCell();
VarContext subContext = context.push(cell, vsc.n);
if (subVC == null)
{
// NodeInst ni = cell.getNodeById(vsc.n.nodeId);
Cell nodeProto = cell.getDatabase().getCell((CellId)vsc.n.protoId);
subVC = drawCell(nodeProto, Orientation.IDENT, subContext);
}
gatherContents(subVC, layerAreas, subContext);
}
}
// ************************************* CACHE CREATION *************************************
/**
* Method to cache the contents of a cell.
* @param cell the Cell to cache
* @param prevTrans the orientation of the cell (just a rotation, no offsets here).
* @return a cached cell object for the given Cell.
*/
private VectorCache.VectorCell drawCell(Cell cell, Orientation prevTrans, VarContext context)
throws AbortRenderingException
{
// caching the cell: check for abort and delay reporting
if (stopRendering) throw new AbortRenderingException();
if (!takingLongTime)
{
long currentTime = System.currentTimeMillis();
if (currentTime - startTime > 1000)
{
System.out.print("Display caching, please wait...");
Main.setBusyCursor(true);
takingLongTime = true;
}
}
return VectorCache.theCache.drawCell(cell.getId(), prevTrans, context, scale);
}
}