/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: RouteElementPort.java
*
* Copyright (c) 2003 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.routing;
import com.sun.electric.database.geometry.Dimension2D;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.Poly;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.database.prototype.NodeProto;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.topology.ArcInst;
import com.sun.electric.database.topology.Connection;
import com.sun.electric.database.topology.NodeInst;
import com.sun.electric.database.topology.PortInst;
import com.sun.electric.database.variable.ElectricObject;
import com.sun.electric.technology.SizeOffset;
import com.sun.electric.tool.routing.RouteElement.RouteElementAction;
import com.sun.electric.tool.user.Highlighter;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Class for defining RouteElements that are ports.
*/
public class RouteElementPort extends RouteElement {
// ---- New Port info ----
/** Node type to create */ private NodeProto np;
/** Port on node to use */ private PortProto portProto;
/** location to create Node */ private EPoint location;
/** size aspect that is seen on screen */ private double width, height;
/** if this bisects an arc */ private boolean isBisectArcPin;
/** RouteElementArcs connecting to this */ private List<RouteElementArc> newArcs;
/** port site spatial extents (db units) */ private transient Poly portInstSite;
/** This contains the newly created instance, or the instance to delete */ private NodeInst nodeInst;
/** This contains the newly created portinst, or the existing port inst */ private PortInst portInst;
/**
* Private Constructor
* @param action the action this RouteElementAction will do.
*/
private RouteElementPort(RouteElementAction action, Cell cell) { super(action, cell); }
/**
* Factory method for making a newNode RouteElement
* @param np Type of NodeInst to make
* @param location the location of the new NodeInst
* @param width the width of the new NodeInst
* @param height the height of the new NodeInst
*/
public static RouteElementPort newNode(Cell cell, NodeProto np, PortProto newNodePort, Point2D location,
double width, double height) {
RouteElementPort e = new RouteElementPort(RouteElement.RouteElementAction.newNode, cell);
e.np = np;
e.portProto = newNodePort;
e.location = EPoint.snap(location);
e.isBisectArcPin = false;
e.newArcs = new ArrayList<RouteElementArc>();
e.setNodeSize(new Dimension2D.Double(width, height));
e.nodeInst = null;
e.portInst = null;
Point2D [] points = {location};
e.portInstSite = new Poly(points);
return e;
}
/**
* Factory method for making a deleteNode RouteElement
* @param nodeInstToDelete the nodeInst to delete
*/
public static RouteElementPort deleteNode(NodeInst nodeInstToDelete) {
RouteElementPort e = new RouteElementPort(RouteElement.RouteElementAction.deleteNode, nodeInstToDelete.getParent());
e.np = nodeInstToDelete.getProto();
e.portProto = null;
e.location = EPoint.snap(nodeInstToDelete.getTrueCenter());
e.isBisectArcPin = false;
e.newArcs = new ArrayList<RouteElementArc>();
e.setNodeSize(new Dimension2D.Double(nodeInstToDelete.getXSizeWithoutOffset(), nodeInstToDelete.getYSizeWithoutOffset()));
e.nodeInst = nodeInstToDelete;
e.portInst = null;
e.portInstSite = null;
return e;
}
/**
* Factory method for making a dummy RouteElement for an
* existing PortInst. This is usually use to demark the
* start and/or ends of the route, which exist before
* we start building the route.
* @param existingPortInst the already existing portInst to connect to
*/
public static RouteElementPort existingPortInst(PortInst existingPortInst, EPoint portInstSite) {
Point2D [] points = {portInstSite};
Poly poly = new Poly(points);
return existingPortInst(existingPortInst, poly);
}
/**
* Factory method for making a dummy RouteElement for an
* existing PortInst. This is usually use to demark the
* start and/or ends of the route, which exist before
* we start building the route.
* @param existingPortInst the already existing portInst to connect to
*/
public static RouteElementPort existingPortInst(PortInst existingPortInst, Poly portInstSite) {
RouteElementPort e = new RouteElementPort(RouteElement.RouteElementAction.existingPortInst, existingPortInst.getNodeInst().getParent());
NodeInst nodeInst = existingPortInst.getNodeInst();
e.np = nodeInst.getProto();
e.portProto = existingPortInst.getPortProto();
e.location = EPoint.snap(nodeInst.getTrueCenter());
e.isBisectArcPin = false;
e.newArcs = new ArrayList<RouteElementArc>();
e.setNodeSize(new Dimension2D.Double(nodeInst.getXSizeWithoutOffset(), nodeInst.getYSizeWithoutOffset()));
e.nodeInst = nodeInst;
e.portInst = existingPortInst;
e.portInstSite = portInstSite;
return e;
}
/**
* Get the NodeProto for connecting to this RouteElementPort.
* This is not the same as getNodeInst.getProto(),
* because if the action has not yet been done the NodeInst
* will not have been created and will be null.
* @return the NodeProto
*/
public NodeProto getNodeProto() { return np; }
/**
* Get the PortProto for connecting to this RouteElementPort.
* This is not the same as getPortInst().getPortProto(),
* because if the action has not yet been done the PortInst
* returned by getPortInst() may have not yet been created.
* For a deleteNode, this will return null.
* @return a PortProto of port to connect to this RouteElement.
*/
public PortProto getPortProto() { return portProto; }
/**
* Get Connecting Port on RouteElement.
* @return the PortInst, or null on error
*/
public PortInst getPortInst() { return portInst; }
/**
* Get Connecting Node on RouteElement.
* @return the NodeInst, or null on error
*/
public NodeInst getNodeInst() { return nodeInst; }
/** Returns location of newNode, existingPortInst, or deleteNode,
* or null otherwise */
public Point2D getLocation() { return location; }
/** Set true by Interactive router if pin used to bisect arc
* Router may want to remove this pin later if it places a
* connecting contact cut in the same position.
*/
public void setBisectArcPin(boolean state) { isBisectArcPin = state; }
/** see setBisectArcPin */
public boolean isBisectArcPin() { return isBisectArcPin; }
/**
* Book-keeping: Adds a newArc RouteElement to a list to keep
* track of what newArc elements use this object as an end point.
* This must be a RouteElement of type newNode or existingPortInst.
* @param re the RouteElement to add.
*/
public void addConnectingNewArc(RouteElementArc re) {
if (re.getAction() != RouteElementAction.newArc) return;
newArcs.add(re);
}
/**
* Reomve a newArc that connects to this newNode or existingPortInst.
* @param re the RouteElement to remove
*/
public void removeConnectingNewArc(RouteElementArc re) {
if (re.getAction() != RouteElementAction.newArc) return;
newArcs.remove(re);
}
/**
* Get largest arc width of newArc RouteElements attached to this
* RouteElement. If none present returns -1.
* <p>Note that these width values should have been pre-adjusted for
* the arc width offset, so these values have had the offset subtracted away.
*/
public double getWidestConnectingArc(ArcProto ap) {
double width = -1;
if (getAction() == RouteElementAction.existingPortInst) {
// find all arcs of type ap connected to this
for (Iterator<Connection> it = portInst.getConnections(); it.hasNext(); ) {
Connection conn = it.next();
ArcInst arc = conn.getArc();
if (arc.getProto() == ap) {
double newWidth = arc.getLambdaBaseWidth();
if (newWidth > width) width = newWidth;
}
}
}
if (getAction() == RouteElementAction.newNode) {
if (newArcs == null) return -1;
for (RouteElementArc re : newArcs) {
if (re.getArcProto() == ap) {
if (re.getArcBaseWidth() > width) width = re.getArcBaseWidth();
}
}
}
return width;
}
/**
* Get largest arc width of newArc RouteElements attached to this
* RouteElement. If none present returns -1.
* <p>Note that these width values should have been pre-adjusted for
* the arc width offset, so these values have had the offset subtracted away.
*/
public double getWidestConnectingArc(ArcProto ap, int arcAngle) {
double width = -1;
if (getAction() == RouteElementAction.existingPortInst) {
// find all arcs of type ap connected to this
for (Iterator<Connection> it = portInst.getConnections(); it.hasNext(); ) {
Connection conn = it.next();
ArcInst arc = conn.getArc();
if (arc.getAngle() != arcAngle) continue;
if (arc.getProto() == ap) {
double newWidth = arc.getLambdaBaseWidth();
if (newWidth > width) width = newWidth;
}
}
}
if (getAction() == RouteElementAction.newNode) {
if (newArcs == null) return -1;
for (RouteElementArc re : newArcs) {
if (re.getArcProto() == ap) {
if (re.getArcBaseWidth() > width) width = re.getArcBaseWidth();
}
}
}
return width;
}
/**
* Get the angle of any arcs connected to this RouteElement.
* If there are multiple arcs, it returns the angle of the widest
* connecting arc.
* @param ap the arc prototype
* @return the angle in centidegrees, 0 if no arcs of the specified type connected.
*/
public int getConnectingArcAngle(ArcProto ap) {
int angle = 0;
double width = -1;
if (getAction() == RouteElementAction.existingPortInst) {
// find all arcs of type ap connected to this
for (Iterator<Connection> it = portInst.getConnections(); it.hasNext(); ) {
Connection conn = it.next();
ArcInst arc = conn.getArc();
if (arc.getProto() == ap) {
double newWidth = arc.getLambdaBaseWidth();
if (newWidth > width) {
width = newWidth;
angle = arc.getAngle() % 1800;
}
}
}
}
if (getAction() == RouteElementAction.newNode) {
if (newArcs == null) return -1;
for (RouteElementArc re : newArcs) {
if (re.getArcProto() == ap) {
if (re.getArcBaseWidth() > width) {
width = re.getArcBaseWidth();
if (re.isArcVertical()) angle = 900;
if (re.isArcHorizontal()) angle = 0;
}
}
}
}
return angle;
}
/**
* Get an iterator over any newArc RouteElements connected to this
* newNode RouteElement. Returns an iterator over an empty list
* if no new arcs.
*/
public Iterator<RouteElement> getNewArcs() {
ArrayList<RouteElement> list = new ArrayList<RouteElement>();
list.addAll(newArcs);
return list.iterator();
}
/**
* Get the size of a newNode, or the NodeInst an existingPortInst
* is attached to.
* @return the width,height of the node, or (-1, -1) if not a node
*/
public Dimension2D.Double getNodeSize() {
return new Dimension2D.Double(width, height);
}
/**
* Set the size of a newNode. Does not make it smaller
* than the default size if this is a PrimitiveNode.
* Does nothing for other RouteElements.
* @param size the new size
*/
public void setNodeSize(Dimension2D size) {
SizeOffset so = np.getProtoSizeOffset();
double widthoffset = so.getLowXOffset() + so.getHighXOffset();
double heightoffset = so.getLowYOffset() + so.getHighYOffset();
double defWidth = np.getDefWidth() - widthoffset; // this is width we see on the screen
double defHeight = np.getDefHeight() - heightoffset; // this is height we see on the screen
if (size.getWidth() > defWidth) width = size.getWidth(); else width = defWidth;
if (size.getHeight() > defHeight) height = size.getHeight(); else height = defHeight;
}
/**
* Get a polygon that defines the port dimensions.
* May return null.
*/
public Poly getConnectingSite() { return portInstSite; }
/**
* Perform the action specified by RouteElementAction <i>action</i>.
* Note that this method performs database editing, and should only
* be called from within a Job.
* @return the object created, or null if deleted or nothing done.
*/
public ElectricObject doAction() {
EDatabase.serverDatabase().checkChanging();
if (isDone()) return null;
ElectricObject returnObj = null;
if (getAction() == RouteElementAction.newNode) {
// create new Node
SizeOffset so = np.getProtoSizeOffset();
double widthso = width + so.getLowXOffset() + so.getHighXOffset();
double heightso = height + so.getLowYOffset() + so.getHighYOffset();
nodeInst = NodeInst.makeInstance(np, location, widthso, heightso, getCell());
if (nodeInst == null) return null;
portInst = nodeInst.findPortInstFromProto(portProto);
returnObj = nodeInst;
}
if (getAction() == RouteElementAction.deleteNode) {
// delete existing arc
nodeInst.kill();
}
setDone();
return returnObj;
}
/**
* Adds RouteElement to highlights
*/
public void addHighlightArea(Highlighter highlighter) {
if (!isShowHighlight()) return;
if (getAction() == RouteElementAction.newNode) {
// create box around new Node
Rectangle2D bounds = new Rectangle2D.Double(location.getX()-0.5*width,
location.getY()-0.5*height, width, height);
highlighter.addArea(bounds, getCell());
}
if (getAction() == RouteElementAction.existingPortInst) {
highlighter.addElectricObject(portInst, getCell());
}
}
/** Return string decribing the RouteElement */
public String toString() {
if (getAction() == RouteElementAction.newNode) {
return "RouteElementPort newNode "+np+" size "+width+","+height+" at "+location;
}
else if (getAction() == RouteElementAction.deleteNode) {
return "RouteElementPort deleteNode "+nodeInst+" at ("+nodeInst.getAnchorCenterX()+","+nodeInst.getAnchorCenterY()+")";
}
else if (getAction() == RouteElementAction.existingPortInst) {
return "RouteElementPort existingPortInst "+portInst;
}
return "RouteElement bad action";
}
/**
* Save the state of the <tt>RouteElementPort</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The numnber of points in portInstSite polygon is emitted (int),
* followed by all of its coordianates ( as pair of doubles ) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
s.defaultWriteObject();
if (portInstSite != null) {
Point2D[] points = portInstSite.getPoints();
s.writeInt(points.length);
for (int i = 0; i < points.length; i++) {
s.writeDouble(points[i].getX());
s.writeDouble(points[i].getY());
}
} else {
s.writeInt(-1);
}
}
/**
* Reconstitute the <tt>RouteElementPort</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
int len = s.readInt();
if (len >= 0) {
Point2D[] points = new Point2D[len];
for (int i = 0; i < len; i++) {
double x = s.readDouble();
double y = s.readDouble();
points[i] = new Point2D.Double(x, y);
}
}
}
}