/*
* JBoss, Home of Professional Open Source.
*
* See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing.
*
* See the AUTHORS.txt file distributed with this work for a full listing of individual contributors.
*/
package org.teiid.designer.diagram.ui.layout.spring;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.WeakHashMap;
import org.eclipse.draw2d.geometry.Rectangle;
import org.teiid.designer.diagram.ui.connection.NodeConnectionModel;
import org.teiid.designer.diagram.ui.layout.DefaultLayoutNode;
import org.teiid.designer.diagram.ui.layout.DiagramLayout;
import org.teiid.designer.diagram.ui.layout.LayoutNode;
import org.teiid.designer.diagram.ui.layout.LayoutUtilities;
import org.teiid.designer.diagram.ui.model.DiagramModelNode;
/**
* @since 8.0
*/
public class SpringLayout extends DiagramLayout {
static final String CLASS_NAME = "SpringLayout"; //$NON-NLS-1$
public static final int LEFT_ALIGNMENT = 0;
public static final int CENTER_ALIGNMENT = 2;
public static final int RIGHT_ALIGNMENT = 1;
public static final int TOP_ALIGNMENT = 3;
public static final int ERROR_NO_COMPONENT = 1;
public static final int ERROR_NON_CONNECTED_GRAPH = 2;
public static final int ERROR_ALL_COMPONENTS_FIXED = 3;
private boolean _autoEdgeLength = true;
private double _edgeLength = 1000.0;
private boolean _useObjectsSizes = true;
// private boolean _fixSelected = false;
private double _XSpacing = 10.0;
private double _YSpacing = 10.0;
private boolean _automaticSingleSpacing = true;
private boolean _specifyLayoutSize = false;
private double _layoutSize = 500.0;
private int _horizontalAlignment = 2;
private int _verticalAlignment = 2;
private int _repaintPeriod = 0;
private double _epsilon = 1.5;
private final HashMap _nodeConstraints;
private final WeakHashMap _linkConstraints;
private DiagramModelNode diagramNode;
private LayoutNode[] springNodes;
public SpringLayout( List nodes) {
_nodeConstraints = new HashMap();
_linkConstraints = new WeakHashMap();
// Find and set the diagram node parent object
Object firstNode = nodes.get(0);
if( firstNode instanceof DiagramModelNode )
diagramNode = ((DiagramModelNode)firstNode).getParent();
else if( firstNode instanceof LayoutNode ) {
diagramNode = ((LayoutNode)firstNode).getModelNode().getParent();
}
createSpringNodes(nodes);
}
public void createSpringNodes(List nodes) {
// Walk through the node list and create the list of new spring nodes.
if( nodes != null && !nodes.isEmpty() ) {
LayoutNode nextSpringNode = null;
Iterator iter = nodes.iterator();
int nNodes = nodes.size();
springNodes = new LayoutNode[nNodes];
int iNode = 0;
Object nextObject = null;
while( iter.hasNext()) {
nextObject = iter.next();
// Could be a list of diagramNodes or layoutNodes
if( nextObject instanceof DiagramModelNode ) {
nextSpringNode = new DefaultLayoutNode((DiagramModelNode)nextObject);
} else if( nextObject instanceof LayoutNode ) {
nextSpringNode = (LayoutNode)nextObject;
}
SpringNodeConstraints newConstraint = null;
if( nextSpringNode != null ) {
springNodes[iNode] = nextSpringNode;
Object modelObject = nextSpringNode.getModelNode().getModelObject();
newConstraint = new SpringNodeConstraints(false, 50.0);
_nodeConstraints.put(modelObject, newConstraint);
iNode++;
}
}
}
}
@Override
public int run() {
int resultValue = run(springNodes, 0.0, 0.0);
// Here's where we need to fix!!!!!
//Here's where...
// if (resultValue == 2) {
// int iVAlign = getVerticalAlignment();
// setVerticalAlignment(3);
//
// Hashtable hashtable = new Hashtable();
//
//// LayoutNode[] allLayoutNodes = DiagramUiUtilities.getNodeArray(diagramNode.getChildren());
//
// int nSpringNodes = springNodes.length;
//
// for (int iNode = 0; iNode < nSpringNodes; iNode++)
// hashtable.put(springNodes[iNode].getModelNode(), new Integer(iNode));
//
// // Set up Connection Array
// NodeConnectionModel[][] connectionNodes = new NodeConnectionModel[nSpringNodes][nSpringNodes];
//
// for (int iNode = 0; iNode < springNodes.length; iNode++) {
//
// LayoutNode nextNode = springNodes[iNode];
//
// if (nextNode != null && nextNode instanceof NodeConnectionModel) {
//
// NodeConnectionModel connectionModel = (NodeConnectionModel)nextNode;
//
// Object sourceNode = hashtable.get(connectionModel.getSourceNode());
//
// if (sourceNode != null) {
//
// Object targetNode = hashtable.get(connectionModel.getTargetNode());
//
// if (targetNode != null) {
// int sourceId = ((Integer)sourceNode).intValue();
// int targetId = ((Integer)targetNode).intValue();
// connectionNodes[sourceId][targetId] = connectionModel;
// connectionNodes[targetId][sourceId] = connectionModel;
// }
// }
// }
// }
// Vector singlesVector = new Vector();
// Vector connectedVector = new Vector();
// if (connectionNodes != null) {
// for (int iConn = 0; iConn < connectionNodes.length; iConn++) {
// boolean bool = true;
// for (int jConn = 0; jConn < connectionNodes.length; jConn++) {
// if (connectionNodes[iConn][jConn] != null)
// bool = false;
// }
// if (bool)
// singlesVector.add(springNodes[iConn]);
// else
// connectedVector.add(springNodes[iConn]);
// }
// }
//
// Rectangle2D rectangle2d;
//
// if (singlesVector.size() > 0)
// rectangle2d = layoutSingleComponents((LayoutNode[])singlesVector.toArray(new LayoutNode[1]));
// else
// rectangle2d = new Rectangle2D.Double(0.0, 0.0, 0.0, 0.0);
//
// if (connectedVector.size() > 0) {
// resultValue = layoutConnectedComponents(
// (Vector)connectedVector.clone(),
// connectionNodes,
// hashtable,
// rectangle2d);
// }
//
// setVerticalAlignment(iVAlign);
// }
// move to upper left hand corner
LayoutUtilities.justifyAllToCorner(springNodes);
// set the final positions on the model objects
setFinalNodePositions();
return resultValue;
}
int run(LayoutNode[] nodeArray, double deltaX, double deltaY) {
int nNodes = nodeArray.length;
Spring spring = new Spring(this);
if (nNodes == 0)
return 0;
Rectangle rectangle2d;
if (_specifyLayoutSize)
rectangle2d = new Rectangle(getStartX(), getStartY(), (int)_layoutSize, (int)_layoutSize);
else {
int length = (int)getSizeEstimate();
rectangle2d = new Rectangle(getStartX(), getStartY(), length, length);
}
spring.setRectangle(rectangle2d);
spring.setAutoEdgeLength(_autoEdgeLength);
spring.setEdgeLength(_edgeLength);
spring.setWidthIgnored(!_useObjectsSizes);
spring.setHeightIgnored(!_useObjectsSizes);
// spring.setFixSelected(_fixSelected);
spring.setRepaintPeriod(_repaintPeriod);
spring.setEpsilon(_epsilon);
spring.setNodeConstraints(_nodeConstraints);
spring.setLinkConstraints(_linkConstraints);
String string = spring.compute(nodeArray, nNodes);
for (int i = 0; i < nodeArray.length; i++) {
LayoutNode nextNode = nodeArray[i];
nextNode.setCenterXY(spring.centerX[i], spring.centerY[i]);
}
Rectangle rectangle = new Rectangle(nodeArray[0].getBounds());
double startX = rectangle.x;
double startY = rectangle.y;
double currentW = rectangle.width;
double currentH = rectangle.height;
for (int i = 1; i < nodeArray.length; i++) {
double thisX = nodeArray[i].getX();
double thisY = nodeArray[i].getY();
double thisW = nodeArray[i].getBounds().width;
double thisH = nodeArray[i].getBounds().height;
startX = Math.min(thisX, startX);
startY = Math.min(thisY, startY);
currentW = Math.max(currentW, thisW);
currentH = Math.max(currentH, thisH);
}
double xOffset;
double totalCenterX = rectangle.getCenter().x;
double totalCenterY = rectangle.getCenter().y;
if (_horizontalAlignment == 0)
xOffset = -deltaX + rectangle.x - getStartX();
else if (_horizontalAlignment == 2)
xOffset = (-deltaX + rectangle.x + rectangle.width * 0.5 - totalCenterX);
else
xOffset = (-deltaX + rectangle.x + rectangle.width - getStartX() - this.getWidth());
double yOffset;
if (_verticalAlignment == 2)
yOffset = (-deltaY + rectangle.y + rectangle.height * 0.5 - totalCenterY);
else
yOffset = -deltaY + rectangle.y - getStartY();
// reposition all objects
for (int i = 0; i < nNodes; i++) {
LayoutNode nextNode = nodeArray[i];
nextNode.setCenterXY(spring.centerX[i] - xOffset, spring.centerY[i] - yOffset);
}
if (string == null)
return 0;
if (string.endsWith("to layout")) //$NON-NLS-1$
return 1;
if (string.endsWith("non-connected graph!")) //$NON-NLS-1$
return 2;
return 3;
}
public SpringNodeConstraints getNodeConstraints(LayoutNode springNode) {
if (!_nodeConstraints.containsKey(springNode))
throw new IllegalArgumentException("Node " + springNode + " is not managed by this layout"); //$NON-NLS-1$ //$NON-NLS-2$
return (SpringNodeConstraints)_nodeConstraints.get(springNode);
}
public void removeNodeConstraints(LayoutNode springNode) {
if (!_nodeConstraints.containsKey(springNode))
throw new IllegalArgumentException("Node " + springNode + " is not managed by this layout"); //$NON-NLS-1$ //$NON-NLS-2$
_nodeConstraints.put(springNode, null);
}
public void setNodeConstraints(
LayoutNode springNode,
SpringNodeConstraints springnodeconstraints) {
if (!_nodeConstraints.containsKey(springNode))
throw new IllegalArgumentException("Node " + springNode + " is not managed by this layout"); //$NON-NLS-1$ //$NON-NLS-2$
_nodeConstraints.put(springNode, springnodeconstraints);
}
public SpringLinkConstraints getLinkConstraints(NodeConnectionModel connectionModel) {
return (SpringLinkConstraints)_linkConstraints.get(connectionModel);
}
public void removeLinkConstraints(NodeConnectionModel connectionModel) {
_linkConstraints.remove(connectionModel);
}
public void setLinkConstraints(
NodeConnectionModel connectionModel,
SpringLinkConstraints springlinkconstraints) {
_linkConstraints.put(connectionModel, springlinkconstraints);
}
public boolean getAutoEdgeLength() {
return _autoEdgeLength;
}
public double getEdgeLength() {
return _edgeLength;
}
public double getEpsilon() {
return _epsilon;
}
public int getRepaintPeriod() {
return _repaintPeriod;
}
public boolean getUseObjectsSizes() {
return _useObjectsSizes;
}
public void setAutoEdgeLength(boolean bool) {
_autoEdgeLength = bool;
}
public void setEdgeLength(double d) {
_edgeLength = d;
}
public void setEpsilon(double d) {
_epsilon = d;
}
public void setRepaintPeriod(int i) {
_repaintPeriod = i;
}
public void setUseObjectsSizes(boolean bool) {
_useObjectsSizes = bool;
}
public int getHorizontalAlignment() {
return _horizontalAlignment;
}
public void setHorizontalAlignment(int i) {
if (i <= 2)
_horizontalAlignment = i;
}
public int getVerticalAlignment() {
return _verticalAlignment;
}
public void setVerticalAlignment(int i) {
if (i >= 2)
_verticalAlignment = i;
}
public boolean getSpecifyLayoutSize() {
return _specifyLayoutSize;
}
public void setSpecifyLayoutSize(boolean bool) {
_specifyLayoutSize = bool;
}
public boolean getAutomaticSingleSpacing() {
return _automaticSingleSpacing;
}
public void setAutomaticSingleSpacing(boolean bool) {
_automaticSingleSpacing = bool;
}
public double getLayoutSize() {
return _layoutSize;
}
public void setLayoutSize(double d) {
_layoutSize = d;
}
public double getXSpacing() {
return _XSpacing;
}
public void setXSpacing(double d) {
_XSpacing = d;
}
public double getYSpacing() {
return _YSpacing;
}
public void setYSpacing(double d) {
_YSpacing = d;
}
private void setFinalNodePositions() {
for( int i=0; i<springNodes.length; i++ ) {
springNodes[i].setFinalPosition();
}
}
/**
* @return
*/
public DiagramModelNode getDiagramNode() {
return diagramNode;
}
public double getSizeEstimate() {
double totalArea = 0.0;
int nNodes = springNodes.length;
double areaFactor = 5 + 2*Math.sqrt(nNodes);
for( int i=0; i<springNodes.length; i++ ) {
totalArea += (springNodes[i].getWidth() * springNodes[i].getHeight());
}
totalArea = areaFactor*totalArea;
double length = Math.sqrt(totalArea);
return length;
}
// public int getCurrentWidth() {
// // Walk through springNodes and get the total width
// double currentWidth = 0;
// double nextXPlusW = 0;
// for( int i=0; i<springNodes.length; i++ ) {
// nextXPlusW = (springNodes[i].getCenterX() + springNodes[i].getWidth()/2);
// currentWidth = Math.max(currentWidth, nextXPlusW);
// }
//
// return (int)currentWidth;
// }
//
//
// public int getCurrentHeight() {
// // Walk through springNodes and get the total width
// double currentHeight = 0;
// double nextYPlusH = 0;
// for( int i=0; i<springNodes.length; i++ ) {
// nextYPlusH = (springNodes[i].getCenterY() + springNodes[i].getHeight()/2);
// currentHeight = Math.max(currentHeight, nextYPlusH);
// }
//
// return (int)currentHeight;
// }
}