/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: PortOriginal.java
*
* Copyright (c) 2003, 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.prototype;
import com.sun.electric.database.ImmutableExport;
import com.sun.electric.database.ImmutableNodeInst;
import com.sun.electric.database.hierarchy.Cell;
import com.sun.electric.database.hierarchy.EDatabase;
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.topology.PortInst;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.util.math.Orientation;
import java.awt.geom.AffineTransform;
/**
* The PortOriginal helper class descends the hierarchy from an Export to the bottommost PrimitivePort
* on a primitive NodeInst.
* <P>
* Many parts of the system need to know what is "under" an Export.
* For example, assume that cell BOT has a contact node that is exported.
* Further assume that cell MID has an instance of cell BOT, and that the
* port on BOT is further exported.
* Finally, assume that cell TOP has an instance of cell MID.
* To find out information about the port on MID, it is necessary to descend
* the hierarchy into MID and into BOT.
* <P>
* This class takes a PortInst or a NodeInst/PortProto pair (such as the port on MID in cell TOP).
* It then provides these pieces of information:
* <UL>
* <LI>The bottommost PortInst (in this case, the port on the contact node in cell BOT).</LI>
* <LI>The bottommost NodeInst (in this case, the contact node in cell BOT).</LI>
* <LI>The bottommost PortProto (in this case, the port on the contact node in cell BOT).</LI>
* <LI>The transformation to the top. In this case, a transformation that:
* (1) accounts for rotation of the contact node;
* (2) accounts for translation between cell BOT and cell MID;
* (3) accounts for rotation on the instance of cell BOT in cell MID;
* (4) accounts for translation between cell MID and cell TOP; and
* (5) accounts for rotation of the instance of cell MID in cell TOP.
* Thus, it transforms from coordinates on the contact node to the coordinate space of cell TOP.</LI>
* <LI>The apparent angle of the lowest node when viewed from the top.
* In this case, it is a combination of the contact, BOT cell instance, and MID cell instance rotations.</LI>
*/
public class PortOriginal {
private AffineTransform subrot;
private Orientation orient;
private Cell bottomCell;
private ImmutableNodeInst bottomNode;
private PortInst bottomPort;
private NodeInst bottomNi;
private PrimitivePort bottomPp;
/**
* Constructor takes a PortInst and traverses it down to the bottom of the hierarchy.
* @param startPort the initial PortInst.
*/
public PortOriginal(PortInst startPort) {
bottomPort = startPort;
// subrot = bottomNi.rotateOut();
traverse(startPort.getNodeInst(), startPort.getPortProto(), null);
}
/**
* Constructor takes a PortInst and traverses it down to the bottom of the hierarchy.
* Also takes a transformation matrix to include in the final computation.
* @param startPort the initial PortInst.
* @param pre the transformation matrix to add to the final transformation.
*/
public PortOriginal(PortInst startPort, AffineTransform pre) {
bottomPort = startPort;
traverse(startPort.getNodeInst(), startPort.getPortProto(), pre);
}
/**
* Constructor takes a NodeInst/PortProto combination and traverses it down to the bottom of the hierarchy.
* @param ni the initial NodeInst.
* @param pp the initial PortProto.
*/
public PortOriginal(NodeInst ni, PortProto pp) {
traverse(ni, pp, null);
}
private void traverse(NodeInst ni, PortProto pp, AffineTransform pre) {
bottomCell = ni.getParent();
bottomNode = ni.getD();
bottomNi = ni;
EDatabase database;
if (bottomCell != null) {
database = bottomCell.getDatabase();
} else if (ni.isCellInstance()) {
database = ((Cell)ni.getProto()).getDatabase();
} else {
database = null;
}
orient = bottomNode.orient;
subrot = orient.rotateAbout(bottomNode.anchor.getLambdaX(), bottomNode.anchor.getLambdaY(), 0, 0);
if (pre != null) {
subrot.preConcatenate(pre);
}
while (bottomNode.protoId instanceof CellId) {
bottomCell = database.getCell((CellId)bottomNode.protoId);
ImmutableExport bottomExport = ((Export)pp).getD();
bottomNode = bottomCell.backupUnsafe().getMemoization().getNodeById(bottomExport.originalNodeId);
bottomPort = null;
bottomNi = null;
// bottomPort = bottomExport.getOriginalPort();
// bottomNi = bottomPort.getNodeInst();
pp = bottomExport.originalPortId.inDatabase(database);
orient = orient.concatenate(bottomNode.orient);
AffineTransform transform = bottomNode.orient.rotateAbout(bottomNode.anchor.getLambdaX(), bottomNode.anchor.getLambdaY(), 0, 0);
subrot.concatenate(transform);
}
bottomPp = (PrimitivePort)pp;
subrot.translate(-bottomNode.anchor.getLambdaX(), -bottomNode.anchor.getLambdaY());
}
/**
* Method to return the bottommost NodeInst (a primitive)
* from the initial port information given to the constructor.
* @return the NodeInst at the bottom of the hierarchy (a primitive).
*/
public NodeInst getBottomNodeInst() {
if (bottomNi == null) {
bottomPort = bottomCell.getPortInst(bottomNode.nodeId, bottomPp.getId());
bottomNi = bottomPort.getNodeInst();
}
return bottomNi;
}
/**
* Method to return the bottommost Cell
* from the initial port information given to the constructor.
* @return the Cell at the bottom of the hierarchy.
*/
public Cell getBottomCell() {
return bottomCell;
}
/**
* Method to return the bottommost ImmutableNodeInst (a primitive)
* from the initial port information given to the constructor.
* @return the ImmutableNodeInst at the bottom of the hierarchy (a primitive).
*/
public ImmutableNodeInst getBottomImmutableNodeInst() {
return bottomNode;
}
/**
* Method to return the bottommost PortProto (a PrimitivePort)
* from the initial port information given to the constructor.
* @return the PortProto at the bottom of the hierarchy (a PrimitivePort).
*/
public PrimitivePort getBottomPortProto() {
return bottomPp;
}
/**
* Method to return the bottommost PortInst (on a primitive NodeInst)
* from the initial port information given to the constructor.
* @return the PortInst at the bottom of the hierarchy (on a primitive NodeInst).
*/
public PortInst getBottomPort() {
if (bottomPort == null) {
getBottomNodeInst();
bottomPort = bottomNi != null ? bottomNi.findPortInstFromProto(bottomPp) : null;
}
return bottomPort;
}
/**
* Method to return the transformation matrix from the bottommost NodeInst
* to the Cell containing the topmost PortInst.
* The transformation includes any rotation on the node with the topmost PortInst.
* @return the the transformation matrix from the bottommost NodeInst
* to the Cell containing the topmost PortInst.
*/
public AffineTransform getTransformToTop() {
return subrot;
}
/**
* Method to return the apparent orientation of the lowest node when viewed from the top.
* The angle can be used to replicate a node at the top level that is in the same
* orientation of the node at the bottom.
* @return the apparent orientation of the lowest node when viewed from the top.
*/
public Orientation getOrientToTop() {
return orient;
}
}