/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: VectorCache.java
* Written by Dmitry Nadezhin, Sun Microsystems.
*
* Copyright (c) 2010, 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.database.geometry.bool;
import com.sun.electric.database.CellBackup;
import com.sun.electric.database.CellRevision;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.ImmutableExport;
import com.sun.electric.database.ImmutableIconInst;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.geometry.EGraphics;
import com.sun.electric.database.geometry.ERectangle;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.id.CellId;
import com.sun.electric.database.id.PrimitiveNodeId;
import com.sun.electric.database.id.PrimitivePortId;
import com.sun.electric.database.id.TechId;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.AbstractShapeBuilder;
import com.sun.electric.technology.Layer;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.util.math.Orientation;
import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
/**
*
*/
public class VectorCache {
private static boolean DEBUG = false;
private static final boolean USE_ELECTRICAL = false;
private static final boolean WIPE_PINS = true;
private Set<Layer> layers = new TreeSet<Layer>();
private Set<Layer> badLayers = new HashSet<Layer>();
private final Snapshot snapshot;
private final TechPool techPool;
private HashMap<CellId, MyVectorCell> cells = new HashMap<CellId,MyVectorCell>();
private final PrimitivePortId busPinPortId;
private final PrimitiveNodeId cellCenterId;
private final PrimitiveNodeId essentialBoundsId;
/** local shape builder */
private final ShapeBuilder shapeBuilder = new ShapeBuilder();
/** List of VectorManhattanBuilders */
private final HashMap<Layer,VectorManhattanBuilder> boxBuilders = new HashMap<Layer,VectorManhattanBuilder>();
private static final int[] NULL_INT_ARRAY = {};
private static class CellLayer {
final Layer layer;
int[] boxCoords = NULL_INT_ARRAY;
ERectangle localBounds;
ArrayList<MyVectorPolygon> polys = new ArrayList<MyVectorPolygon>();
private CellLayer(Layer layer) {
this.layer = layer;
}
private void setBoxCoords(int[] boxCoords) {
this.boxCoords = boxCoords;
int lX = Integer.MAX_VALUE, lY = Integer.MAX_VALUE, hX = Integer.MIN_VALUE, hY = Integer.MIN_VALUE;
for (int i = 0; i < boxCoords.length; i += 4) {
lX = Math.min(lX, boxCoords[i + 0]);
lY = Math.min(lY, boxCoords[i + 1]);
hX = Math.max(hX, boxCoords[i + 2]);
hY = Math.max(hY, boxCoords[i + 3]);
}
localBounds = ERectangle.fromGrid(lX, hY, hX - (long)lX, hY - (long)lY);
}
}
private class MyVectorCell {
private final TechId techId;
private final TreeMap<Layer,CellLayer> layers = new TreeMap<Layer,CellLayer>();
private final ArrayList<ImmutableNodeInst> subCells = new ArrayList<ImmutableNodeInst>();
private final List<ImmutableNodeInst> unmodifiebleSubCells = Collections.unmodifiableList(subCells);
MyVectorCell(CellId cellId) {
CellBackup cellBackup = snapshot.getCell(cellId);
techId = cellBackup.cellRevision.d.techId;
Technology tech = techPool.getTech(techId);
if (isCellParameterized(cellBackup.cellRevision)) {
throw new IllegalArgumentException();
}
long startTime = DEBUG ? System.currentTimeMillis() : 0;
for (VectorManhattanBuilder b : boxBuilders.values()) {
b.clear();
}
// draw all arcs
shapeBuilder.setup(cellBackup, Orientation.IDENT, USE_ELECTRICAL, WIPE_PINS, false, null);
shapeBuilder.polyLayer = null;
for (Layer layer: tech.getLayersSortedByHeight()) {
if (layer.getFunction() == Layer.Function.POLY1) {
shapeBuilder.polyLayer = layer;
}
}
for (ImmutableArcInst a: cellBackup.cellRevision.arcs) {
shapeBuilder.genShapeOfArc(a);
}
// draw all primitive nodes
for (ImmutableNodeInst n: cellBackup.cellRevision.nodes) {
if (n.protoId instanceof CellId) {
if (!n.orient.isManhattan()) {
throw new IllegalArgumentException();
}
subCells.add(n);
} else {
boolean hideOnLowLevel = n.is(ImmutableNodeInst.VIS_INSIDE) || n.protoId == cellCenterId ||
n.protoId == essentialBoundsId;
if (!hideOnLowLevel) {
PrimitiveNode pn = techPool.getPrimitiveNode((PrimitiveNodeId)n.protoId);
shapeBuilder.genShapeOfNode(n);
}
}
}
addBoxesFromBuilder(this, boxBuilders);
if (DEBUG) {
long stopTime = System.currentTimeMillis();
System.out.println((stopTime - startTime) + " init " + cellBackup.cellRevision.d.cellId);
}
}
private CellLayer getCellLayer(Layer layer) {
CellLayer cellLayer = layers.get(layer);
if (cellLayer == null) {
cellLayer = new CellLayer(layer);
layers.put(layer, cellLayer);
}
return cellLayer;
}
}
public Collection<Layer> getLayers() {
return new ArrayList<Layer>(layers);
}
public Collection<Layer> getBadLayers() {
return new ArrayList<Layer>(badLayers);
}
public boolean isBadLayer(Layer layer) {
return badLayers.contains(layer);
}
public VectorCache(Snapshot snapshot) {
this.snapshot = snapshot;
techPool = snapshot.getTechPool();
busPinPortId = techPool.getSchematics().busPinNode.getPort(0).getId();
cellCenterId = techPool.getGeneric().cellCenterNode.getId();
essentialBoundsId = techPool.getGeneric().essentialBoundsNode.getId();
}
public void scanLayers(CellId topCellId) {
HashSet<CellId> visited = new HashSet<CellId>();
scanLayers(topCellId, visited);
}
public List<ImmutableNodeInst> getSubcells(CellId cellId) {
return findVectorCell(cellId).unmodifiebleSubCells;
}
public int getNumBoxes(CellId cellId, Layer layer) {
CellLayer cellLayer = findVectorCell(cellId).layers.get(layer);
return cellLayer != null ? cellLayer.boxCoords.length/4 : 0;
}
public int getNumFlatBoxes(CellId cellId, Layer layer) {
return getNumFlatBoxes(cellId, layer, new HashMap<CellId,Integer>());
}
public ERectangle getLocalBounds(CellId cellId, Layer layer) {
CellLayer cellLayer = findVectorCell(cellId).layers.get(layer);
return cellLayer != null ? cellLayer.localBounds : null;
}
public void getBoxes(CellId cellId, Layer layer, int offset, int size, int[] result) {
CellLayer cellLayer = findVectorCell(cellId).layers.get(layer);
if (cellLayer == null || offset < 0 || size < 0 || offset + size > cellLayer.boxCoords.length/4 || size > result.length/4) {
throw new IndexOutOfBoundsException();
}
System.arraycopy(cellLayer.boxCoords, offset*4, result, 0, size*4);
}
public static interface PutRectangle {
public void put(int lx, int ly, int hx, int hy);
}
public void collectLayer(Layer layer, CellId cellId, boolean rotate, PutRectangle putRectangle) {
Orientation orient = (rotate ? Orientation.XR : Orientation.IDENT).canonic();
collectLayer(layer, findVectorCell(cellId), new Point(0, 0), orient, putRectangle);
}
private void scanLayers(CellId cellId, HashSet<CellId> visited) {
if (!visited.add(cellId))
return;
MyVectorCell mvc = findVectorCell(cellId);
for (ImmutableNodeInst n: mvc.subCells) {
scanLayers((CellId)n.protoId, visited);
}
}
private MyVectorCell findVectorCell(CellId cellId) {
MyVectorCell mvc = cells.get(cellId);
if (mvc == null) {
mvc = new MyVectorCell(cellId);
cells.put(cellId, mvc);
}
return mvc;
}
private int getNumFlatBoxes(CellId cellId, Layer layer, HashMap<CellId,Integer> numFlatBoxes) {
Integer num = numFlatBoxes.get(cellId);
if (num == null) {
int count = getNumBoxes(cellId, layer);
for (ImmutableNodeInst n: getSubcells(cellId)) {
count += getNumFlatBoxes((CellId)n.protoId, layer, numFlatBoxes);
}
num = Integer.valueOf(count);
numFlatBoxes.put(cellId, num);
}
return num.intValue();
}
private void addBoxesFromBuilder(MyVectorCell vc, HashMap<Layer,VectorManhattanBuilder> boxBuilders) {
for (Map.Entry<Layer,VectorManhattanBuilder> e: boxBuilders.entrySet()) {
Layer layer = e.getKey();
VectorManhattanBuilder b = e.getValue();
if (b.size == 0) {
continue;
}
CellLayer cellLayer = vc.getCellLayer(layer);
assert cellLayer.boxCoords.length == 0;
cellLayer.setBoxCoords(b.toArray());
}
}
private void collectLayer(Layer layer, MyVectorCell vc, Point anchor, Orientation orient, PutRectangle putRectangle) {
int[] coords = new int[4];
CellLayer cellLayer = vc.layers.get(layer);
if (cellLayer != null) {
int[] boxCoords = cellLayer.boxCoords;
for (int i = 0; i < boxCoords.length; i += 4) {
coords[0] = boxCoords[i + 0];
coords[1] = boxCoords[i + 1];
coords[2] = boxCoords[i + 2];
coords[3] = boxCoords[i + 3];
orient.rectangleBounds(coords);
int lx = anchor.x + coords[0];
int ly = anchor.y + coords[1];
int hx = anchor.x + coords[2];
int hy = anchor.y + coords[3];
assert lx <= hx && ly <= hy;
putRectangle.put(lx, ly, hx, hy);
}
}
for (ImmutableNodeInst n: vc.subCells) {
assert n.orient.isManhattan();
coords[0] = (int)n.anchor.getGridX();
coords[1] = (int)n.anchor.getGridY();
orient.transformPoints(1, coords);
Orientation subOrient = orient.concatenate(n.orient).canonic();
MyVectorCell subCell = findVectorCell((CellId)n.protoId);
collectLayer(layer, subCell, new Point(anchor.x + coords[0], anchor.y + coords[1]), subOrient, putRectangle);
}
}
/**
* Method to tell whether a Cell is parameterized.
* Code is taken from tool.drc.Quick.checkEnumerateProtos
* Could also use the code in tool.io.output.Spice.checkIfParameterized
* @param cellRevision the Cell to examine
* @return true if the cell has parameters
*/
private boolean isCellParameterized(CellRevision cellRevision) {
if (cellRevision.d.getNumParameters() > 0) {
return true;
}
// look for any Java coded stuff (Logical Effort calls)
for (ImmutableNodeInst n : cellRevision.nodes) {
if (n instanceof ImmutableIconInst) {
for (Iterator<Variable> vIt = ((ImmutableIconInst) n).getDefinedParameters(); vIt.hasNext();) {
Variable var = vIt.next();
if (var.isCode()) {
return true;
}
}
}
for (Iterator<Variable> vIt = n.getVariables(); vIt.hasNext();) {
Variable var = vIt.next();
if (var.isCode()) {
return true;
}
}
}
for (ImmutableArcInst a : cellRevision.arcs) {
for (Iterator<Variable> vIt = a.getVariables(); vIt.hasNext();) {
Variable var = vIt.next();
if (var.isCode()) {
return true;
}
}
}
// bus pin appearance depends on parent Cell
for (ImmutableExport e : cellRevision.exports) {
if (e.originalPortId == busPinPortId) {
return true;
}
}
return false;
}
private class ShapeBuilder extends AbstractShapeBuilder {
private Layer polyLayer;
@Override
public void pushPoly(Poly.Type style, Layer layer, EGraphics graphicsOverride, PrimitivePort pp) {
if (layer.getFunction() == Layer.Function.GATE && polyLayer != null) {
layer = polyLayer;
}
super.pushPoly(style, layer, null, null);
}
@Override
public void addDoublePoly(int numPoints, Poly.Type style, Layer layer, EGraphics graphicsOverride, PrimitivePort pp) {
if (numPoints == 2)
return;
if (layer.isPseudoLayer()) {
return;
}
badLayer(layer);
}
@Override
public void addDoubleTextPoly(int numPoints, Poly.Type style, Layer layer, PrimitivePort pp, String message, TextDescriptor descriptor) {
badLayer(layer);
}
@Override
public void addIntPoly(int numPoints, Poly.Type style, Layer layer, EGraphics graphicsOverride, PrimitivePort pp) {
if (numPoints == 2)
return;
badLayer(layer);
}
@Override
public void addIntBox(int[] coords, Layer layer) {
layers.add(layer);
// convert coordinates
int lX = coords[0];
int lY = coords[1];
int hX = coords[2];
int hY = coords[3];
putBox(layer, boxBuilders, lX, lY, hX, hY);
}
}
private void badLayer(Layer layer) {
if (badLayers.add(layer))
layer = layer;
}
private static void putBox(Layer layer, HashMap<Layer,VectorManhattanBuilder> boxBuilders, int lX, int lY, int hX, int hY) {
VectorManhattanBuilder b = boxBuilders.get(layer);
if (b == null) {
b = new VectorManhattanBuilder();
boxBuilders.put(layer, b);
}
assert lX <= hX && lY <= hY;
if (lX < hX && lY < hY)
b.add(lX, lY, hX, hY);
}
static class MyVectorPolygon {
final Layer layer;
final Poly.Type style;
final Point2D[] points;
private MyVectorPolygon(Poly.Type style, Layer layer, Point2D[] points) {
this.layer = layer;
this.style = style;
this.points = points;
}
}
/**
* Class which collects boxes for VectorManhattan.
*/
static class VectorManhattanBuilder {
/** Number of boxes. */
int size; // number of boxes
/** Coordiantes of boxes. */
int[] coords = new int[4];
private void add(int lX, int lY, int hX, int hY) {
if (size * 4 >= coords.length) {
int[] newCoords = new int[coords.length * 2];
System.arraycopy(coords, 0, newCoords, 0, coords.length);
coords = newCoords;
}
int i = size * 4;
coords[i] = lX;
coords[i + 1] = lY;
coords[i + 2] = hX;
coords[i + 3] = hY;
size++;
}
int[] toArray() {
int[] a = new int[size * 4];
System.arraycopy(coords, 0, a, 0, a.length);
return a;
}
private void clear() {
size = 0;
}
}
}