com.sun.electric.database.topology.ArcInst Maven / Gradle / Ivy
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: ArcInst.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.topology;
import com.sun.electric.database.EObjectInputStream;
import com.sun.electric.database.EObjectOutputStream;
import com.sun.electric.database.EditingPreferences;
import com.sun.electric.database.ImmutableArcInst;
import com.sun.electric.database.constraint.Constraints;
import com.sun.electric.database.geometry.EPoint;
import com.sun.electric.database.geometry.ERectangle;
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.database.id.CellId;
import com.sun.electric.database.id.PrimitivePortId;
import com.sun.electric.database.prototype.PortProto;
import com.sun.electric.database.text.Name;
import com.sun.electric.database.variable.DisplayedText;
import com.sun.electric.database.variable.EditWindow0;
import com.sun.electric.database.variable.TextDescriptor;
import com.sun.electric.database.variable.Variable;
import com.sun.electric.technology.ArcProto;
import com.sun.electric.technology.BoundsBuilder;
import com.sun.electric.technology.PrimitivePort;
import com.sun.electric.technology.TechPool;
import com.sun.electric.technology.Technology;
import com.sun.electric.technology.technologies.Schematics;
import com.sun.electric.tool.user.ErrorLogger;
import com.sun.electric.util.math.DBMath;
import com.sun.electric.util.math.FixpTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* An ArcInst is an instance of an ArcProto (a wire type)
* An ArcInst points to its prototype, the Cell on which it has been
* instantiated, and the connection at either end of the wire.
* The geometry of the wire (width and length) is captured in the
* bounds of the Geometric portion of this object.
*
* ArcInst objects have properties that constrain them. Here is the notion of "Fixed angle":
*
*
*
* Here is the notion of rigid arcs:
*
*
*
* Here is the notion of slidable arcs:
*
*
*
* Constraints propagate hierarchically:
*
*
*/
public class ArcInst extends Geometric implements Comparable {
/** the default angle of an arc (used to be 0) */
public static final int DEFAULTANGLE = -1;
/** empty array of ArcInsts. */
public static final ArcInst[] NULL_ARRAY = {};
/** The index of the tail of this ArcInst. */
public static final int TAILEND = ImmutableArcInst.TAILEND;
/** The index of the head of this ArcInst. */
public static final int HEADEND = ImmutableArcInst.HEADEND;
/** Key of the obsolete variable holding arc name.*/
public static final Variable.Key ARC_NAME = Variable.newKey("ARC_name");
/** Minimal distance of arc end to port polygon. */
static final double MINPORTDISTANCE = DBMath.getEpsilon() * 0.71; // sqrt(0.5)
// -------------------------- private data ----------------------------------
/** Owner of this ArcInst. */
private final Topology topology;
/** persistent data of this ArcInst. */
ImmutableArcInst d;
/** bounds after transformation. */
private ERectangle visBounds;
/** PortInst on tail end of this arc instance */ /*package*/
final PortInst tailPortInst;
/** PortInst on head end of this arc instance */ /*package*/
final PortInst headPortInst;
// /** 0-based index of this ArcInst in Cell. */ private int arcIndex = -1; //scanline
/**
* Private constructor of ArcInst.
* @param topology the Topology of the ArcInst.
* @param d persistent data of ArcInst.
* @param headPort the head end PortInst.
* @param tailPort the tail end PortInst.
*/
public ArcInst(Topology topology, ImmutableArcInst d, PortInst headPort, PortInst tailPort) {
this.topology = topology;
// initialize this object
assert topology == headPort.getNodeInst().topology;
assert topology == tailPort.getNodeInst().topology;
assert d.headNodeId == headPort.getNodeInst().getD().nodeId;
assert d.tailNodeId == tailPort.getNodeInst().getD().nodeId;
assert d.headPortId == headPort.getPortProto().getId();
assert d.tailPortId == tailPort.getPortProto().getId();
this.d = d;
// create node/arc connections and place them properly
tailPortInst = tailPort;
// tailEnd = new TailConnection(this);
headPortInst = headPort;
// headEnd = new HeadConnection(this);
}
private Object writeReplace() {
return new ArcInstKey(this);
}
private static class ArcInstKey extends EObjectInputStream.Key {
/**
* Constructor for ArcInstKey. Although it is not invoked locally, it is used by reflection.
*/
public ArcInstKey() {
}
private ArcInstKey(ArcInst ai) {
super(ai);
}
@Override
public void writeExternal(EObjectOutputStream out, ArcInst ai) throws IOException {
if (ai.getDatabase() != out.getDatabase() || !ai.isLinked()) {
throw new NotSerializableException(ai + " not linked");
}
out.writeObject(ai.topology.cell);
out.writeInt(ai.getArcId());
}
@Override
public ArcInst readExternal(EObjectInputStream in) throws IOException, ClassNotFoundException {
Cell cell = (Cell) in.readObject();
int arcId = in.readInt();
ArcInst ai = cell.getArcById(arcId);
if (ai == null) {
throw new InvalidObjectException("ArcInst from " + cell);
}
return ai;
}
}
/**
* Comparator class for sorting ArcInst by their length.
*/
public static class ArcsByLength implements Comparator {
/**
* Method to sort ArcInst by their length.
*/
public int compare(ArcInst a1, ArcInst a2) {
double len1 = a1.getHeadLocation().distance(a1.getTailLocation());
double len2 = a2.getHeadLocation().distance(a2.getTailLocation());
if (len1 == len2) {
return 0;
}
if (len1 < len2) {
return 1;
}
return -1;
}
}
/****************************** CREATE, DELETE, MODIFY ******************************/
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst makeInstance(ArcProto type, PortInst head, PortInst tail) {
return makeInstance(type, EditingPreferences.getThreadEditingPreferences(), head, tail);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst makeInstance(ArcProto type, EditingPreferences ep,
PortInst head, PortInst tail) {
ImmutableArcInst a = type.getDefaultInst(ep);
return newInstanceBase(type, ep, type.getDefaultLambdaBaseWidth(ep), head, tail, null, null, null, DEFAULTANGLE, a.flags);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst makeInstanceBase(ArcProto type, double baseWidth, PortInst head, PortInst tail) {
return makeInstanceBase(type, EditingPreferences.getThreadEditingPreferences(), baseWidth, head, tail);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst makeInstanceBase(ArcProto type, EditingPreferences ep,
double baseWidth, PortInst head, PortInst tail) {
ImmutableArcInst a = type.getDefaultInst(ep);
return newInstanceBase(type, ep, baseWidth, head, tail, null, null, null, DEFAULTANGLE, a.flags);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst makeInstance(ArcProto type, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name) {
return makeInstance(type, EditingPreferences.getThreadEditingPreferences(), head, tail, headPt, tailPt, name);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst makeInstance(ArcProto type, EditingPreferences ep,
PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name) {
ImmutableArcInst a = type.getDefaultInst(ep);
return newInstanceBase(type, ep, type.getDefaultLambdaBaseWidth(ep), head, tail, headPt, tailPt, name, DEFAULTANGLE, a.flags);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst makeInstanceBase(ArcProto type, double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name) {
return makeInstanceBase(type, EditingPreferences.getThreadEditingPreferences(), baseWidth, head, tail, headPt, tailPt, name);
}
/**
* Method to create a new ArcInst with appropriate defaults, connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst makeInstanceBase(ArcProto type, EditingPreferences ep,
double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name) {
ImmutableArcInst a = type.getDefaultInst(ep);
return newInstanceBase(type, ep, baseWidth, head, tail, headPt, tailPt, name, DEFAULTANGLE, a.flags);
}
/**
* Method to create a new ArcInst connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst newInstanceBase(ArcProto type, double baseWidth, PortInst head, PortInst tail) {
return newInstanceBase(type, EditingPreferences.getThreadEditingPreferences(), baseWidth, head, tail);
}
/**
* Method to create a new ArcInst connecting two PortInsts.
* Since no coordinates are given, the ArcInst connects to the center of the PortInsts.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst newInstanceBase(ArcProto type, EditingPreferences ep,
double baseWidth, PortInst head, PortInst tail) {
return newInstanceBase(type, ep, baseWidth, head, tail, null, null, null, DEFAULTANGLE, ImmutableArcInst.DEFAULT_FLAGS);
}
/**
* Method to create a new ArcInst connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @param defAngle default angle in case port points coincide
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst newInstanceBase(ArcProto type, double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name, int defAngle) {
return newInstanceBase(type, EditingPreferences.getThreadEditingPreferences(), baseWidth, head, tail, headPt, tailPt, name, defAngle);
}
/**
* Method to create a new ArcInst connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @param defAngle default angle in case port points coincide
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst newInstanceBase(ArcProto type, EditingPreferences ep,
double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name, int defAngle) {
return newInstanceBase(type, ep, baseWidth, head, tail, headPt, tailPt, name, defAngle, ImmutableArcInst.DEFAULT_FLAGS);
}
/**
* Method to create a new ArcInst connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @param defAngle default angle in case port points coincide
* @param flags flags of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public static ArcInst newInstanceBase(ArcProto type, double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name, int defAngle, int flags) {
return newInstanceBase(type, EditingPreferences.getThreadEditingPreferences(), baseWidth, head, tail, headPt, tailPt, name, defAngle, flags);
}
/**
* Method to create a new ArcInst connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* @param type the prototype of the new ArcInst.
* @param ep EditingPreferences with default sizes and text descriptors.
* @param baseWidth the base width of the new ArcInst. The width must be > 0.
* @param head the head end PortInst.
* @param tail the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param name the name of the new ArcInst
* @param defAngle default angle in case port points coincide
* @param flags flags of the new ArcInst
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst newInstanceBase(ArcProto type, EditingPreferences ep,
double baseWidth, PortInst head, PortInst tail,
Point2D headPt, Point2D tailPt, String name, int defAngle, int flags) {
// if (type.isNotUsed())
// {
//// System.out.println("Cannot create arc instance of " + type + " because prototype is unused");
//// return null;
// }
long gridExtendOverMin = DBMath.lambdaToGrid(0.5 * baseWidth) - type.getBaseExtend().getGrid();
// if (gridFullWidth < type.getMaxLayerGridOffset())
// gridFullWidth = type.getDefaultGridFullWidth();
// if points are null, create them as would newInstance
EPoint headP;
if (headPt == null) {
headP = head.getCenter();
} else {
headP = EPoint.snap(headPt);
}
EPoint tailP;
if (tailPt == null) {
tailP = tail.getCenter();
} else {
tailP = EPoint.snap(tailPt);
}
// make sure points are valid
Cell parent = head.getNodeInst().topology.cell;
Poly headPoly = head.getPoly();
if (!stillInPoly(headP, headPoly)) {
System.out.println("Error in " + parent + ": head of " + type.getName()
+ " arc at (" + headP.getX() + "," + headP.getY() + ") does not fit in "
+ head + " which is centered at (" + headPoly.getCenterX() + "," + headPoly.getCenterY() + ")");
return null;
}
Poly tailPoly = tail.getPoly();
if (!stillInPoly(tailP, tailPoly)) {
System.out.println("Error in " + parent + ": tail of " + type.getName()
+ " arc at (" + tailP.getX() + "," + tailP.getY() + ") does not fit in "
+ tail + " which is centered at (" + tailPoly.getCenterX() + "," + tailPoly.getCenterY() + ")");
return null;
}
TextDescriptor nameDescriptor = ep.getArcTextDescriptor();
Name nameKey = name != null ? Name.findName(name) : null;
if (nameKey != null && !nameKey.isTempname()) {
// adjust the name descriptor for "smart" text placement
long gridBaseWidth = 2 * (gridExtendOverMin + type.getBaseExtend().getGrid());
nameDescriptor = getSmartTextDescriptor(defAngle, DBMath.gridToLambda(gridBaseWidth), nameDescriptor, ep);
}
return newInstanceNoCheck(parent, type, name, nameDescriptor, head, tail, headP, tailP, gridExtendOverMin, defAngle, flags);
}
/**
* Method to create a new ArcInst connecting two PortInsts at specified locations.
* This is more general than the version that does not take coordinates.
* WARNING: this method does not check endpoint coordinates so make sure they are correct
* to prevent database corruption
* @param parent the parent Cell of this ArcInst
* @param protoType the ArcProto of this ArcInst.
* @param name the name of this ArcInst
* @param nameDescriptor text descriptor of name of this ArcInst
* @param headPort the head end PortInst.
* @param tailPort the tail end PortInst.
* @param headPt the coordinate of the head end PortInst.
* @param tailPt the coordinate of the tail end PortInst.
* @param gridExtendOverMin the extend of this ArcInst over minimal-width arc of this type in grid units.
* @param angle angle in tenth-degrees.
* @param flags flag bits.
* @return the newly created ArcInst, or null if there is an error.
*/
public static ArcInst newInstanceNoCheck(Cell parent, ArcProto protoType, String name, TextDescriptor nameDescriptor,
PortInst headPort, PortInst tailPort, EPoint headPt, EPoint tailPt, long gridExtendOverMin, int angle, int flags) {
parent.checkChanging();
Topology topology = parent.getTopology();
// make sure fields are valid
if (protoType == null || headPort == null || tailPort == null || !headPort.isLinked() || !tailPort.isLinked()) {
return null;
}
if (headPt == null || tailPt == null) {
return null;
}
if (topology != headPort.getNodeInst().topology || topology != tailPort.getNodeInst().topology) {
System.out.println("ArcProto.newInst: the 2 PortInsts are in different Cells!");
System.out.println("Cell " + parent.getName());
System.out.println("Head " + headPort.getNodeInst().topology.cell.getName());
System.out.println("Tail " + tailPort.getNodeInst().topology.cell.getName());
return null;
}
// make sure the arc can connect to these ports
PortProto headProto = headPort.getPortProto();
PrimitivePort headPrimPort = headProto.getBasePort();
if (!headPrimPort.connectsTo(protoType)) {
System.out.println("Cannot create " + protoType + " from (" + headPt.getX() + "," + headPt.getY()
+ ") to (" + tailPt.getX() + "," + tailPt.getY() + ") in " + parent
+ " because the 'from' port (" + headProto.getName() + " on node "
+ headPort.getNodeInst().describe(false) + ") does not connect to " + protoType);
return null;
}
PortProto tailProto = tailPort.getPortProto();
PrimitivePort tailPrimPort = tailProto.getBasePort();
if (!tailPrimPort.connectsTo(protoType)) {
System.out.println("Cannot create " + protoType + " from (" + headPt.getX() + "," + headPt.getY()
+ ") to (" + tailPt.getX() + "," + tailPt.getY() + ") in " + parent
+ " because the 'to' port (" + tailProto.getName() + " on node "
+ tailPort.getNodeInst().describe(false) + ") does not connect to " + protoType);
return null;
}
if (nameDescriptor == null) {
throw new NullPointerException();
}
Name nameKey = name != null ? Name.findName(name) : null;
if (nameKey == null || checkNameKey(nameKey, topology)
|| nameKey.isBus() && protoType != Schematics.tech().bus_arc) {
nameKey = topology.getArcAutoname();
// } else
// {
// // adjust the name descriptor for "smart" text placement
// long gridBaseWidth = 2*(gridExtendOverMin + protoType.getGridBaseExtend());
// nameDescriptor = getSmartTextDescriptor(angle, DBMath.gridToLambda(gridBaseWidth), nameDescriptor);
}
TechPool techPool = parent.getTechPool();
if (!(tailProto.getId() instanceof PrimitivePortId && techPool.getPrimitivePort((PrimitivePortId) tailProto.getId()).isNegatable())) {
flags = ImmutableArcInst.TAIL_NEGATED.set(flags, false);
}
if (!(headProto.getId() instanceof PrimitivePortId && techPool.getPrimitivePort((PrimitivePortId) headProto.getId()).isNegatable())) {
flags = ImmutableArcInst.HEAD_NEGATED.set(flags, false);
}
if (protoType.getTechnology().isNoNegatedArcs()) {
flags = ImmutableArcInst.TAIL_NEGATED.set(flags, false);
flags = ImmutableArcInst.HEAD_NEGATED.set(flags, false);
}
CellId parentId = parent.getId();
// search for spare arcId
int arcId;
do {
arcId = parentId.newArcId();
} while (parent.getArcById(arcId) != null);
ImmutableArcInst d = ImmutableArcInst.newInstance(arcId, protoType.getId(), nameKey, nameDescriptor,
tailPort.getNodeInst().getD().nodeId, tailProto.getId(), tailPt,
headPort.getNodeInst().getD().nodeId, headProto.getId(), headPt,
gridExtendOverMin, angle, flags);
ArcInst ai = new ArcInst(topology, d, headPort, tailPort);
// attach this arc to the two nodes it connects
headPort.getNodeInst().redoGeometric();
tailPort.getNodeInst().redoGeometric();
// add this arc to the cell
topology.addArc(ai);
// handle change control, constraint, and broadcast
Constraints.getCurrent().newObject(ai);
return ai;
}
/**
* Method to delete this ArcInst.
*/
public void kill() {
if (!isLinked()) {
System.out.println("ArcInst already killed");
return;
}
checkChanging();
// remove this arc from the two nodes it connects
headPortInst.getNodeInst().redoGeometric();
tailPortInst.getNodeInst().redoGeometric();
// remove this arc from the cell
topology.removeArc(this);
// handle change control, constraint, and broadcast
Constraints.getCurrent().killObject(this);
}
/**
* Method to change the width and end locations of this ArcInst.
* @param dHeadX the change to the X coordinate of the head of this ArcInst.
* @param dHeadY the change to the Y coordinate of the head of this ArcInst.
* @param dTailX the change to the X coordinate of the tail of this ArcInst.
* @param dTailY the change to the Y coordinate of the tail of this ArcInst.
*/
public void modify(double dHeadX, double dHeadY, double dTailX, double dTailY) {
// save old arc state
ImmutableArcInst oldD = d;
// change the arc
EPoint tail = d.tailLocation;
if (dTailX != 0 || dTailY != 0) {
tail = new EPoint(tail.getX() + dTailX, tail.getY() + dTailY);
}
EPoint head = d.headLocation;
if (dHeadX != 0 || dHeadY != 0) {
head = new EPoint(head.getX() + dHeadX, head.getY() + dHeadY);
}
lowLevelModify(d.withLocations(tail, head));
// track the change
Constraints.getCurrent().modifyArcInst(this, oldD);
}
/**
* Method to change the width this ArcInst.
* @param lambdaBaseWidth new base width of the ArcInst in lambda units.
*/
public void setLambdaBaseWidth(double lambdaBaseWidth) {
setGridBaseWidth(DBMath.lambdaToSizeGrid(lambdaBaseWidth));
}
/**
* Method to change the width this ArcInst.
* @param gridBaseWidth new base width of the ArcInst in lambda units.
*/
public void setGridBaseWidth(long gridBaseWidth) {
if (gridBaseWidth == getGridBaseWidth()) {
return;
}
// save old arc state
ImmutableArcInst oldD = d;
// change the arc
lowLevelModify(d.withGridExtendOverMin(gridBaseWidth / 2 - getProto().getBaseExtend().getGrid()));
// track the change
Constraints.getCurrent().modifyArcInst(this, oldD);
}
/**
* Method to replace this ArcInst with one of another type.
* @param ap the new type of arc.
* @param ep EditingPreferences
* @return the new ArcInst (null on error).
*/
public ArcInst replace(ArcProto ap, EditingPreferences ep) {
// check for connection allowance
if (!headPortInst.getPortProto().connectsTo(ap) || !tailPortInst.getPortProto().connectsTo(ap)) {
System.out.println("Cannot replace " + this + " with one of type " + ap.getName()
+ " because the nodes cannot connect to it");
return null;
}
// first create the new nodeinst in place
ArcInst newar = ArcInst.newInstanceBase(ap, ep, getLambdaBaseWidth(), headPortInst, tailPortInst, d.headLocation, d.tailLocation, null, ArcInst.DEFAULTANGLE);
if (newar == null) {
System.out.println("Cannot replace " + this + " with one of type " + ap.getName()
+ " because the new arc failed to create");
return null;
}
// copy all variables on the arcinst
newar.copyPropertiesFrom(this);
// now delete the original nodeinst
kill();
newar.setName(getName(), ep);
return newar;
}
/****************************** LOW-LEVEL IMPLEMENTATION ******************************/
/**
* Returns persistent data of this ArcInst.
* @return persistent data of this ArcInst.
*/
@Override
public ImmutableArcInst getD() {
return d;
}
/**
* Modifies persistent data of this ArcInst.
* @param newD new persistent data.
* @param notify true to notify Undo system.
* @return true if persistent data was modified.
*/
public boolean setD(ImmutableArcInst newD, boolean notify) {
checkChanging();
ImmutableArcInst oldD = d;
if (newD == oldD) {
return false;
}
topology.cell.setTopologyModified();
d = newD;
if (notify) {
Constraints.getCurrent().modifyArcInst(this, oldD);
}
return true;
}
public void setDInUndo(ImmutableArcInst newD) {
checkUndoing();
d = newD;
}
/**
* Method to add a Variable on this ArcInst.
* It may add repaired copy of this Variable in some cases.
* @param var Variable to add.
*/
public void addVar(Variable var) {
if (setD(d.withVariable(var), true)) {
// check for side-effects of the change
checkPossibleVariableEffects(var.getKey());
}
}
/**
* Method to handle special case side-effects of setting variables on this NodeInst.
* Overrides the general method on ElectricObject.
* Currently it handles changes to the number-of-degrees on a circle node.
* @param key the Variable key that has changed on this NodeInst.
*/
public void checkPossibleVariableEffects(Variable.Key key) {
if (key == ImmutableArcInst.ARC_RADIUS) {
lowLevelModify(d);
}
}
/**
* Method to delete a Variable from this ArcInst.
* @param key the key of the Variable to delete.
*/
@Override
public void delVar(Variable.Key key) {
setD(d.withoutVariable(key), true);
}
/**
* Method to copy all variables from another ArcInst to this ArcInst.
* @param other the other ArcInst from which to copy Variables.
*/
public void copyVarsFrom(ArcInst other) {
checkChanging();
for (Iterator it = other.getVariables(); it.hasNext();) {
addVar(it.next());
}
}
/**
* Low-level method to change the width and end locations of this ArcInst.
* New persistent data may differ from old one only by width and end locations
* @param d the new persistent data of this ArcInst.
*/
public void lowLevelModify(ImmutableArcInst d) {
// first remove from the R-Tree structure
boolean renamed = this.d.name != d.name;
if (renamed) {
topology.removeArc(this);
}
// now make the change
setD(d, false);
if (renamed) {
topology.addArc(this);
}
topology.setArcsDirty();
// update end shrinkage information ?????????????????????
// headPortInst.getNodeInst().updateShrinkage();
// tailPortInst.getNodeInst().updateShrinkage();
}
/****************************** GRAPHICS ******************************/
/**
* Method to return the full width of this ArcInst in grid units.
* @return the full width of this ArcInst in grid units.
*/
public long getGridFullWidth() {
return 2 * (d.getGridExtendOverMin() + getProto().getMaxLayerExtend().getGrid());
}
/**
* Method to return the base width of this ArcInst in lambda units.
* @return the base width of this ArcInst in lambda units.
*/
public double getLambdaBaseWidth() {
return DBMath.gridToLambda(getGridBaseWidth());
}
/**
* Method to return the base width of this ArcInst in grid units.
* @return the base width of this ArcInst in grid units.
*/
public long getGridBaseWidth() {
return 2 * (d.getGridExtendOverMin() + getProto().getBaseExtend().getGrid());
}
/**
* Method to return the length of this ArcInst in lambda units.
* @return the length of this ArcInst in lambda units.
*/
public double getLambdaLength() {
return d.getLambdaLength();
}
/**
* Method to return the length of this ArcInst in grid units.
* @return the length of this ArcInst in grid units.
*/
public double getGridLength() {
return d.getGridLength();
}
/**
* Returns true if length of this ArcInst is zero.
* @return true if length of this ArcInst is zero.
*/
public boolean isZeroLength() {
return d.isZeroLength();
}
/**
* Method to return the rotation angle of this ArcInst.
* This is an angle of direction from tailLocation to headLocation.
* @return the rotation angle of this ArcInst (in tenth-degrees).
* Undefined-angles return -1;
*/
public int getAngle() {
return d.getAngle();
}
/**
* Method to return the rotation angle of this ArcInst.
* This is an angle of direction from tailLocation to headLocation.
* @return the rotation angle of this ArcInst (in tenth-degrees).
* Undefined-angles return 0;
*/
public int getDefinedAngle() {
int angle = d.getAngle();
if (angle == -1) {
return 0;
}
return angle;
}
/**
* Method to set the rotation angle of this ArcInst.
* @param angle the rotation angle of this ArcInst (in tenth-degrees).
* In general, you should not call this method because the
* constructors and modification methods update this correctly.
* If, however, you have a zero-length arc and want to explicitly set
* its angle, then use this method.
*/
public void setAngle(int angle) {
checkChanging();
ImmutableArcInst oldD = d;
lowLevelModify(d.withAngle(angle));
assert topology != null;
Constraints.getCurrent().modifyArcInst(this, oldD);
}
/**
* Returns the polygons that describe this ArcInst.
* @param polyBuilder Poly builder.
* @return an iterator on Poly objects that describes this ArcInst graphically.
* These Polys include displayable variables on the ArcInst.
*/
@Override
public Iterator getShape(Poly.Builder polyBuilder) {
return polyBuilder.getShape(this);
}
/**
* Method to return the bounds of this ArcInst.
* TODO: dangerous to give a pointer to our internal field; should make a copy of visBounds
* @return the bounds of this ArcInst.
*/
@Override
public ERectangle getBounds() {
if (!topology.validArcBounds) {
topology.computeArcBounds();
}
assert visBounds != null;
return visBounds;
}
void computeBounds(BoundsBuilder b, long[] gridCoords) {
if (b.genBoundsEasy(d, gridCoords)) {
long gridMinX = gridCoords[0];
long gridMinY = gridCoords[1];
long gridMaxX = gridCoords[2];
long gridMaxY = gridCoords[3];
if (visBounds != null &&
gridMinX == visBounds.getGridMinX() && gridMinY == visBounds.getGridMinY() &&
gridMaxX == visBounds.getGridMaxX() && gridMaxY == visBounds.getGridMaxY()) {
return;
}
visBounds = ERectangle.fromGrid(gridMinX, gridMinY, gridMaxX - gridMinX, gridMaxY - gridMinY);
} else {
b.clear();
b.genShapeOfArc(d);
visBounds = b.makeBounds(null, visBounds);
}
}
/**
* Method to create a Poly object that describes an ArcInst in lambda units.
* The ArcInst is described by its width and style.
* @param gridWidth the width of the Poly in grid units.
* @param style the style of the ArcInst.
* @return a Poly that describes the ArcInst in lambda units.
*/
public Poly makeLambdaPoly(long gridWidth, Poly.Type style) {
Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
polyBuilder.setup(topology.cell);
return polyBuilder.makePoly(getD(), gridWidth, style);
}
// /**
// * Method to create a Poly object that describes an ArcInst in grid units.
// * The ArcInst is described by its width and style.
// * @param gridWidth the width of the Poly in grid units.
// * @param style the style of the ArcInst.
// * @return a Poly that describes the ArcInst in grid units.
// */
// public Poly makeGridPoly(long gridWidth, Poly.Type style) {
// CellBackup.Memoization m = parent != null ? parent.getMemoization() : null;
// return getD().makeGridPoly(m, gridWidth, style);
// }
/**
* Method to fill polygon "poly" with the outline in lambda units of the curved arc in
* this ArcInst whose width in grid units is "gridWidth". The style of the polygon is set to "style".
* If there is no curvature information in the arc, the routine returns null,
* otherwise it returns the curved polygon.
*/
public Poly curvedArcLambdaOutline(Poly.Type style, long gridWidth, long gridRadius) {
Poly.Builder polyBuilder = Poly.threadLocalLambdaBuilder();
polyBuilder.setup(topology.cell);
Variable radius = Variable.newInstance(ImmutableArcInst.ARC_RADIUS, new Double(DBMath.gridToLambda(gridRadius)), TextDescriptor.EMPTY);
return polyBuilder.makePoly(getD().withVariable(radius), gridWidth, style);
}
// /**
// * Method to return a list of Polys that describes all text on this ArcInst.
// * @param hardToSelect is true if considering hard-to-select text.
// * @param wnd the window in which the text will be drawn.
// * @return an array of Polys that describes the text.
// */
// public Poly [] getAllText(boolean hardToSelect, EditWindow0 wnd)
// {
// int dispVars = numDisplayableVariables(false);
// int totalText = dispVars;
// if (totalText == 0) return null;
// Poly [] polys = new Poly[totalText];
// addDisplayableVariables(getBounds(), polys, 0, wnd, false);
// return polys;
// }
/**
* Method to add all displayable Variables on this ArcInst to an array of Poly objects.
* @param rect a rectangle describing the bounds of the object on which the Variables will be displayed.
* @param polys an list of Poly objects that will be filled with the displayable Variables.
* @param wnd window in which the Variables will be displayed.
* @param multipleStrings true to break multi-line text into multiple Polys.
* @param showTempNames show temporary names on nodes and arcs
*/
@Override
public void addDisplayableVariables(Rectangle2D rect, List polys, EditWindow0 wnd, boolean multipleStrings, boolean showTempNames) {
if (isUsernamed() && d.nameDescriptor.isDisplay()) {
double cX = rect.getCenterX();
double cY = rect.getCenterY();
TextDescriptor td = d.nameDescriptor;
double offX = td.getXOff();
double offY = td.getYOff();
TextDescriptor.Position pos = td.getPos();
Poly.Type style = pos.getPolyType();
Poly.Point[] pointList;
if (style == Poly.Type.TEXTBOX) {
pointList = Poly.makePoints(rect);
} else {
pointList = new Poly.Point[1];
pointList[0] = Poly.fromLambda(cX + offX, cY + offY);
}
Poly poly = new Poly(pointList);
poly.setStyle(style);
poly.setString(getNameKey().toString());
poly.setTextDescriptor(td);
poly.setLayer(null);
poly.setDisplayedText(new DisplayedText(this, ARC_NAME));
polys.add(poly);
}
super.addDisplayableVariables(rect, polys, wnd, multipleStrings, false);
}
/**
* Method to get all displayable Variables on this ArcInst to an array of Poly objects.
* @param wnd window in which the Variables will be displayed.
* @param showTempNames show temporary names on nodes and arcs.
* @return an array of Poly objects with displayable variables.
*/
public Poly[] getDisplayableVariables(EditWindow0 wnd, boolean showTempNames) {
return getDisplayableVariables(getBounds(), wnd, true, false);
}
/****************************** CONNECTIONS ******************************/
/**
* Method to return the Connection on the tail end of this ArcInst.
* @return the Connection on the tail end of this ArcInst.
*/
public TailConnection getTail() {
return new TailConnection(this);
}
/**
* Method to return the Connection on the head end of this ArcInst.
* @return the Connection on the head end of this ArcInst.
*/
public HeadConnection getHead() {
return new HeadConnection(this);
}
/**
* Method to return the connection at an end of this ArcInst.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
*/
public Connection getConnection(int connIndex) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
return new TailConnection(this);
case ImmutableArcInst.HEADEND:
return new HeadConnection(this);
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to return the PortInst on tail of this ArcInst.
* @return the PortInst on tail.
*/
public PortInst getTailPortInst() {
return tailPortInst;
}
/**
* Method to return the PortInst on head of this ArcInst.
* @return the PortInst on head.
*/
public PortInst getHeadPortInst() {
return headPortInst;
}
/**
* Method to tell whether this ArcInst is connected directly to another
* Geometric object (that is, an arcinst connected to a nodeinst).
* The method returns true if they are connected.
* @param geom other Geometric object.
* @return true if this and other Geometric objects are connected.
*/
@Override
public boolean isConnected(Geometric geom) {
return tailPortInst.getNodeInst() == geom || headPortInst.getNodeInst() == geom;
}
/**
* Method to return the PortInst on an end of this ArcInst.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @return the PortInst at an end.
*/
public PortInst getPortInst(int connIndex) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
return tailPortInst;
case ImmutableArcInst.HEADEND:
return headPortInst;
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to return the Location on tail of this ArcInst.
* @return the Location on tail.
*/
public EPoint getTailLocation() {
return d.tailLocation;
}
/**
* Method to return the Location on head of this ArcInst.
* @return the Location on head.
*/
public EPoint getHeadLocation() {
return d.headLocation;
}
/**
* Method to return the Location on an end of this ArcInst.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @return the Location on an end.
*/
public EPoint getLocation(int connIndex) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
return d.tailLocation;
case ImmutableArcInst.HEADEND:
return d.headLocation;
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to tell whether a tail connection on this ArcInst contains a port location.
* @param pt the point in question.
* @param reduceForArc if true reduce width by width offset of it prototype.
* @return true if the point is inside of the port.
*/
public boolean tailStillInPort(Point2D pt, boolean reduceForArc) {
return stillInPort(ImmutableArcInst.TAILEND, pt, reduceForArc);
}
/**
* Method to tell whether a head connection on this ArcInst contains a port location.
* @param pt the point in question.
* @param reduceForArc if true reduce width by width offset of it prototype.
* @return true if the point is inside of the port.
*/
public boolean headStillInPort(Point2D pt, boolean reduceForArc) {
return stillInPort(ImmutableArcInst.HEADEND, pt, reduceForArc);
}
/**
* Method to tell whether a connection on this ArcInst contains a port location.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @param pt the point in question.
* @param reduceForArc if true reduce width by width offset of it prototype.
* @return true if the point is inside of the port.
*/
public boolean stillInPort(int connIndex, Point2D pt, boolean reduceForArc) {
// determine the area of the nodeinst
PortInst pi = getPortInst(connIndex);
Poly poly = pi.getPoly();
if (reduceForArc) {
double wid = getLambdaBaseWidth();
poly.reducePortPoly(pi, wid, getAngle());
}
return stillInPoly(pt, poly);
// if (poly.isInside(pt)) return true;
// if (poly.polyDistance(pt.getX(), pt.getY()) < MINPORTDISTANCE) return true;
//
// // no good
// return false;
}
private static boolean stillInPoly(Point2D pt, Poly poly) {
return poly.isInside(pt) || poly.polyDistance(pt.getX(), pt.getY()) < MINPORTDISTANCE;
}
/****************************** TEXT ******************************/
/**
* Method to return the name of this ArcInst.
* @return the name of this ArcInst.
*/
public String getName() {
return d.name.toString();
}
/**
* Returns true if this ArcInst was named by user.
* @return true if this ArcInst was named by user.
*/
public boolean isUsernamed() {
return d.isUsernamed();
}
/**
* Method to return the name key of this ArcInst.
* @return the name key of this ArcInst, null if there is no name.
*/
public Name getNameKey() {
return d.name;
}
/**
* Method to rename this ArcInst.
* This ArcInst must be linked to database.
* @param name new name of this geometric.
* @return true on error
* @deprecated Use method with explicit EditingPreferences parameter.
*/
public boolean setName(String name) {
return setName(name, EditingPreferences.getThreadEditingPreferences());
}
/**
* Method to rename this ArcInst.
* This ArcInst must be linked to database.
* @param name new name of this geometric.
* @param ep EditingPreferences with default sizes and text descriptors.
* @return true on error
*/
public boolean setName(String name, EditingPreferences ep) {
assert isLinked();
Name key;
boolean doSmart = false;
if (name != null && name.length() > 0) {
if (name.equals(getName())) {
return false;
}
if (!isUsernamed()) {
doSmart = true;
}
key = Name.findName(name);
} else {
if (!isUsernamed()) {
return false;
}
key = topology.getArcAutoname();
}
if (checkNameKey(key, topology) || key.isBus() && getProto() != Schematics.tech().bus_arc) {
return true;
}
ImmutableArcInst oldD = d;
lowLevelModify(d.withName(key));
if (doSmart) {
TextDescriptor td = ep.getArcTextDescriptor();
TextDescriptor smartDescriptor = getSmartTextDescriptor(getAngle(), getLambdaBaseWidth(), td, ep);
setTextDescriptor(ARC_NAME, smartDescriptor);
}
// apply constraints
Constraints.getCurrent().modifyArcInst(this, oldD);
return false;
}
/**
* Method to return a "smart" text descriptor for an arc.
* @param angle the angle of the arc (in tenths of a degree).
* @param width the width of the arc.
* @param prev the former text descriptor of the arc.
* @return a new text descriptor that handles smart placement.
*/
private static TextDescriptor getSmartTextDescriptor(int angle, double width, TextDescriptor prev, EditingPreferences ep) {
// assigning valid name: do smart text placement
if (angle == -1) {
angle = 0;
}
if ((angle % 1800) == 0) {
// horizontal arc
int smart = ep.getSmartHorizontalPlacementArc();
if (smart == 1) {
// arc text above
return prev.withPos(TextDescriptor.Position.UP).withOff(0, width / 2);
} else if (smart == 2) {
// arc text below
return prev.withPos(TextDescriptor.Position.DOWN).withOff(0, -width / 2);
}
} else if ((angle % 1800) == 900) {
// vertical arc
int smart = ep.getSmartVerticalPlacementArc();
if (smart == 1) {
// arc text to the left
return prev.withPos(TextDescriptor.Position.LEFT).withOff(-width / 2, 0);
} else if (smart == 2) {
// arc text to the right
return prev.withPos(TextDescriptor.Position.RIGHT).withOff(width / 2, 0);
}
}
return prev;
}
/**
* Method to check the new name key of an ArcInst.
* @param name new name key of this ArcInst.
* @param parent parent Cell used for error message
* @return true on error.
*/
private static boolean checkNameKey(Name name, Topology topology) {
Cell parent = topology.cell;
if (!name.isValid()) {
System.out.println(parent + ": Invalid name \"" + name + "\" wasn't assigned to arc" + " :" + Name.checkName(name.toString()));
return true;
}
if (name.isBus()) {
if (name.isTempname()) {
System.out.println(parent + ": Temporary name \"" + name + "\" can't be bus");
return true;
}
if (!parent.busNamesAllowed()) {
System.out.println(parent + ": Bus name \"" + name + "\" can be in icons and schematics only");
return true;
}
}
if (name.isTempname() && name.getBasename() != ImmutableArcInst.BASENAME) {
System.out.println(parent + ": Temporary arc name \"" + name + "\" must have prefix net@");
return true;
}
if (name.hasEmptySubnames()) {
if (name.isBus()) {
System.out.println(parent + ": Name \"" + name + "\" with empty subnames wasn't assigned to arc");
} else {
System.out.println(parent + ": Cannot assign empty name \"" + name + "\" to arc");
}
return true;
}
if (topology.hasTempArcName(name)) {
System.out.println(parent + " already has ArcInst with temporary name \"" + name + "\"");
return true;
}
return false;
}
/**
* Returns the TextDescriptor on this ArcInst selected by variable key.
* This key may be a key of variable on this ArcInst or the
* special keys:
* ArcInst.ARC_NAME
* The TextDescriptor gives information for displaying the Variable.
* @param varKey key of variable or special key.
* @return the TextDescriptor on this ArcInst.
*/
public TextDescriptor getTextDescriptor(Variable.Key varKey) {
if (varKey == ARC_NAME) {
return d.nameDescriptor;
}
return super.getTextDescriptor(varKey);
}
/**
* Updates the TextDescriptor on this ArcInst selected by varKey.
* The varKey may be a key of variable on this ArcInst or
* the special key ArcInst.ARC_NAME.
* If varKey doesn't select any text descriptor, no action is performed.
* The TextDescriptor gives information for displaying the Variable.
* @param varKey key of variable or special key.
* @param td new value TextDescriptor
*/
@Override
public void setTextDescriptor(Variable.Key varKey, TextDescriptor td) {
if (varKey == ARC_NAME) {
setD(d.withNameDescriptor(td), true);
return;
}
super.setTextDescriptor(varKey, td);
}
/**
* Method to determine whether a variable key on ArcInst is deprecated.
* Deprecated variable keys are those that were used in old versions of Electric,
* but are no longer valid.
* @param key the key of the variable.
* @return true if the variable key is deprecated.
*/
@Override
public boolean isDeprecatedVariable(Variable.Key key) {
if (key == ARC_NAME) {
return true;
}
return super.isDeprecatedVariable(key);
}
/**
* Method to describe this ArcInst as a string.
* @param withQuotes to wrap description between quotes
* @return a description of this ArcInst.
*/
@Override
public String describe(boolean withQuotes) {
String description = getProto().describe();
String name = (withQuotes) ? "'" + getName() + "'" : getName();
if (name != null) {
description += "[" + name + "]";
}
return description;
}
/**
* Method to describe this ArcInst as a string.
* Arc technology is always prepended.
* @return a description of this ArcInst.
*/
public String libDescribe() {
StringBuilder sb = new StringBuilder();
sb.append(getProto().getFullName());
sb.append('[');
sb.append(getName());
sb.append(']');
return sb.toString();
}
/**
* Method to describe this ArcInst as a string.
* Arc technology is never prepended.
* @return a description of this ArcInst.
*/
public String noLibDescribe() {
StringBuilder sb = new StringBuilder();
sb.append(getProto().getName());
sb.append('[');
sb.append(getName());
sb.append(']');
return sb.toString();
}
/**
* Compares ArcInsts by their Cells and names.
* @param that the other ArcInst.
* @return a comparison between the ArcInsts.
*/
public int compareTo(ArcInst that) {
int cmp;
if (this.topology != that.topology) {
cmp = this.topology.cell.compareTo(that.topology.cell);
if (cmp != 0) {
return cmp;
}
}
cmp = this.getName().compareTo(that.getName());
if (cmp != 0) {
return cmp;
}
return this.d.arcId - that.d.arcId;
}
/**
* Returns a printable version of this ArcInst.
* @return a printable version of this ArcInst.
*/
public String toString() {
return "arc " + describe(true);
}
/****************************** CONSTRAINTS ******************************/
private void setFlag(ImmutableArcInst.Flag flag, boolean state) {
checkChanging();
if (setD(d.withFlag(flag, state), true)) {
topology.setArcsDirty();
}
}
/**
* Method to set this ArcInst to be rigid.
* Rigid arcs cannot change length or the angle of their connection to a NodeInst.
* @param state
*/
public void setRigid(boolean state) {
setFlag(ImmutableArcInst.RIGID, state);
}
/**
* Method to tell whether this ArcInst is rigid.
* Rigid arcs cannot change length or the angle of their connection to a NodeInst.
* @return true if this ArcInst is rigid.
*/
public boolean isRigid() {
return d.isRigid();
}
/**
* Method to set this ArcInst to be fixed-angle.
* Fixed-angle arcs cannot change their angle, so if one end moves,
* the other may also adjust to keep the arc angle constant.
* @param state
*/
public void setFixedAngle(boolean state) {
setFlag(ImmutableArcInst.FIXED_ANGLE, state);
}
/**
* Method to tell whether this ArcInst is fixed-angle.
* Fixed-angle arcs cannot change their angle, so if one end moves,
* the other may also adjust to keep the arc angle constant.
* @return true if this ArcInst is fixed-angle.
*/
public boolean isFixedAngle() {
return d.isFixedAngle();
}
/**
* Method to set this ArcInst to be slidable.
* Arcs that slide will not move their connected NodeInsts if the arc's end is still within the port area.
* Arcs that cannot slide will force their NodeInsts to move by the same amount as the arc.
* Rigid arcs cannot slide but nonrigid arcs use this state to make a decision.
* @param state
*/
public void setSlidable(boolean state) {
setFlag(ImmutableArcInst.SLIDABLE, state);
}
/**
* Method to tell whether this ArcInst is slidable.
* Arcs that slide will not move their connected NodeInsts if the arc's end is still within the port area.
* Arcs that cannot slide will force their NodeInsts to move by the same amount as the arc.
* Rigid arcs cannot slide but nonrigid arcs use this state to make a decision.
* @return true if this ArcInst is slidable.
*/
public boolean isSlidable() {
return d.isSlidable();
}
/****************************** PROPERTIES ******************************/
/**
* Method to determine whether this ArcInst is directional, with an arrow on one end.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @return true if that end has a directional arrow on it.
*/
public boolean isArrowed(int connIndex) {
return d.isArrowed(connIndex);
}
/**
* Method to determine whether this ArcInst is directional, with an arrow on the tail.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @return true if the arc's tail has a directional arrow on it.
*/
public boolean isTailArrowed() {
return d.isTailArrowed();
}
/**
* Method to determine whether this ArcInst is directional, with an arrow on the head.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @return true if the arc's head has a directional arrow on it.
*/
public boolean isHeadArrowed() {
return d.isHeadArrowed();
}
/**
* Method to determine whether this ArcInst is directional, with an arrow line drawn down the center.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* The body is typically drawn when one of the ends has an arrow on it, but it may be
* drawn without an arrow head in order to continue an attached arc that has an arrow.
* @return true if the arc's tail has an arrow line on it.
*/
public boolean isBodyArrowed() {
return d.isBodyArrowed();
}
/**
* Method to set this ArcInst to be directional, with an arrow on one end.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @param state true to show a directional arrow on the specified end.
*/
public void setArrowed(int connIndex, boolean state) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
setTailArrowed(state);
break;
case ImmutableArcInst.HEADEND:
setHeadArrowed(state);
break;
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to set this ArcInst to be directional, with an arrow on the tail.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @param state true to show a directional arrow on the tail.
*/
public void setTailArrowed(boolean state) {
setFlag(ImmutableArcInst.TAIL_ARROWED, state);
}
/**
* Method to set this ArcInst to be directional, with an arrow on the head.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* @param state true to show a directional arrow on the head.
*/
public void setHeadArrowed(boolean state) {
setFlag(ImmutableArcInst.HEAD_ARROWED, state);
}
/**
* Method to set this ArcInst to be directional, with an arrow line drawn down the center.
* Directional arcs have an arrow drawn on them to indicate flow.
* It is only for documentation purposes and does not affect the circuit.
* The body is typically drawn when one of the ends has an arrow on it, but it may be
* drawn without an arrow head in order to continue an attached arc that has an arrow.
* @param state true to show a directional line on this arc.
*/
public void setBodyArrowed(boolean state) {
setFlag(ImmutableArcInst.BODY_ARROWED, state);
}
/**
* Method to tell whether an end of ArcInst has its ends extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @return true if that end of this ArcInst is extended.
*/
public boolean isExtended(int connIndex) {
return d.isExtended(connIndex);
}
/**
* Method to tell whether the tail of this arc is extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @return true if the tail of this arc is extended.
*/
public boolean isTailExtended() {
return d.isTailExtended();
}
/**
* Method to tell whether the head of this arc is extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @return true if the head of this arc is extended.
*/
public boolean isHeadExtended() {
return d.isHeadExtended();
}
/**
* Method to set whether an end of this arc is extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @param e true to set that end of this arc to be extended.
*/
public void setExtended(int connIndex, boolean e) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
setTailExtended(e);
break;
case ImmutableArcInst.HEADEND:
setHeadExtended(e);
break;
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to set whether the tail of this arc is extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @param e true to set the tail of this arc to be extended.
*/
public void setTailExtended(boolean e) {
setFlag(ImmutableArcInst.TAIL_EXTENDED, e);
// if (isLinked()) updateGeometric();
}
/**
* Method to set whether the head of this arc is extended.
* Extended arcs continue past their endpoint by half of their width.
* Most layout arcs want this so that they make clean connections to orthogonal arcs.
* @param e true to set the head of this arc to be extended.
*/
public void setHeadExtended(boolean e) {
setFlag(ImmutableArcInst.HEAD_EXTENDED, e);
// if (isLinked()) updateGeometric();
}
/**
* Method to tell whether an end of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @return true if set that end of this arc is negated.
*/
public boolean isNegated(int connIndex) {
return d.isNegated(connIndex);
}
/**
* Method to tell whether the tail of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @return true if set the tail of this arc is negated.
*/
public boolean isTailNegated() {
return d.isTailNegated();
}
/**
* Method to tell whether the head of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @return true if set the head of this arc is negated.
*/
public boolean isHeadNegated() {
return d.isHeadNegated();
}
/**
* Method to set whether an end of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @param connIndex TAILEND (0) for the tail of this ArcInst, HEADEND (1) for the head.
* @param n true to set that end of this arc to be negated.
*/
public void setNegated(int connIndex, boolean n) {
switch (connIndex) {
case ImmutableArcInst.TAILEND:
setTailNegated(n);
break;
case ImmutableArcInst.HEADEND:
setHeadNegated(n);
break;
default:
throw new IllegalArgumentException("Bad end " + connIndex);
}
}
/**
* Method to set whether the tail of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @param n true to set the tail of this arc to be negated.
*/
public void setTailNegated(boolean n) {
if (!(d.tailPortId instanceof PrimitivePortId && getTechPool().getPrimitivePort((PrimitivePortId) d.tailPortId).isNegatable())) {
n = false;
}
if (getProto().getTechnology().isNoNegatedArcs()) {
n = false;
}
setFlag(ImmutableArcInst.TAIL_NEGATED, n);
}
/**
* Method to set whether the head of this arc is negated.
* Negated arc have a negating bubble on them to indicate negation.
* This is only valid in schematics technologies.
* @param n true to set the head of this arc to be negated.
*/
public void setHeadNegated(boolean n) {
if (!(d.headPortId instanceof PrimitivePortId && getTechPool().getPrimitivePort((PrimitivePortId) d.headPortId).isNegatable())) {
n = false;
}
if (getProto().getTechnology().isNoNegatedArcs()) {
n = false;
}
setFlag(ImmutableArcInst.HEAD_NEGATED, n);
}
/****************************** MISCELLANEOUS ******************************/
/**
* Method to check and repair data structure errors in this ArcInst.
*/
public int checkAndRepair(boolean repair, List list, ErrorLogger errorLogger) {
int errorCount = 0;
ArcProto ap = getProto();
Cell parent = topology.cell;
if (ap.isNotUsed()) {
// if (repair)
if (errorLogger != null) {
String msg = "Prototype of arc " + getName() + " is unused";
if (repair) {
// Can't put this arc into error logger because it will be deleted.
Poly poly = makeLambdaPoly(getGridBaseWidth(), Poly.Type.CLOSED);
errorLogger.logError(msg, poly, parent, 1);
} else {
errorLogger.logError(msg, this, parent, null, 1);
}
}
if (repair) {
list.add(this);
}
// This counts as 1 error, ignoring other errors
return 1;
}
// see if the ends are in their ports
if (!headStillInPort(d.headLocation, false)) {
Poly poly = headPortInst.getPoly();
String msg = parent + ", " + this
+ ": head not in port, is at (" + d.headLocation.getX() + "," + d.headLocation.getY()
+ ") distance to port is " + poly.polyDistance(d.headLocation.getX(), d.headLocation.getY())
+ " port center is (" + poly.getCenterX() + "," + poly.getCenterY() + ")";
System.out.println(msg);
if (errorLogger != null) {
List