package com.sun.electric.tool.user; import com.sun.electric.database.EditingPreferences; import com.sun.electric.tool.JobException; import com.sun.electric.database.hierarchy.Cell; import com.sun.electric.database.hierarchy.Export; import com.sun.electric.database.prototype.PortProto; import com.sun.electric.database.prototype.PortCharacteristic; import com.sun.electric.database.prototype.NodeProto; import com.sun.electric.database.geometry.ERectangle; import com.sun.electric.database.geometry.EPoint; import com.sun.electric.database.geometry.DBMath; import com.sun.electric.database.topology.NodeInst; import com.sun.electric.database.topology.PortInst; import com.sun.electric.database.topology.ArcInst; import com.sun.electric.database.variable.TextDescriptor; import com.sun.electric.technology.technologies.Artwork; import com.sun.electric.technology.technologies.Schematics; import com.sun.electric.technology.technologies.Generic; import com.sun.electric.technology.ArcProto; import com.sun.electric.technology.TechPool; import java.io.Serializable; import java.util.*; import java.awt.geom.Point2D; /** * Class to define parameters for automatic icon generation */ public class IconParameters implements Serializable { /** side for input ports (when placeByCellLocation false) */ int inputSide; /** side for output ports (when placeByCellLocation false) */ int outputSide; /** side for bidir ports (when placeByCellLocation false) */ int bidirSide; /** side for power ports (when placeByCellLocation false) */ int pwrSide; /** side for ground ports (when placeByCellLocation false) */ int gndSide; /** side for clock ports (when placeByCellLocation false) */ int clkSide; /** rotation of top text (when placeByCellLocation true) */ int topRot; /** rotation of bottom text (when placeByCellLocation true) */ int bottomRot; /** rotation of left text (when placeByCellLocation true) */ int leftRot; /** rotation of right text (when placeByCellLocation true) */ int rightRot; public static IconParameters makeInstance(boolean userDefaults) { return new IconParameters(userDefaults); } private IconParameters(boolean userDefaults) { inputSide = 0; outputSide = 1; bidirSide = 2; pwrSide = 3; gndSide = 3; clkSide = 0; topRot = 0; bottomRot = 0; leftRot = 0; rightRot = 0; if (userDefaults) initFromUserDefaults(); } public void initFromUserDefaults() { inputSide = User.getIconGenInputSide(); outputSide = User.getIconGenOutputSide(); bidirSide = User.getIconGenBidirSide(); pwrSide = User.getIconGenPowerSide(); gndSide = User.getIconGenGroundSide(); clkSide = User.getIconGenClockSide(); topRot = User.getIconGenTopRot(); bottomRot = User.getIconGenBottomRot(); leftRot = User.getIconGenLeftRot(); rightRot = User.getIconGenRightRot(); } /** * Method to create an icon for a cell. * @param curCell the cell to turn into an icon. * @return the icon cell (null on error). */ public Cell makeIconForCell(Cell curCell) throws JobException { // create the new icon cell EditingPreferences ep = curCell.getEditingPreferences(); String iconCellName = curCell.getName() + "{ic}"; Cell iconCell = Cell.makeInstance(curCell.getLibrary(), iconCellName); if (iconCell == null) throw new JobException("Cannot create Icon cell " + iconCellName); iconCell.setWantExpanded(); // determine number of ports on each side int leftSide = 0, rightSide = 0, bottomSide = 0, topSide = 0; Map<Export,Integer> portIndex = new HashMap<Export,Integer>(); Map<Export,Integer> portSide = new HashMap<Export,Integer>(); Map<Export,Integer> portRotation = new HashMap<Export,Integer>(); // make a sorted list of exports List<Export> exportList = new ArrayList<Export>(); for(Iterator<PortProto> it = curCell.getPorts(); it.hasNext(); ) { Export pp = (Export)it.next(); if (pp.isBodyOnly()) continue; exportList.add(pp); } if (ep.iconGenExportPlacement == 1) { // place exports according to their location in the cell Collections.sort(exportList, new ExportsByAngle()); // figure out how many exports go on each side int numExports = exportList.size(); leftSide = rightSide = topSide = bottomSide = numExports / 4; if (leftSide + rightSide + topSide + bottomSide < numExports) leftSide++; if (leftSide + rightSide + topSide + bottomSide < numExports) rightSide++; if (leftSide + rightSide + topSide + bottomSide < numExports) topSide++; // cache the location of each export Map<Export, Point2D> portCenters = new HashMap<Export,Point2D>(); for(int i=0; i<numExports; i++) { Export pp = exportList.get(i); portCenters.put(pp, pp.getOriginalPort().getCenter()); } // make an array of points in the middle of each side ERectangle bounds = curCell.getBounds(); Point2D leftPoint = new Point2D.Double(bounds.getCenterX() - bounds.getWidth(), bounds.getCenterY()); Point2D rightPoint = new Point2D.Double(bounds.getCenterX() + bounds.getWidth(), bounds.getCenterY()); Point2D topPoint = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY() + bounds.getWidth()); Point2D bottomPoint = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY() - bounds.getWidth()); Point2D[] sidePoints = new Point2D[numExports]; int fill = 0; for(int i=0; i<leftSide; i++) sidePoints[fill++] = leftPoint; for(int i=0; i<topSide; i++) sidePoints[fill++] = topPoint; for(int i=0; i<rightSide; i++) sidePoints[fill++] = rightPoint; for(int i=0; i<bottomSide; i++) sidePoints[fill++] = bottomPoint; // rotate the points and find the rotation with the least distance to the side points double [] totDist = new double[numExports]; for(int i=0; i<numExports; i++) { totDist[i] = 0; for(int j=0; j<numExports; j++) { Point2D ppCtr = portCenters.get(exportList.get((j+i)%numExports)); double dist = ppCtr.distance(sidePoints[j]); totDist[i] += dist; } } double bestDist = Double.MAX_VALUE; int bestIndex = -1; for(int i=0; i<numExports; i++) { if (totDist[i] < bestDist) { bestDist = totDist[i]; bestIndex = i; } } // assign ports along each side for(int i=0; i<leftSide; i++) { Export pp = exportList.get((i+bestIndex)%numExports); portSide.put(pp, new Integer(0)); portIndex.put(pp, new Integer(leftSide-i-1)); portRotation.put(pp, new Integer(leftRot)); } for(int i=0; i<topSide; i++) { Export pp = exportList.get((i+leftSide+bestIndex)%numExports); portSide.put(pp, new Integer(2)); portIndex.put(pp, new Integer(topSide-i-1)); portRotation.put(pp, new Integer(topRot)); } for(int i=0; i<rightSide; i++) { Export pp = exportList.get((i+leftSide+topSide+bestIndex)%numExports); portSide.put(pp, new Integer(1)); portIndex.put(pp, new Integer(i)); portRotation.put(pp, new Integer(rightRot)); } for(int i=0; i<bottomSide; i++) { Export pp = exportList.get((i+leftSide+topSide+rightSide+bestIndex)%numExports); portSide.put(pp, new Integer(3)); portIndex.put(pp, new Integer(i)); portRotation.put(pp, new Integer(bottomRot)); } } else { // place exports according to their characteristics if (ep.iconGenReverseExportOrder) Collections.reverse(exportList); for(Export pp : exportList) { int index = iconPosition(pp); portSide.put(pp, new Integer(index)); switch (index) { case 0: portIndex.put(pp, new Integer(leftSide++)); break; case 1: portIndex.put(pp, new Integer(rightSide++)); break; case 2: portIndex.put(pp, new Integer(topSide++)); break; case 3: portIndex.put(pp, new Integer(bottomSide++)); break; } int rotation = ViewChanges.iconTextRotation(pp); portRotation.put(pp, new Integer(rotation)); } } // determine the size of the "black box" core double xSize, ySize; if (ep.iconGenExportPlacement == 1 && ep.iconGenExportPlacementExact) { xSize = curCell.getDefWidth(); ySize = curCell.getDefHeight(); } else { ySize = Math.max(Math.max(leftSide, rightSide), 5) * ep.iconGenLeadSpacing; xSize = Math.max(Math.max(topSide, bottomSide), 3) * ep.iconGenLeadSpacing; } // create the "black box" NodeInst bbNi = null; if (ep.iconGenDrawBody) { bbNi = NodeInst.newInstance(Artwork.tech().openedThickerPolygonNode, new Point2D.Double(0,0), xSize, ySize, iconCell); if (bbNi == null) return null; EPoint[] boxOutline = new EPoint[5]; if (ep.iconGenExportPlacement == 1 && ep.iconGenExportPlacementExact) { boxOutline[0] = new EPoint(curCell.getBounds().getMinX(), curCell.getBounds().getMinY()); boxOutline[1] = new EPoint(curCell.getBounds().getMinX(), curCell.getBounds().getMaxY()); boxOutline[2] = new EPoint(curCell.getBounds().getMaxX(), curCell.getBounds().getMaxY()); boxOutline[3] = new EPoint(curCell.getBounds().getMaxX(), curCell.getBounds().getMinY()); boxOutline[4] = new EPoint(curCell.getBounds().getMinX(), curCell.getBounds().getMinY()); } else { boxOutline[0] = new EPoint(-xSize/2, -ySize/2); boxOutline[1] = new EPoint(-xSize/2, ySize/2); boxOutline[2] = new EPoint( xSize/2, ySize/2); boxOutline[3] = new EPoint( xSize/2, -ySize/2); boxOutline[4] = new EPoint(-xSize/2, -ySize/2); } bbNi.setTrace(boxOutline); // put the original cell name on it TextDescriptor td = TextDescriptor.getAnnotationTextDescriptor().withRelSize(ep.iconGenBodyTextSize); bbNi.newVar(Schematics.SCHEM_FUNCTION, curCell.getName(), td); } // place pins around the Black Box int total = 0; for(Export pp : exportList) { // determine location and side of the port int portPosition = portIndex.get(pp).intValue(); int index = portSide.get(pp).intValue(); double spacing = ep.iconGenLeadSpacing; double xPos = 0, yPos = 0; double xBBPos = 0, yBBPos = 0; if (ep.iconGenExportPlacement == 1 && ep.iconGenExportPlacementExact) { xBBPos = xPos = pp.getOriginalPort().getCenter().getX(); yBBPos = yPos = pp.getOriginalPort().getCenter().getY(); } else { switch (index) { case 0: // left side xBBPos = -xSize/2; xPos = xBBPos - ep.iconGenLeadLength; if (leftSide*2 < rightSide) spacing = ep.iconGenLeadSpacing * 2; yBBPos = yPos = ySize/2 - ((ySize - (leftSide-1)*spacing) / 2 + portPosition * spacing); break; case 1: // right side xBBPos = xSize/2; xPos = xBBPos + ep.iconGenLeadLength; if (rightSide*2 < leftSide) spacing = ep.iconGenLeadSpacing * 2; yBBPos = yPos = ySize/2 - ((ySize - (rightSide-1)*spacing) / 2 + portPosition * spacing); break; case 2: // top if (topSide*2 < bottomSide) spacing = ep.iconGenLeadSpacing * 2; xBBPos = xPos = xSize/2 - ((xSize - (topSide-1)*spacing) / 2 + portPosition * spacing); yBBPos = ySize/2; yPos = yBBPos + ep.iconGenLeadLength; break; case 3: // bottom if (bottomSide*2 < topSide) spacing = ep.iconGenLeadSpacing * 2; xBBPos = xPos = xSize/2 - ((xSize - (bottomSide-1)*spacing) / 2 + portPosition * spacing); yBBPos = -ySize/2; yPos = yBBPos - ep.iconGenLeadLength; break; } } int rotation = portRotation.get(pp).intValue(); if (makeIconExport(pp, index, xPos, yPos, xBBPos, yBBPos, iconCell, rotation)) total++; } // if no body, leads, or cell center is drawn, and there is only 1 export, add more if (!ep.iconGenDrawBody && !ep.iconGenDrawLeads && ep.placeCellCenter && total <= 1) { NodeInst.newInstance(Generic.tech().invisiblePinNode, new Point2D.Double(0,0), xSize, ySize, iconCell); } return iconCell; } /** * Method to determine the side of the icon that port "pp" belongs on. */ private int iconPosition(Export pp) { PortCharacteristic character = pp.getCharacteristic(); // special detection for power and ground ports if (pp.isPower()) character = PortCharacteristic.PWR; if (pp.isGround()) character = PortCharacteristic.GND; // see which side this type of port sits on if (character == PortCharacteristic.IN) return inputSide; if (character == PortCharacteristic.OUT) return outputSide; if (character == PortCharacteristic.BIDIR) return bidirSide; if (character == PortCharacteristic.PWR) return pwrSide; if (character == PortCharacteristic.GND) return gndSide; if (character.isClock()) return clkSide; return inputSide; } /** * Helper method to create an export in an icon. * @param pp the Export to build. * @param index the side (0: left, 1: right, 2: top, 3: bottom). * @param xPos the export location * @param yPos the export location * @param xBBPos the central box location * @param yBBPos the central box location. * @param np the cell in which to create the export. * @param textRotation * @return true if the export was created. */ public static boolean makeIconExport(Export pp, int index, double xPos, double yPos, double xBBPos, double yBBPos, Cell np, int textRotation) { EditingPreferences ep = pp.getEditingPreferences(); // presume "universal" exports (Generic technology) NodeProto pinType = Generic.tech().universalPinNode; double pinSizeX = 0, pinSizeY = 0; if (ep.iconGenExportTech != 0) { // instead, use "schematic" exports (Schematic Bus Pins) pinType = Schematics.tech().busPinNode; pinSizeX = pinType.getDefWidth(); pinSizeY = pinType.getDefHeight(); } // determine the type of wires used for leads ArcProto wireType = Schematics.tech().wire_arc; if (pp.getBasePort().connectsTo(Schematics.tech().bus_arc) && pp.getNameKey().isBus()) { wireType = Schematics.tech().bus_arc; pinType = Schematics.tech().busPinNode; pinSizeX = pinType.getDefWidth(); pinSizeY = pinType.getDefHeight(); } // if the export is on the body (no leads) then move it in if (!ep.iconGenDrawLeads) { xPos = xBBPos; yPos = yBBPos; } // make the pin with the port NodeInst pinNi = NodeInst.newInstance(pinType, new Point2D.Double(xPos, yPos), pinSizeX, pinSizeY, np); if (pinNi == null) return false; // export the port that should be on this pin PortInst pi = pinNi.getOnlyPortInst(); Export port = Export.newInstance(np, pi, pp.getName(), pp.getCharacteristic(), null); if (port != null) { TextDescriptor td = port.getTextDescriptor(Export.EXPORT_NAME); if (textRotation != 0) td = td.withRotation(TextDescriptor.Rotation.getRotationAt(textRotation)); switch (ep.iconGenExportStyle) { case 0: // Centered td = td.withPos(TextDescriptor.Position.CENT); break; case 1: // Inward switch (index) { case 0: td = td.withPos(TextDescriptor.Position.RIGHT); break; // left case 1: td = td.withPos(TextDescriptor.Position.LEFT); break; // right case 2: td = td.withPos(TextDescriptor.Position.DOWN); break; // top case 3: td = td.withPos(TextDescriptor.Position.UP); break; // bottom } break; case 2: // Outward switch (index) { case 0: td = td.withPos(TextDescriptor.Position.LEFT); break; // left case 1: td = td.withPos(TextDescriptor.Position.RIGHT); break; // right case 2: td = td.withPos(TextDescriptor.Position.UP); break; // top case 3: td= td.withPos(TextDescriptor.Position.DOWN); break; // bottom } break; } port.setTextDescriptor(Export.EXPORT_NAME, td); double xOffset = 0, yOffset = 0; int loc = ep.iconGenExportLocation; if (!ep.iconGenDrawLeads) loc = 0; switch (loc) { case 0: // port on body xOffset = xBBPos - xPos; yOffset = yBBPos - yPos; break; case 1: // port on lead end break; case 2: // port on lead middle xOffset = (xPos+xBBPos) / 2 - xPos; yOffset = (yPos+yBBPos) / 2 - yPos; break; } port.setOff(Export.EXPORT_NAME, xOffset, yOffset); port.setAlwaysDrawn(ep.iconsAlwaysDrawn); port.copyVarsFrom(pp); } // add lead if requested if (ep.iconGenDrawLeads) { pinType = wireType.findPinProto(); if (pinType == Schematics.tech().busPinNode) pinType = Generic.tech().invisiblePinNode; double wid = pinType.getDefWidth(); double hei = pinType.getDefHeight(); NodeInst ni = NodeInst.newInstance(pinType, new Point2D.Double(xBBPos, yBBPos), wid, hei, np); if (ni != null) { PortInst head = ni.getOnlyPortInst(); PortInst tail = pinNi.getOnlyPortInst(); ArcInst ai = ArcInst.makeInstance(wireType, head, tail, new Point2D.Double(xBBPos, yBBPos), new Point2D.Double(xPos, yPos), null); if (ai != null && wireType == Schematics.tech().bus_arc) { ai.setHeadExtended(false); ai.setTailExtended(false); } } } return true; } /** * Comparator class for sorting Exports by their angle about the cell center. */ private static class ExportsByAngle implements Comparator<Export> { /** * Method to sort Exports by their angle about the cell center. */ public int compare(Export p1, Export p2) { Cell cell = p1.getParent(); ERectangle bounds = cell.getBounds(); Point2D cellCtr = new Point2D.Double(bounds.getCenterX(), bounds.getCenterY()); Point2D p1Ctr = p1.getOriginalPort().getCenter(); Point2D p2Ctr = p2.getOriginalPort().getCenter(); double angle1 = DBMath.figureAngleRadians(cellCtr, p1Ctr); double angle2 = DBMath.figureAngleRadians(cellCtr, p2Ctr); if (angle1 < angle2) return 1; if (angle1 > angle2) return -1; return 0; } } }