/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Utils.java
*
* 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.tool.erc.wellcheck;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.List;
import com.sun.electric.database.topology.RTNode;
import com.sun.electric.technology.PrimitiveNode;
import com.sun.electric.tool.erc.ERCWellCheck.WellBound;
import com.sun.electric.tool.erc.ERCWellCheck.WellBoundRecord;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.GenMath.MutableBoolean;
/**
* Utilities for ERC well check
*
*/
public class Utils {
public enum WorkDistributionStrategy {
/**
* assign the well contacts to the worker which is most closed by
*/
cluster,
/**
* assign the well contacts randomly
*/
random,
/**
* Use a grid over the cell to find the right worker
*/
bucket;
}
public static final boolean GATHERSTATISTICS = false;
public static final boolean INCREMENTALGROWTH = false;
// Change this static variable to change the work distribution
public static final WorkDistributionStrategy WORKDISTRIBUTION = WorkDistributionStrategy.bucket;
public static List<WellBoundRecord> wellBoundSearchOrder;
public static int numObjSearches;
/**
* Method to tell whether this function describes an element which can be
* used as substrate tap (ERC)
*
* @return
*/
public static boolean canBeSubstrateTap(PrimitiveNode.Function fun) {
return fun == PrimitiveNode.Function.SUBSTRATE || fun == PrimitiveNode.Function.RESPWELL;
}
/**
* Method to tell whether this function describes an element which can be
* used as well tap (ERC)
*
* @return
*/
public static boolean canBeWellTap(PrimitiveNode.Function fun) {
return fun == PrimitiveNode.Function.WELL || fun == PrimitiveNode.Function.RESNWELL;
}
/**
* Search area for touching well polygons
*
* @param cX
* @param cY
* @param wellNum
* @param rtree
* @param threadIndex
*/
public static void spreadWellSeed(double cX, double cY, NetValues wellNum, RTNode rtree,
int threadIndex) {
RTNode allFound = null;
Point2D ctr = new Point2D.Double(cX, cY);
Rectangle2D searchArea = new Rectangle2D.Double(cX, cY, 0, 0);
MutableBoolean keepSearching = new MutableBoolean(true);
Rectangle2D[] sides = new Rectangle2D[4];
for (int i = 0; i < 4; i++)
sides[i] = new Rectangle2D.Double(0, 0, 0, 0);
int numSides = 1;
sides[0].setRect(searchArea);
while (keepSearching.booleanValue()) {
if (INCREMENTALGROWTH) {
// grow the search area incrementally
double lX = sides[0].getMinX(), hX = sides[0].getMaxX();
double lY = sides[0].getMinY(), hY = sides[0].getMaxY();
for (int i = 1; i < numSides; i++) {
lX = Math.min(lX, sides[i].getMinX());
hX = Math.max(hX, sides[i].getMinX());
lY = Math.min(lY, sides[i].getMinY());
hY = Math.max(hY, sides[i].getMinY());
}
double newLX = lX, newHX = hX;
double newLY = lY, newHY = hY;
boolean anySearchesGood = false;
for (int i = 0; i < numSides; i++) {
allFound = searchInArea(sides[i], wellNum, rtree, allFound, ctr, keepSearching,
threadIndex);
if (keepSearching.booleanValue())
anySearchesGood = true;
newLX = Math.min(newLX, sides[i].getMinX());
newHX = Math.max(newHX, sides[i].getMinX());
newLY = Math.min(newLY, sides[i].getMinY());
newHY = Math.max(newHY, sides[i].getMinY());
}
keepSearching.setValue(anySearchesGood);
// compute new bounds
numSides = 0;
if (newLX < lX)
sides[numSides++].setRect(newLX, newLY, lX - newLX, newHY - newLY);
if (newHX > hX)
sides[numSides++].setRect(hX, newLY, newHX - hX, newHY - newLY);
if (newLY < lY)
sides[numSides++].setRect(newLX, newLY, newHX - newLX, lY - newLY);
if (newHY > hY)
sides[numSides++].setRect(newLX, hY, newHX - newLX, newHY - hY);
} else {
// just keep growing the search area
allFound = searchInArea(searchArea, wellNum, rtree, allFound, ctr, keepSearching,
threadIndex);
}
}
}
/**
* Search in a given area
*
* @param searchArea
* @param wellNum
* @param rtree
* @param allFound
* @param ctr
* @param keepSearching
* @param threadIndex
* @return
*/
public static RTNode searchInArea(Rectangle2D searchArea, NetValues wellNum, RTNode rtree,
RTNode allFound, Point2D ctr, MutableBoolean keepSearching, int threadIndex) {
keepSearching.setValue(false);
for (RTNode.Search sea = new RTNode.Search(searchArea, rtree, true); sea.hasNext();) {
WellBound wb = (WellBound) sea.next();
if (GATHERSTATISTICS)
numObjSearches++;
// ignore if this object is already properly connected
if (wb.getNetID() != null && wb.getNetID().getIndex() == wellNum.getIndex())
continue;
// see if this object actually touches something in the set
if (allFound == null) {
// start from center of contact
if (!wb.getBounds().contains(ctr))
continue;
} else {
boolean touches = false;
for (RTNode.Search subSea = new RTNode.Search(wb.getBounds(), allFound, true); subSea
.hasNext();) {
WellBound subWB = (WellBound) subSea.next();
if (DBMath.rectsIntersect(subWB.getBounds(), wb.getBounds())) {
touches = true;
break;
}
}
if (!touches)
continue;
}
// this touches what is gathered so far: add to it
synchronized (wb) {
if (wb.getNetID() != null)
wellNum.merge(wb.getNetID());
else
wb.setNetID(wellNum);
}
if (GATHERSTATISTICS)
wellBoundSearchOrder.add(new WellBoundRecord(wb, threadIndex));
// expand the next search area by this added bound
Rectangle2D.union(searchArea, wb.getBounds(), searchArea);
if (allFound == null)
allFound = RTNode.makeTopLevel();
allFound = RTNode.linkGeom(null, allFound, wb);
keepSearching.setValue(true);
}
return allFound;
}
}