/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: VectorDrawing.java
*
* Copyright (c) 2005, 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 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.EGraphics;
import com.sun.electric.database.geometry.Poly;
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.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 com.sun.electric.tool.user.ui.LayerVisibility;
import com.sun.electric.tool.util.concurrent.utils.ElapseTimer;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath;
import com.sun.electric.util.math.GenMath.MutableDouble;
import com.sun.electric.util.math.Orientation;
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 ElapseTimer timer = ElapseTimer.createInstance();
/** 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, LayerVisibility lv) {
// 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
timer.start();
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, lv);
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;
timer.end();
System.out.println("Time to render: " + timer + " 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.
* @param lv
* current layer visibility.
*/
private void render(VectorCache.VectorCell vc, int oX, int oY, VarContext context, int level, LayerVisibility lv)
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, lv);
int fadeColor = getFadeColor(subVC, subContext, lv);
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, lv);
int fadeColor = getFadeColor(subVC, context, lv);
drawTinyBox(lX, hX, lY, hY, fadeColor, subVC);
tinySubCellCount++;
continue;
}
}
int subLevel = level;
if (subLevel == 0)
subLevel = 1;
render(subVC, soX, soY, subContext, subLevel, lv);
} 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;
if (vt.tempName && !PixelDrawing.gp.isShowTempNames()) {
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, vca.bigArc, 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, LayerVisibility lv) 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, null);
subVD.render(subVC, 0, 0, VarContext.globalContext, -1, lv);
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, LayerVisibility lv)
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) {
if (!lv.isVisible(layer))
continue;
MutableDouble md = layerAreas.get(layer);
totalArea += md.doubleValue();
}
double r = 0, g = 0, b = 0;
if (totalArea == 0) {
// no fade color, make it the background color
vc.fadeColor = PixelDrawing.gp.getColor(User.ColorPrefType.BACKGROUND).getRGB();
} else {
for (Layer layer : keys) {
if (!lv.isVisible(layer))
continue;
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 (timer.currentTimeLong() > 1000) {
System.out.print("Display caching, please wait...");
Main.setBusyCursor(true);
takingLongTime = true;
}
}
return VectorCache.theCache.drawCell(cell.getId(), prevTrans, context, scale);
}
}