### Eclipse Workspace Patch 1.0 #P org.eclipse.zest.layouts Index: src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/ContinuousLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.10 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; - -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; - -/** - * - * @author Ian Bull - * - * Used to represent algorithms that can continuously run. - * - */ -public abstract class ContinuousLayoutAlgorithm extends AbstractLayoutAlgorithm { - - double x, y, widht, height; - - public ContinuousLayoutAlgorithm(int styles) { - super(styles); - } - - /** - * The logic to determine if a layout should continue running or not - */ - protected abstract boolean performAnotherNonContinuousIteration(); - - /** - * Computes a single iteration of the layout algorithm - * @return - */ - protected abstract void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height); - - private boolean continueRunning() { - if (layoutStopped) { - return false; - } else if (this.internalContinuous && !layoutStopped) { - return true; - } else if (performAnotherNonContinuousIteration()) { - return true; - } else { - return false; - } - } - - public void setLayoutArea(double x, double y, double width, double height) { - this.setBounds(x, y, width, height); - - } - - public synchronized DisplayIndependentRectangle getBounds() { - return new DisplayIndependentRectangle(this.x, this.y, this.widht, this.height); - } - - public synchronized void setBounds(double x, double y, double width, double height) { - this.x = x; - this.y = y; - this.widht = width; - this.height = height; - } - - /** - * Calculates and applies the positions of the given entities based on a - * spring layout using the given relationships. - */ - protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - - this.setBounds(x, y, width, height); - - while (continueRunning()) { - // check for entities and relationships to add or remove - entitiesToLayout = updateEntities(entitiesToLayout); - relationshipsToConsider = updateRelationships(relationshipsToConsider); - DisplayIndependentRectangle bounds = this.getBounds(); - double localX = bounds.x; - double localY = bounds.y; - double localWidth = bounds.width; - double localHeight = bounds.height; - - computeOneIteration(entitiesToLayout, relationshipsToConsider, localX, localY, localWidth, localHeight); - - updateLayoutLocations(entitiesToLayout); - - if (this.internalContinuous) { - fireProgressEvent(1, 1); - } else { - fireProgressEvent(getCurrentLayoutStep(), getTotalNumberOfLayoutSteps()); - } - - } - } - -} Index: src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java,v retrieving revision 1.7 diff -u -r1.7 GridLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 +++ src/org/eclipse/zest/layouts/algorithms/GridLayoutAlgorithm.java 10 Jul 2009 18:49:26 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,14 +7,15 @@ * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ package org.eclipse.zest.layouts.algorithms; -import java.util.Arrays; - -import org.eclipse.zest.layouts.LayoutStyles; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; /** @@ -22,133 +23,96 @@ * @author Ian Bull * @author Casey Best and Rob Lintern */ -public class GridLayoutAlgorithm extends AbstractLayoutAlgorithm { +public class GridLayoutAlgorithm implements LayoutAlgorithm { private static final double PADDING_PERCENTAGE = 0.95; + private static final int MIN_ENTITY_SIZE = 5; + protected double aspectRatio = 1.0; protected int rowPadding = 0; - - public void setLayoutArea(double x, double y, double width, double height) { - throw new RuntimeException("Operation not implemented"); + private boolean resize = false; + protected int rows, cols, numChildren; + protected double colWidth, rowHeight, offsetX, offsetY; + protected double childrenHeight, childrenWidth; + + private LayoutContext context; + + + public void setLayoutContext(LayoutContext context) { + this.context = context; } - - int rows, cols, numChildren; - double colWidth, rowHeight, offsetX, offsetY; - int totalProgress; - double h, w; - /** - * Initializes the grid layout. - * @param styles - * @see LayoutStyles - */ - public GridLayoutAlgorithm(int styles) { - super(styles); + public void applyLayout() { + DisplayIndependentRectangle bounds = context.getBounds(); + calculateGrid(bounds); + applyLayoutInternal(context.getEntities(), bounds); } - + /** - * Inititalizes the grid layout with no style. + * Calculates all the dimensions of grid that layout entities will be fit + * in. The following fields are set by this method: {@link #numChildren}, + * {@link #rows}, {@link #cols}, {@link #colWidth}, {@link #rowHeight}, + * {@link #offsetX}, {@link #offsetY} + * + * @param bounds */ - public GridLayoutAlgorithm() { - this(LayoutStyles.NONE); - } - - - protected int getCurrentLayoutStep() { - // TODO: This isn't right - return 0; - } - - protected int getTotalNumberOfLayoutSteps() { - return totalProgress; - } - - /** - * - */ - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { + protected void calculateGrid(DisplayIndependentRectangle bounds) { + numChildren = context.getNodes().length; + int[] result = calculateNumberOfRowsAndCols(numChildren, bounds.x, bounds.y, bounds.width, bounds.height); + cols = result[0]; + rows = result[1]; - // TODO: Filter unwanted entities and relationships - //super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight); - // now begin - numChildren = entitiesToLayout.length; - if (numChildren < 1) return; + colWidth = bounds.width / cols; + rowHeight = bounds.height / rows; - int[] colsAndRows = calculateNumberOfRowsAndCols(numChildren, x, y, width, height); - cols = colsAndRows[0]; - rows = colsAndRows[1]; - - totalProgress = rows + 2; - fireProgressEvent (1, totalProgress); - - // sort the entities - if (comparator != null) { - Arrays.sort(entitiesToLayout, comparator); - } else { - Arrays.sort(entitiesToLayout); - } - fireProgressEvent (2, totalProgress); - - // Calculate row height and column width - colWidth = width/cols; - rowHeight = height/rows; - - // Calculate amount to scale children double [] nodeSize = calculateNodeSize (colWidth, rowHeight); - w = nodeSize[0]; - h = nodeSize[1]; - offsetX = (colWidth - w)/2.0; // half of the space between columns - offsetY = (rowHeight - h)/2.0; // half of the space between rows + childrenWidth = nodeSize[0]; + childrenHeight = nodeSize[1]; + offsetX = (colWidth - childrenWidth) / 2.0; // half of the space between + // columns + offsetY = (rowHeight - childrenHeight) / 2.0; // half of the space + // between rows } - + /** - * Use this algorithm to layout the given entities, using the given relationships and bounds. - * The entities will be placed in the same order as they are passed in, unless a comparator - * is supplied. - * - * @param entitiesToLayout Apply the algorithm to these entities - * @param relationshipsToConsider Only consider these relationships when applying the algorithm. - * @param boundsX The left side of the bounds in which the layout can place the entities. - * @param boundsY The top side of the bounds in which the layout can place the entities. - * @param boundsWidth The width of the bounds in which the layout can place the entities. - * @param boundsHeight The height of the bounds in which the layout can place the entities. - * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider + * Use this algorithm to layout the given entities and bounds. The entities + * will be placed in the same order as they are passed in. + * + * @param entitiesToLayout + * apply the algorithm to these entities + * @param bounds + * the bounds in which the layout can place the entities. */ - protected synchronized void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { + protected synchronized void applyLayoutInternal(EntityLayout[] entitiesToLayout, DisplayIndependentRectangle bounds) { int index = 0; for( int i = 0; i < rows; i++ ) { for( int j = 0; j < cols; j++ ) { if( (i*cols + j) < numChildren ) { - // find new position for child - double xmove = boundsX + j * colWidth + offsetX; - double ymove = boundsY + i * rowHeight + offsetY; - InternalNode sn = entitiesToLayout[index++]; - sn.setInternalLocation( xmove, ymove ); - sn.setInternalSize( Math.max(w, MIN_ENTITY_SIZE), Math.max(h, MIN_ENTITY_SIZE) ); + EntityLayout node = entitiesToLayout[index++]; + if (resize && node.isResizable()) + node.setSize(Math.max(childrenWidth, MIN_ENTITY_SIZE), Math.max(childrenHeight, MIN_ENTITY_SIZE)); + DisplayIndependentDimension size = node.getSize(); + double xmove = bounds.x + j * colWidth + offsetX + size.width / 2; + double ymove = bounds.y + i * rowHeight + offsetY + size.height / 2; + if (node.isMovable()) + node.setLocation(xmove, ymove); } } - fireProgressEvent (2 + i, totalProgress); - } - updateLayoutLocations(entitiesToLayout); - fireProgressEvent (totalProgress, totalProgress); - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - + } } /** * Calculates and returns an array containing the number of columns, followed by the number of rows */ - protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { - if (getEntityAspectRatio() == 1.0) { + protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { + if (aspectRatio == 1.0) { return calculateNumberOfRowsAndCols_square (numChildren, boundX, boundY, boundWidth, boundHeight); } else { return calculateNumberOfRowsAndCols_rectangular (numChildren); } } - + protected int[] calculateNumberOfRowsAndCols_square (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { int rows = Math.max (1, (int) Math.sqrt(numChildren * boundHeight/boundWidth)); int cols = Math.max (1, (int) Math.sqrt(numChildren * boundWidth/boundHeight)); @@ -188,46 +152,68 @@ int[] result = {cols, rows}; return result; } - + protected int [] calculateNumberOfRowsAndCols_rectangular (int numChildren) { int rows = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); int cols = Math.max (1, (int)Math.ceil(Math.sqrt(numChildren))); int[] result = {cols, rows}; return result; } - - + protected double [] calculateNodeSize (double colWidth, double rowHeight) { double childW = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*colWidth); double childH = Math.max (MIN_ENTITY_SIZE, PADDING_PERCENTAGE*(rowHeight - rowPadding)); double whRatio = colWidth/rowHeight; - if (whRatio < getEntityAspectRatio()) { - childH = childW/getEntityAspectRatio(); + if (whRatio < aspectRatio) { + childH = childW / aspectRatio; } else { - childW = childH*getEntityAspectRatio(); + childW = childH * aspectRatio; } double [] result = {childW, childH}; return result; } - + /** - * Increases the padding between rows in the grid - * @param rowPadding Value will not be set if less than 0. + * Sets the padding between rows in the grid + * + * @param rowPadding + * padding - should be greater than or equal to 0 */ public void setRowPadding(int rowPadding) { - if (rowPadding < 0 ) { - return; + if (rowPadding >= 0) { + this.rowPadding = rowPadding; } - this.rowPadding = rowPadding; } - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) return false; - else if ( asynchronous && !continueous ) return true; - else if ( !asynchronous && continueous ) return false; - else if ( !asynchronous && !continueous ) return true; - - return false; + /** + * Sets the preferred aspect ratio for layout entities. The default aspect + * ratio is 1. + * + * @param aspectRatio + * aspect ratio - should be greater than 0 + */ + public void setAspectRatio(double aspectRatio) { + if (aspectRatio > 0) { + this.aspectRatio = aspectRatio; + } + } + + /** + * + * @return true if this algorithm is set to resize elements + */ + public boolean isResizing() { + return resize; + } + + /** + * + * @param resizing + * true if this algorithm should resize elements (default is + * false) + */ + public void setResizing(boolean resizing) { + resize = resizing; } } Index: src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/HorizontalTreeLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; - -import org.eclipse.zest.layouts.LayoutStyles; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; - - - - -/** - * A simple algorithm to arrange graph nodes in a layered horizontal tree-like layout. - * @see TreeLayoutAlgorithm - * - * @version 1.0 - * @author Rob Lintern - */ -public class HorizontalTreeLayoutAlgorithm extends TreeLayoutAlgorithm { - - - /** - * Creates a horizontal tree layout with no style - */ - public HorizontalTreeLayoutAlgorithm() { - this( LayoutStyles.NONE ); - } - - /** - * - */ - public HorizontalTreeLayoutAlgorithm( int styles ) { - super( styles ); - } - - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // NOTE: width and height are swtiched here when calling super method - super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y, height, width); - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - // swap x->y and width->height - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode entity = entitiesToLayout[i]; - entity.setInternalLocation(entity.getInternalY(), entity.getInternalX() ); - entity.setInternalSize( entity.getInternalWidth(), entity.getInternalHeight() ); - } - super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider); - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) return false; - else if ( asynchronous && !continueous ) return true; - else if ( !asynchronous && continueous ) return false; - else if ( !asynchronous && !continueous ) return true; - - return false; - } - -} Index: src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java,v retrieving revision 1.12 diff -u -r1.12 SpringLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.12 +++ src/org/eclipse/zest/layouts/algorithms/SpringLayoutAlgorithm.java 10 Jul 2009 18:49:26 -0000 @@ -1,23 +1,25 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of the Eclipse Public License v1.0 which + * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html - * + * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ package org.eclipse.zest.layouts.algorithms; -import java.util.Date; import java.util.HashMap; -import java.util.Map; -import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.ConnectionLayout; +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; /** * The SpringLayoutAlgorithm has its own data repository and relation @@ -37,772 +39,522 @@ * @author Ian Bull * @author Casey Best (version 1.0 by Jingwei Wu/Rob Lintern) */ -public class SpringLayoutAlgorithm extends ContinuousLayoutAlgorithm { +public class SpringLayoutAlgorithm implements LayoutAlgorithm { - private final static boolean DEFAULT_ANCHOR = false; + /** + * The default value for the spring layout number of interations. + */ + public static final int DEFAULT_SPRING_ITERATIONS = 1000; + + /** + * the default value for the time algorithm runs. + */ + public static final long MAX_SPRING_TIME = 10000; + + /** + * The default value for positioning nodes randomly. + */ + public static final boolean DEFAULT_SPRING_RANDOM = true; + + /** + * The default value for the spring layout move-control. + */ + public static final double DEFAULT_SPRING_MOVE = 1.0f; + + /** + * The default value for the spring layout strain-control. + */ + public static final double DEFAULT_SPRING_STRAIN = 1.0f; + + /** + * The default value for the spring layout length-control. + */ + public static final double DEFAULT_SPRING_LENGTH = 1.0f; + + /** + * The default value for the spring layout gravitation-control. + */ + public static final double DEFAULT_SPRING_GRAVITATION = 1.0f; + + /** + * Minimum distance considered between nodes + */ + protected static final double MIN_DISTANCE = 0.001d; + + /** + * An arbitrarily small value in mathematics. + */ + protected static final double EPSILON = 0.001d; + + /** + * The variable can be customized to set the number of iterations used. + */ + private int sprIterations = DEFAULT_SPRING_ITERATIONS; + + /** + * This variable can be customized to set the max number of MS the algorithm + * should run + */ + private long maxTimeMS = MAX_SPRING_TIME; + + /** + * The variable can be customized to set whether or not the spring layout + * nodes are positioned randomly before beginning iterations. + */ + private boolean sprRandom = DEFAULT_SPRING_RANDOM; + + /** + * The variable can be customized to set the spring layout move-control. + */ + private double sprMove = DEFAULT_SPRING_MOVE; + + /** + * The variable can be customized to set the spring layout strain-control. + */ + private double sprStrain = DEFAULT_SPRING_STRAIN; + + /** + * The variable can be customized to set the spring layout length-control. + */ + private double sprLength = DEFAULT_SPRING_LENGTH; + + /** + * The variable can be customized to set the spring layout + * gravitation-control. + */ + private double sprGravitation = DEFAULT_SPRING_GRAVITATION; + + /** + * Variable indicating whether the algorithm should resize elements. + */ + private boolean resize = false; + + private int iteration; + + private double[][] srcDestToSumOfWeights; + + private EntityLayout[] entities; + + private double[] forcesX, forcesY; + + private double[] locationsX, locationsY; + + private double[] sizeW, sizeH; + + private DisplayIndependentRectangle bounds; + + private double boundsScale = 0.2; + + private LayoutContext context; + + public void applyLayout() { + initLayout(); + while (performAnotherNonContinuousIteration()) { + computeOneIteration(); + } + saveLocations(); + if (resize) + AlgorithmHelper.maximizeSizes(entities); + AlgorithmHelper.fitWithinBounds(entities, bounds, resize); + } - /** - * The default value for the spring layout number of interations. - */ - public static final int DEFAULT_SPRING_ITERATIONS = 1000; - - /** - * the default value for the time algorithm runs. - */ - public static final long MAX_SPRING_TIME = 10000; - - /** - * The default value for positioning nodes randomly. - */ - public static final boolean DEFAULT_SPRING_RANDOM = true; - - /** - * The default value for ignoring unconnected nodes. - */ - public static final boolean DEFAULT_SPRING_IGNORE_UNCON = true; - - /** - * The default value for separating connected components. - */ - public static final boolean DEFAULT_SPRING_SEPARATE_COMPONENTS = true; - - /** - * The default value for the spring layout move-control. - */ - public static final double DEFAULT_SPRING_MOVE = 1.0f; - - /** - * The default value for the spring layout strain-control. - */ - public static final double DEFAULT_SPRING_STRAIN = 1.0f; - - /** - * The default value for the spring layout length-control. - */ - public static final double DEFAULT_SPRING_LENGTH = 1.0f; - - /** - * The default value for the spring layout gravitation-control. - */ - public static final double DEFAULT_SPRING_GRAVITATION = 1.0f; - - /** - * The variable can be customized to set the number of iterations used. - */ - private static int sprIterations = DEFAULT_SPRING_ITERATIONS; - - /** - * This variable can be customized to set the max number of MS the algorithm - * should run - */ - private static long maxTimeMS = MAX_SPRING_TIME; - - /** - * The variable can be customized to set whether or not the spring layout - * nodes are positioned randomly before beginning iterations. - */ - private static boolean sprRandom = DEFAULT_SPRING_RANDOM; - - /** - * Minimum distance considered between nodes - */ - protected static final double MIN_DISTANCE = 0.001d; - - /** - * An arbitrarily small value in mathematics. - */ - protected static final double EPSILON = 0.001d; - - /** - * The variable can be customerized to set the spring layout move-control. - */ - private static double sprMove = DEFAULT_SPRING_MOVE; - - /** - * The variable can be customized to set the spring layout strain-control. - */ - private static double sprStrain = DEFAULT_SPRING_STRAIN; - - /** - * The variable can be customized to set the spring layout length-control. - */ - private static double sprLength = DEFAULT_SPRING_LENGTH; - - /** - * The variable can be customized to set the spring layout - * gravitation-control. - */ - private static double sprGravitation = DEFAULT_SPRING_GRAVITATION; - - /** - * The largest movement of all vertices that has occured in the most recent - * iteration. - */ - private double largestMovement = 0; - - - /** - * Maps a src and dest object to the number of relations between them. Key - * is src.toString() + dest.toString(), value is an Integer - */ - private Map srcDestToNumRelsMap; - - /** - * Maps a src and dest object to the average weight of the relations between - * them. Key is src.toString() + dest.toString(), value is a Double - */ - private Map srcDestToRelsAvgWeightMap; - - /** - * Maps a relationship type to a weight. Key is a string, value is a Double - */ - private static Map relTypeToWeightMap = new HashMap(); - - private int iteration; - - private int[][] srcDestToNumRels; - - private double[][] srcDestToRelsAvgWeight; - - private double[] tempLocationsX; - - private double[] tempLocationsY; - - private double[] forcesX; - - private double[] forcesY; - - private boolean[] anchors; - - private DisplayIndependentRectangle bounds = null; - - Date date = null; - - /** - * Constructor. - */ - public SpringLayoutAlgorithm( int styles ) { - super( styles ); - srcDestToNumRelsMap = new HashMap(); - srcDestToRelsAvgWeightMap = new HashMap(); - date = new Date(); - } - - - /** - * Creates a sprint layout algoirthm with no style - * - */ - public SpringLayoutAlgorithm() { - this( LayoutStyles.NONE ); - } - - public void setLayoutArea(double x, double y, double width, double height) { - bounds = new DisplayIndependentRectangle(x,y,width,height); - } - - /** - * Sets the spring layout move-control. - * - * @param move - * The move-control value. - */ - public void setSpringMove(double move) { - sprMove = move; - } - - /** - * Returns the move-control value of this SpringLayoutAlgorithm in - * double presion. - * - * @return The move-control value. - */ - public double getSpringMove() { - return sprMove; - } - - /** - * Sets the spring layout strain-control. - * - * @param strain - * The strain-control value. - */ - public void setSpringStrain(double strain) { - sprStrain = strain; - } - - /** - * Returns the strain-control value of this SpringLayoutAlgorithm in - * double presion. - * - * @return The strain-control value. - */ - public double getSpringStrain() { - return sprStrain; - } - - /** - * Sets the spring layout length-control. - * - * @param length - * The length-control value. - */ - public void setSpringLength(double length) { - sprLength = length; - } - - /** - * Gets the max time this algorithm will run for - * - * @return - */ - public long getSpringTimeout() { - return maxTimeMS; - } - - /** - * Sets the spring timeout - * - * @param timeout - */ - public void setSpringTimeout(long timeout) { - maxTimeMS = timeout; - } - - /** - * Returns the length-control value of this SpringLayoutAlgorithm in - * double presion. - * - * @return The length-control value. - */ - public double getSpringLength() { - return sprLength; - } - - /** - * Sets the spring layout gravitation-control. - * - * @param gravitation - * The gravitation-control value. - */ - public void setSpringGravitation(double gravitation) { - sprGravitation = gravitation; - } - - /** - * Returns the gravitation-control value of this SpringLayoutAlgorithm - * in double presion. - * - * @return The gravitation-control value. - */ - public double getSpringGravitation() { - return sprGravitation; - } - - /** - * Sets the number of iterations to be used. - * - * @param gravitation - * The number of iterations. - */ - public void setIterations(int iterations) { - sprIterations = iterations; - } - - /** - * Returns the number of iterations to be used. - * - * @return The number of iterations. - */ - public int getIterations() { - return sprIterations; - } - - /** - * Sets whether or not this SpringLayoutAlgorithm will layout the - * nodes randomly before beginning iterations. - * - * @param random - * The random placement value. - */ - public void setRandom(boolean random) { - sprRandom = random; - } - - /** - * Returns whether or not this SpringLayoutAlgorithm will layout the - * nodes randomly before beginning iterations. - */ - public boolean getRandom() { - return sprRandom; - } - - public void setWeight(String relType, double weight) { - relTypeToWeightMap.put(relType, new Double(weight)); - } - - public double getWeight(String relType) { - Double weight = (Double) relTypeToWeightMap.get(relType); - return (weight == null) ? 1 : weight.doubleValue(); - } - - /** - * Sets the default conditions. - */ - public void setDefaultConditions() { - // sprMove = DEFAULT_SPRING_MOVE; - // sprStrain = DEFAULT_SPRING_STRAIN; - // sprLength = DEFAULT_SPRING_LENGTH; - // sprGravitation = DEFAULT_SPRING_GRAVITATION; - // sprIterations = DEFAULT_SPRING_ITERATIONS; - } - - /** - * Clean up after done - * - * @param entitiesToLayout - */ - private void reset(InternalNode[] entitiesToLayout) { - tempLocationsX = null; - tempLocationsY = null; - forcesX = null; - forcesY = null; - anchors = null; - setDefaultConditions(); - srcDestToNumRelsMap = new HashMap(); - srcDestToRelsAvgWeightMap = new HashMap(); - relTypeToWeightMap = new HashMap(); - } - - private long startTime = 0; - - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // TODO: Filter out any non-wanted entities and relationships - // super.applyLayout(entitiesToLayout, relationshipsToConsider, x, y, - // width, height); - //InternalNode[] a_entitiesToLayout = (InternalNode[]) entitiesToLayout.toArray(new InternalNode[entitiesToLayout.size()]); - bounds = new DisplayIndependentRectangle(x,y,width,height); - tempLocationsX = new double[entitiesToLayout.length]; - tempLocationsY = new double[entitiesToLayout.length]; - forcesX = new double[entitiesToLayout.length]; - forcesY = new double[entitiesToLayout.length]; - anchors = new boolean[entitiesToLayout.length]; - - for (int i = 0; i < entitiesToLayout.length; i++) { - anchors[i] = DEFAULT_ANCHOR; - } - for (int i = 0; i < relationshipsToConsider.length; i++) { - InternalRelationship layoutRelationship = relationshipsToConsider[i]; - addRelation(layoutRelationship); - } - - // do the calculations - preCompute(entitiesToLayout); - startTime = date.getTime(); - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - reset(entitiesToLayout); - } - - /** - * Adds a simple relation between two nodes to the relation repository. - * - * @param layoutRelationship - * The simple relation to be added - * @throws java.lang.NullPointerExcetption - * If sr is null - * @see SimpleRelation - */ - private void addRelation(InternalRelationship layoutRelationship) { - if (layoutRelationship == null) { - throw new IllegalArgumentException("The arguments can not be null!"); - } else { - double weight = layoutRelationship.getWeight(); - weight = (weight <= 0 ? 0.1 : weight); - String key1 = layoutRelationship.getSource().toString() + layoutRelationship.getDestination().toString(); - String key2 = layoutRelationship.getDestination().toString() + layoutRelationship.getSource().toString(); - String[] keys = { key1, key2 }; - for (int i = 0; i < keys.length; i++) { - String key = keys[i]; - Integer count = (Integer) srcDestToNumRelsMap.get(key); - Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key); - if (count == null) { - count = new Integer(1); - avgWeight = new Double(weight); - } else { - int newCount = count.intValue() + 1; - double newAverage = (avgWeight.doubleValue() * count.doubleValue() + weight) / newCount; - avgWeight = new Double(newAverage); - count = new Integer(newCount); - } - srcDestToNumRelsMap.put(key, count); - srcDestToRelsAvgWeightMap.put(key, avgWeight); - } - } - } - - private void preCompute(InternalNode [] entitiesToLayout) { - // count number of relationships between all nodes and the average - // weight between them - srcDestToNumRels = new int[entitiesToLayout.length][entitiesToLayout.length]; - srcDestToRelsAvgWeight = new double[entitiesToLayout.length][entitiesToLayout.length]; - - for (int i = 0; i < entitiesToLayout.length - 1; i++) { - InternalNode layoutEntity1 = entitiesToLayout[i]; - for (int j = i + 1; j < entitiesToLayout.length; j++) { - InternalNode layoutEntity2 = entitiesToLayout[j]; - srcDestToNumRels[i][j] = numRelations(layoutEntity1, layoutEntity2); - srcDestToNumRels[i][j] += numRelations(layoutEntity2, layoutEntity1); - srcDestToRelsAvgWeight[i][j] = avgWeight(layoutEntity1, layoutEntity2); - } - } - - if (sprRandom) - placeRandomly(entitiesToLayout); // put vertices in random places - else - convertToUnitCoordinates(entitiesToLayout); - - iteration = 1; - largestMovement = Double.MAX_VALUE; - } - - // TODO: This is a complete Clone! (and not in a good way) - protected DisplayIndependentRectangle getLayoutBoundsTemp(InternalNode [] entitiesToLayout, boolean includeNodeSize) { - double rightSide = Double.MIN_VALUE; - double bottomSide = Double.MIN_VALUE; - double leftSide = Double.MAX_VALUE; - double topSide = Double.MAX_VALUE; - for (int i = 0; i < entitiesToLayout.length; i++) { - double x = tempLocationsX[i]; - double y = tempLocationsY[i]; - - leftSide = Math.min(x, leftSide); - topSide = Math.min(y, topSide); - rightSide = Math.max(x, rightSide); - bottomSide = Math.max(y, bottomSide); - - } - return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); - } - - protected void convertNodePositionsBack(int i, InternalNode entityToConvert, double px, double py, double screenWidth, double screenHeight, DisplayIndependentRectangle layoutBounds) { - - // If the node selected is outside the screen, map it to the boarder - if ( px > screenWidth ) px = screenWidth; - if ( py > screenHeight ) py = screenHeight; - - if ( px < 0 ) px = 1; - if ( py < 0 ) py = 1; - - double x = (px / screenWidth) * layoutBounds.width + layoutBounds.x; - double y = (py / screenHeight) * layoutBounds.height + layoutBounds.y; - - tempLocationsX[i] = x; - tempLocationsY[i] = y; - //setTempLocation(entityToConvert, new DisplayIndependentPoint(x, y)); - - if (entityToConvert.getInternalX() < 0) { - // System.out.println("We have nodes less than 0 here!"); - } - - } - - private void checkPreferredLocation(InternalNode [] entitiesToLayout, DisplayIndependentRectangle realBounds) { - // use 10% for the border - 5% on each side - double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; - DisplayIndependentRectangle screenBounds = new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth); - - DisplayIndependentRectangle layoutBounds = getLayoutBoundsTemp(entitiesToLayout, false); - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode layoutEntity = entitiesToLayout[i]; - if (layoutEntity.hasPreferredLocation()) { - convertNodePositionsBack(i, layoutEntity, layoutEntity.getPreferredX(), layoutEntity.getPreferredY(), screenBounds.width, screenBounds.height, layoutBounds); - } - } - } - - /** - * Scales the current iteration counter based on how long the algorithm has - * been running for. You can set the MaxTime in maxTimeMS! - */ - private void setSprIterationsBasedOnTime() { - if (maxTimeMS <= 0) - return; - - long currentTime = date.getTime(); - double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS)); - int currentIteration = (int) (fractionComplete * sprIterations); - if (currentIteration > iteration) { - iteration = currentIteration; - } - - } - - protected boolean performAnotherNonContinuousIteration() { - setSprIterationsBasedOnTime(); - if (iteration <= sprIterations && largestMovement >= sprMove) - return true; - else - return false; - } - - protected int getCurrentLayoutStep() { - return iteration; - } - - protected int getTotalNumberOfLayoutSteps() { - return sprIterations; - } - - protected void computeOneIteration(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - if ( bounds == null ) - bounds = new DisplayIndependentRectangle(x,y,width,height); - checkPreferredLocation(entitiesToLayout, bounds ); - computeForces(entitiesToLayout); - largestMovement = Double.MAX_VALUE; - computePositions(entitiesToLayout); - - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode layoutEntity = entitiesToLayout[i]; - layoutEntity.setInternalLocation(tempLocationsX[i], tempLocationsY[i]); - } - - defaultFitWithinBounds(entitiesToLayout, bounds); - - iteration++; - } - - /** - * Puts vertices in random places, all between (0,0) and (1,1). - */ - public void placeRandomly(InternalNode[] entitiesToLayout) { - // If only one node in the data repository, put it in the middle - if (entitiesToLayout.length == 1) { - // If only one node in the data repository, put it in the middle - tempLocationsX[0] = 0.5; - tempLocationsY[0] = 0.5; - } else { - for (int i = 0; i < entitiesToLayout.length; i++) { - if (i == 0) { - tempLocationsX[i] = 0.0; - tempLocationsY[i] = 0.0; - } else if (i == 1) { - tempLocationsX[i] = 1.0; - tempLocationsY[i] = 1.0; - } else { - tempLocationsX[i] = Math.random(); - tempLocationsY[i] = Math.random(); - } - } - } - } - - // ///////////////////////////////////////////////////////////////// - // /// Protected Methods ///// - // ///////////////////////////////////////////////////////////////// - - /** - * Computes the force for each node in this SpringLayoutAlgorithm. The - * computed force will be stored in the data repository - */ - protected void computeForces(InternalNode[] entitiesToLayout) { - - // initialize all forces to zero - for (int i = 0; i < entitiesToLayout.length; i++) { - forcesX[i] = 0.0; - forcesY[i] = 0.0; - } - - // TODO: Again really really slow! - - for (int i = 0; i < entitiesToLayout.length - 1; i++) { - InternalNode sourceEntity = entitiesToLayout[i]; - - double srcLocationX = tempLocationsX[i]; - double srcLocationY = tempLocationsY[i]; - double fx = forcesX[i]; // force in x direction - double fy = forcesY[i]; // force in y direction - - - for (int j = i + 1; j < entitiesToLayout.length; j++) { - InternalNode destinationEntity = entitiesToLayout[j]; - - if (!destinationEntity.equals(sourceEntity)) { - double destLocationX = tempLocationsX[j]; - double destLocationY = tempLocationsY[j]; - double dx = srcLocationX - destLocationX; - double dy = srcLocationY- destLocationY; - double distance = Math.sqrt(dx * dx + dy * dy); - double distance_sq = distance * distance; - // make sure distance and distance squared not too small - distance = Math.max(MIN_DISTANCE, distance); - - // If there are relationships between srcObj and destObj - // then decrease force on srcObj (a pull) in direction of destObj - // If no relation between srcObj and destObj then increase - // force on srcObj (a push) from direction of destObj. - int numRels = srcDestToNumRels[i][j]; - double avgWeight = srcDestToRelsAvgWeight[i][j]; - if (numRels > 0) { - // nodes are pulled towards each other - double f = sprStrain * Math.log(distance / sprLength) * numRels * avgWeight; - - fx = fx - (f * dx / distance); - fy = fy - (f * dy / distance); - - } else { - // nodes are repelled from each other - //double f = Math.min(100, sprGravitation / (distance*distance)); - double f = sprGravitation / (distance_sq); - fx = fx + (f * dx / distance); - fy = fy + (f * dy / distance); - } - - // According to Newton, "for every action, there is an equal - // and opposite reaction." - // so give the dest an opposite force - forcesX[j] = forcesX[j] - fx; - forcesY[j] = forcesY[j] - fy; - } - } - - /* - * //make sure forces aren't too big if (fx > 0 ) fx = Math.min(fx, - * 10*sprMove); else fx = Math.max(fx, -10*sprMove); if (fy > 0) fy = - * Math.min(fy, 10*sprMove); else fy = Math.max(fy, -10*sprMove); - */ - forcesX[i] = fx; - forcesY[i] = fy; - // Remove the src object from the list of destinations since - // we've already calculated the force from it on all other - // objects. - // dests.remove(srcObj); - - } - } - - /** - * Computes the position for each node in this SpringLayoutAlgorithm. - * The computed position will be stored in the data repository. position = - * position + sprMove * force - */ - protected void computePositions(InternalNode[] entitiesToLayout) { - for (int i = 0; i < entitiesToLayout.length; i++) { - if (!anchors[i] || entitiesToLayout[i].hasPreferredLocation() ) { - double oldX = tempLocationsX[i]; - double oldY = tempLocationsY[i]; - double deltaX = sprMove * forcesX[i]; - double deltaY = sprMove * forcesY[i]; - - // constrain movement, so that nodes don't shoot way off to the edge - double maxMovement = 0.2d * sprMove; - if (deltaX >= 0) { - deltaX = Math.min(deltaX, maxMovement); - } else { - deltaX = Math.max(deltaX, -maxMovement); - } - if (deltaY >= 0) { - deltaY = Math.min(deltaY, maxMovement); - } else { - deltaY = Math.max(deltaY, -maxMovement); - } - - - largestMovement = Math.max(largestMovement, Math.abs(deltaX)); - largestMovement = Math.max(largestMovement, Math.abs(deltaY)); - - double newX = oldX + deltaX; - double newY = oldY + deltaY; - tempLocationsX[i] = newX; - tempLocationsY[i] = newY; - } - - } - - } - - /** - * Converts the position for each node in this SpringLayoutAlgorithm - * to unit coordinates in double precision. The computed positions will be - * still stored in the data repository. - */ - protected void convertToUnitCoordinates(InternalNode[] entitiesToLayout) { - double minX = Double.MAX_VALUE; - double maxX = Double.MIN_VALUE; - double minY = Double.MAX_VALUE; - double maxY = Double.MIN_VALUE; - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode layoutEntity = entitiesToLayout[i]; - minX = Math.min(minX, layoutEntity.getInternalX()); - minY = Math.min(minY, layoutEntity.getInternalY()); - maxX = Math.max(maxX, layoutEntity.getInternalX()); - maxY = Math.max(maxY, layoutEntity.getInternalY()); - } - - double spanX = maxX - minX; - double spanY = maxY - minY; - double maxSpan = Math.max(spanX, spanY); - - if (maxSpan > EPSILON) { - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode layoutEntity = entitiesToLayout[i]; - double x = (layoutEntity.getInternalX() - minX) / spanX; - double y = (layoutEntity.getInternalY() - minY) / spanY; - tempLocationsX[i] = x; - tempLocationsY[i] = y; - } - } else { - placeRandomly(entitiesToLayout); - } - } - - /** - * Examines the number of specified relation between the src - * and the dest that exist in this - * SpringLayoutAlgorithm's relation repository. - * - * @param src - * The source part of the relaton to be examined. - * @param dest - * The destination part of the relation to be examined. - * @return The number of relations between src and dest. - */ - private int numRelations(Object src, Object dest) { - String key = src.toString() + dest.toString(); - Integer count = (Integer) srcDestToNumRelsMap.get(key); - int intCount = (count == null) ? 0 : count.intValue(); - return intCount; - } - - /** - * Returns the average weight between a src and dest object. - * - * @param src - * @param dest - * @return The average weight between the given src and dest nodes - */ - private double avgWeight(Object src, Object dest) { - String key = src.toString() + dest.toString(); - Double avgWeight = (Double) srcDestToRelsAvgWeightMap.get(key); - double doubleWeight = (avgWeight == null) ? 1 : avgWeight.doubleValue(); - return doubleWeight; - } - - - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if (asynchronous && continueous) - return true; - else if (asynchronous && !continueous) - return true; - else if (!asynchronous && continueous) - return false; - else if (!asynchronous && !continueous) - return true; + public void setLayoutContext(LayoutContext context) { + this.context = context; + } - return false; - } + public void performOneIteration() { + if (iteration == 0) { + entities = context.getEntities(); + loadLocations(); + initLayout(); + } + bounds = context.getBounds(); + computeOneIteration(); + saveLocations(); + context.flushChanges(false); + } + /** + * + * @return true if this algorithm is set to resize elements + */ + public boolean isResizing() { + return resize; + } -} + /** + * + * @param resizing + * true if this algorithm should resize elements (default is + * false) + */ + public void setResizing(boolean resizing) { + resize = resizing; + } + + /** + * Sets the spring layout move-control. + * + * @param move + * The move-control value. + */ + public void setSpringMove(double move) { + sprMove = move; + } + + /** + * Returns the move-control value of this SpringLayoutAlgorithm in + * double presion. + * + * @return The move-control value. + */ + public double getSpringMove() { + return sprMove; + } + + /** + * Sets the spring layout strain-control. + * + * @param strain + * The strain-control value. + */ + public void setSpringStrain(double strain) { + sprStrain = strain; + } + + /** + * Returns the strain-control value of this SpringLayoutAlgorithm in + * double presion. + * + * @return The strain-control value. + */ + public double getSpringStrain() { + return sprStrain; + } + + /** + * Sets the spring layout length-control. + * + * @param length + * The length-control value. + */ + public void setSpringLength(double length) { + sprLength = length; + } + + /** + * Gets the max time this algorithm will run for + * + * @return + */ + public long getSpringTimeout() { + return maxTimeMS; + } + + /** + * Sets the spring timeout + * + * @param timeout + */ + public void setSpringTimeout(long timeout) { + maxTimeMS = timeout; + } + + /** + * Returns the length-control value of this SpringLayoutAlgorithm in + * double presion. + * + * @return The length-control value. + */ + public double getSpringLength() { + return sprLength; + } + + /** + * Sets the spring layout gravitation-control. + * + * @param gravitation + * The gravitation-control value. + */ + public void setSpringGravitation(double gravitation) { + sprGravitation = gravitation; + } + + /** + * Returns the gravitation-control value of this SpringLayoutAlgorithm + * in double presion. + * + * @return The gravitation-control value. + */ + public double getSpringGravitation() { + return sprGravitation; + } + + /** + * Sets the number of iterations to be used. + * + * @param gravitation + * The number of iterations. + */ + public void setIterations(int iterations) { + sprIterations = iterations; + } + + /** + * Returns the number of iterations to be used. + * + * @return The number of iterations. + */ + public int getIterations() { + return sprIterations; + } + + /** + * Sets whether or not this SpringLayoutAlgorithm will layout the + * nodes randomly before beginning iterations. + * + * @param random + * The random placement value. + */ + public void setRandom(boolean random) { + sprRandom = random; + } + + /** + * Returns whether or not this SpringLayoutAlgorithm will layout the + * nodes randomly before beginning iterations. + */ + public boolean getRandom() { + return sprRandom; + } + + private long startTime = 0; + + private void initLayout() { + entities = context.getEntities(); + bounds = context.getBounds(); + loadLocations(); + + srcDestToSumOfWeights = new double[entities.length][entities.length]; + HashMap entityToPosition = new HashMap(); + for (int i = 0; i < entities.length; i++) { + entityToPosition.put(entities[i], new Integer(i)); + } + + ConnectionLayout[] connections = context.getConnections(); + for (int i = 0; i < connections.length; i++) { + ConnectionLayout connection = connections[i]; + Integer source = (Integer) entityToPosition.get(connection.getSource()); + Integer target = (Integer) entityToPosition.get(connection.getTarget()); + if (source == null || target == null) + continue; + double weight = connection.getWeight(); + weight = (weight <= 0 ? 0.1 : weight); + srcDestToSumOfWeights[source.intValue()][target.intValue()] += weight; + srcDestToSumOfWeights[target.intValue()][source.intValue()] += weight; + } + + if (sprRandom) + placeRandomly(); // put vertices in random places + + iteration = 1; + + startTime = System.currentTimeMillis(); + } + + private void loadLocations() { + if (locationsX == null || locationsX.length != entities.length) { + int length = entities.length; + locationsX = new double[length]; + locationsY = new double[length]; + sizeW = new double[length]; + sizeH = new double[length]; + forcesX = new double[length]; + forcesY = new double[length]; + } + for (int i = 0; i < entities.length; i++) { + DisplayIndependentPoint location = entities[i].getLocation(); + locationsX[i] = location.x; + locationsY[i] = location.y; + DisplayIndependentDimension size = entities[i].getSize(); + sizeW[i] = size.width; + sizeH[i] = size.height; + } + } + + private void saveLocations() { + for (int i = 0; i < entities.length; i++) { + entities[i].setLocation(locationsX[i], locationsY[i]); + } + } + + /** + * Scales the current iteration counter based on how long the algorithm has + * been running for. You can set the MaxTime in maxTimeMS! + */ + private void setSprIterationsBasedOnTime() { + if (maxTimeMS <= 0) + return; + + long currentTime = System.currentTimeMillis(); + double fractionComplete = (double) ((double) (currentTime - startTime) / ((double) maxTimeMS)); + int currentIteration = (int) (fractionComplete * sprIterations); + if (currentIteration > iteration) { + iteration = currentIteration; + } + + } + + protected boolean performAnotherNonContinuousIteration() { + setSprIterationsBasedOnTime(); + return (iteration <= sprIterations); + } + + protected int getCurrentLayoutStep() { + return iteration; + } + + protected int getTotalNumberOfLayoutSteps() { + return sprIterations; + } + + protected void computeOneIteration() { + computeForces(); + computePositions(); + DisplayIndependentRectangle currentBounds = getLayoutBounds(); + improveBoundsScale(currentBounds); + moveToCenter(currentBounds); + iteration++; + } + + /** + * Puts vertices in random places, all between (0,0) and (1,1). + */ + public void placeRandomly() { + // If only one node in the data repository, put it in the middle + if (locationsX.length == 1) { + // If only one node in the data repository, put it in the middle + locationsX[0] = bounds.x + 0.5 * bounds.width; + locationsY[0] = bounds.y + 0.5 * bounds.height; + } else { + locationsX[0] = bounds.x; + locationsY[0] = bounds.y; + locationsX[1] = bounds.x + bounds.width; + locationsY[1] = bounds.y + bounds.height; + for (int i = 2; i < locationsX.length; i++) { + locationsX[i] = bounds.x + Math.random() * bounds.width; + locationsY[i] = bounds.y + Math.random() * bounds.height; + } + } + } + + // ///////////////////////////////////////////////////////////////// + // /// Protected Methods ///// + // ///////////////////////////////////////////////////////////////// + + /** + * Computes the force for each node in this SpringLayoutAlgorithm. The + * computed force will be stored in the data repository + */ + protected void computeForces() { + + // initialize all forces to zero + for (int i = 0; i < forcesX.length; i++) { + forcesX[i] = 0.0; + forcesY[i] = 0.0; + } + // TODO: Again really really slow! + + for (int i = 0; i < locationsX.length; i++) { + + for (int j = i + 1; j < locationsX.length; j++) { + double dx = (locationsX[i] - locationsX[j]) / bounds.width / boundsScale; + double dy = (locationsY[i] - locationsY[j]) / bounds.height / boundsScale; + double distance_sq = dx * dx + dy * dy; + // make sure distance and distance squared not too small + distance_sq = Math.max(MIN_DISTANCE * MIN_DISTANCE, distance_sq); + double distance = Math.sqrt(distance_sq); + + // If there are relationships between srcObj and destObj + // then decrease force on srcObj (a pull) in direction of destObj + // If no relation between srcObj and destObj then increase + // force on srcObj (a push) from direction of destObj. + double sumOfWeights = srcDestToSumOfWeights[i][j]; + + double f; + if (sumOfWeights > 0) { + // nodes are pulled towards each other + f = -sprStrain * Math.log(distance / sprLength) * sumOfWeights; + } else { + // nodes are repelled from each other + f = sprGravitation / (distance_sq); + } + double dfx = f * dx / distance; + double dfy = f * dy / distance; + + forcesX[i] += dfx; + forcesY[i] += dfy; + + forcesX[j] -= dfx; + forcesY[j] -= dfy; + } + } + } + /** + * Computes the position for each node in this SpringLayoutAlgorithm. + * The computed position will be stored in the data repository. position = + * position + sprMove * force + */ + protected void computePositions() { + for (int i = 0; i < entities.length; i++) { + if (entities[i].isMovable()) { + double deltaX = sprMove * forcesX[i]; + double deltaY = sprMove * forcesY[i]; + + // constrain movement, so that nodes don't shoot way off to the + // edge + double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + double maxMovement = 0.2d * sprMove; + if (dist > maxMovement) { + deltaX *= maxMovement / dist; + deltaY *= maxMovement / dist; + } + + locationsX[i] += deltaX * bounds.width * boundsScale; + locationsY[i] += deltaY * bounds.height * boundsScale; + } + } + } + + private DisplayIndependentRectangle getLayoutBounds() { + double minX, maxX, minY, maxY; + minX = minY = Double.POSITIVE_INFINITY; + maxX = maxY = Double.NEGATIVE_INFINITY; + + for (int i = 0; i < locationsX.length; i++) { + maxX = Math.max(maxX, locationsX[i] + sizeW[i] / 2); + minX = Math.min(minX, locationsX[i] - sizeW[i] / 2); + maxY = Math.max(maxY, locationsY[i] + sizeH[i] / 2); + minY = Math.min(minY, locationsY[i] - sizeH[i] / 2); + } + return new DisplayIndependentRectangle(minX, minY, maxX - minX, maxY - minY); + } + + private void improveBoundsScale(DisplayIndependentRectangle currentBounds) { + double boundaryProportion = Math.max(currentBounds.width / bounds.width, currentBounds.height / bounds.height); + if (boundaryProportion < 0.9) + boundsScale *= 1.01; + if (boundaryProportion > 1) + boundsScale /= 1.01; + } + + private void moveToCenter(DisplayIndependentRectangle currentBounds) { + double moveX = (currentBounds.x + currentBounds.width / 2) - (bounds.x + bounds.width / 2); + double moveY = (currentBounds.y + currentBounds.height / 2) - (bounds.y + bounds.height / 2); + for (int i = 0; i < locationsX.length; i++) { + locationsX[i] -= moveX; + locationsY[i] -= moveY; + } + } +} Index: src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/AbstractLayoutAlgorithm.java 12 Jan 2008 01:34:29 -0000 1.24 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,1035 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ - -package org.eclipse.zest.layouts.algorithms; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Collection; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.zest.layouts.Filter; -import org.eclipse.zest.layouts.InvalidLayoutConfiguration; -import org.eclipse.zest.layouts.LayoutAlgorithm; -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutItem; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.LayoutStyles; -import org.eclipse.zest.layouts.Stoppable; -import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; -import org.eclipse.zest.layouts.dataStructures.BendPoint; -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; -import org.eclipse.zest.layouts.progress.ProgressEvent; -import org.eclipse.zest.layouts.progress.ProgressListener; - -/** - * Handles common elements in all layout algorithms - * [irbull] Refactored into a template pattern. ApplyLayout now delegates the - * task to ApplyLayoutInternal - * - * [irbull] Included asynchronous layouts - * - * @version 1.0 - * @author Casey Best - * @author Ian Bull - * @author Chris Callendar - * @author Rob Lintern - * @author Chris Bennett - */ -public abstract class AbstractLayoutAlgorithm implements LayoutAlgorithm, Stoppable { - - public void removeRelationships(Collection collection) { - - } - - public final static int MIN_ENTITY_SIZE = 5; - private final static int MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS = 1; - - private Thread creationThread = null; - protected Comparator comparator; - protected Filter filter; - private List progressListeners; - private Calendar lastProgressEventFired; - private double widthToHeightRatio; - - class InternalComparator implements Comparator { - Comparator externalComparator = null; - - public InternalComparator(Comparator externalComparator) { - this.externalComparator = externalComparator; - } - - public int compare(Object o1, Object o2) { - InternalNode internalNode1 = (InternalNode) o1; - InternalNode internalNode2 = (InternalNode) o2; - - return this.externalComparator.compare(internalNode1.getLayoutEntity(), internalNode2.getLayoutEntity()); - } - - } - - /* - * Internal Nodes. - */ - private InternalNode[] internalNodes; - private InternalRelationship[] internalRelationships; - private double internalX; - private double internalY; - private double internalWidth; - private double internalHeight; - protected boolean internalContinuous; - protected boolean internalAsynchronous; - - /* - * A queue of entities and relationships to add or remove. Each layout - * algorithm should check these and update their internal lists. - */ - - /** A list of LayoutEntity objects to be removed from the layout. */ - private List entitiesToRemove; - /** A list of LayoutRelationship objects to be removed. */ - private List relationshipsToRemove; - /** A list of LayoutEntity objects to be added to the layout. */ - private List entitiesToAdd; - /** A list of LayoutRelationship objects to be added. */ - private List relationshipsToAdd; - - //protected boolean cancelled = false; - - protected boolean layoutStopped = true; - - protected int layout_styles = 0; - - // Child classes can set to false to retain node shapes and sizes - protected boolean resizeEntitiesAfterLayout = true; - - /** - * Initializes the abstract layout algorithm. - * @see LayoutStyles - */ - public AbstractLayoutAlgorithm(int styles) { - this.creationThread = Thread.currentThread(); - this.progressListeners = new ArrayList(); - this.lastProgressEventFired = Calendar.getInstance(); - this.widthToHeightRatio = 1.0; - - this.entitiesToRemove = new ArrayList(); - this.relationshipsToRemove = new ArrayList(); - this.entitiesToAdd = new ArrayList(); - this.relationshipsToAdd = new ArrayList(); - this.layout_styles = styles; - } - - /** - * Queues up the given entity (if it isn't in the list) to be added to the algorithm. - * @param entity - */ - public void addEntity(LayoutEntity entity) { - if ((entity != null) && !entitiesToAdd.contains(entity)) { - entitiesToAdd.add(entity); - } - } - - /** - * Queues up the given relationshp (if it isn't in the list) to be added to the algorithm. - * @param relationship - */ - public void addRelationship(LayoutRelationship relationship) { - if ((relationship != null) && !relationshipsToAdd.contains(relationship)) { - relationshipsToAdd.add(relationship); - } - } - - /** - * Queues up the given entity to be removed from the algorithm next time it runs. - * @param entity The entity to remove - */ - public void removeEntity(LayoutEntity entity) { - if ((entity != null) && !entitiesToRemove.contains(entity)) { - entitiesToRemove.add(entity); - } - } - - /** - * Queues up the given relationship to be removed from the algorithm next time it runs. - * @param relationship The relationship to remove. - */ - public void removeRelationship(LayoutRelationship relationship) { - if ((relationship != null) && !relationshipsToRemove.contains(relationship)) { - relationshipsToRemove.add(relationship); - } - } - - /** - * Queues up all the relationships in the list to be removed. - * @param relationships - */ - public void removeRelationships(List relationships) { - // note we don't check if the relationshipsToRemove contains - // any of the objects in relationships. - relationshipsToRemove.addAll(relationships); - } - - /** - * Sets the current layout style. This overwrites all other layout styles. - * Use getStyle to get the current style. - * @param style - */ - public void setStyle(int style) { - this.layout_styles = style; - } - - /** - * Gets the current layout style - * @return - */ - public int getStyle() { - return this.layout_styles; - } - - public abstract void setLayoutArea(double x, double y, double width, double height); - - /** - * Determines if the configuration is valid for this layout - * @param asynchronous - * @param continuous - */ - protected abstract boolean isValidConfiguration(boolean asynchronous, boolean continuous); - - /** - * Apply the layout to the given entities. The entities will be moved and resized based - * on the algorithm. - * - * @param entitiesToLayout Apply the algorithm to these entities - * @param relationshipsToConsider Only consider these relationships when applying the algorithm. - * @param x The left side of the bounds in which the layout can place the entities. - * @param y The top side of the bounds in which the layout can place the entities. - * @param width The width of the bounds in which the layout can place the entities. - * @param height The height of the bounds in which the layout can place the entities. - */ - abstract protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight); - - /** - * Updates the given array of entities checking if any need to be removed or added. - * @param entities the current entities - * @return the updated entities array - */ - protected InternalNode[] updateEntities(InternalNode[] entities) { - if ((entitiesToRemove.size() > 0) || (entitiesToAdd.size() > 0)) { - List internalNodesList = new ArrayList(Arrays.asList(entities)); - - // remove nodes - for (Iterator iter = entitiesToRemove.iterator(); iter.hasNext();) { - LayoutEntity entity = (LayoutEntity) iter.next(); - if (entity.getLayoutInformation() != null) { - internalNodesList.remove(entity.getLayoutInformation()); - } - } - - // Also remove from _internalNodes - ArrayList updatedEntities = new ArrayList(internalNodes.length - entitiesToRemove.size() + entitiesToAdd.size()); - for (int i = 0; i < internalNodes.length; i++) { - InternalNode node = internalNodes[i]; - if (entitiesToRemove.contains(node.getLayoutEntity())) { - entitiesToRemove.remove(node.getLayoutEntity()); - } else { - updatedEntities.add(node); - } - } - entitiesToRemove.clear(); - - // Add any new nodes - LayoutEntity[] entitiesArray = new LayoutEntity[entitiesToAdd.size()]; - entitiesArray = (LayoutEntity[]) entitiesToAdd.toArray(entitiesArray); - InternalNode[] newNodes = createInternalNodes(entitiesArray); - for (int i = 0; i < newNodes.length; i++) { - internalNodesList.add(newNodes[i]); - updatedEntities.add(newNodes[i]); - } - entitiesToAdd.clear(); - - entities = new InternalNode[internalNodesList.size()]; - entities = (InternalNode[]) internalNodesList.toArray(entities); - - internalNodes = new InternalNode[updatedEntities.size()]; - internalNodes = (InternalNode[]) updatedEntities.toArray(internalNodes); - } - - return entities; - } - - /** - * Updates the given array of relationships checking if any need to be removed or added. - * Also updates the original array of relationships. - * @param relationships the current relationships - * @return the update relationships array - */ - protected InternalRelationship[] updateRelationships(InternalRelationship[] relationships) { - if ((relationshipsToRemove.size() > 0) || (relationshipsToAdd.size() > 0)) { - List internalRelsList = new ArrayList(Arrays.asList(relationships)); - - // remove relationships - if (relationshipsToRemove.size() > 0) { - for (Iterator iter = relationshipsToRemove.iterator(); iter.hasNext();) { - LayoutRelationship relation = (LayoutRelationship) iter.next(); - if (relation.getLayoutInformation() != null) { - internalRelsList.remove(relation.getLayoutInformation()); - } - } - } - - // Also remove from _internalRelationships - ArrayList updatedRelationships = new ArrayList(internalRelationships.length - relationshipsToRemove.size() + relationshipsToAdd.size()); - for (int i = 0; i < internalRelationships.length; i++) { - InternalRelationship relation = internalRelationships[i]; - if (relationshipsToRemove.contains(relation.getLayoutRelationship())) { - relationshipsToRemove.remove(relation.getLayoutRelationship()); - } else { - updatedRelationships.add(relation); - } - } - relationshipsToRemove.clear(); - - // add relationships - if (relationshipsToAdd.size() > 0) { - LayoutRelationship[] relsArray = new LayoutRelationship[relationshipsToAdd.size()]; - relsArray = (LayoutRelationship[]) relationshipsToAdd.toArray(relsArray); - InternalRelationship[] newRelationships = createInternalRelationships(relsArray); - for (int i = 0; i < newRelationships.length; i++) { - internalRelsList.add(newRelationships[i]); - updatedRelationships.add(newRelationships[i]); - } - } - relationshipsToAdd.clear(); - - relationships = new InternalRelationship[internalRelsList.size()]; - relationships = (InternalRelationship[]) internalRelsList.toArray(relationships); - - internalRelationships = new InternalRelationship[updatedRelationships.size()]; - internalRelationships = (InternalRelationship[]) updatedRelationships.toArray(internalRelationships); - } - - return relationships; - } - - /** - * Moves all the entities by the given amount. - * @param dx the amount to shift the entities in the x-direction - * @param dy the amount to shift the entities in the y-direction - */ - /* - public void moveAllEntities(double dx, double dy) { - if ((dx != 0) || (dy != 0)) { - synchronized (_internalNodes) { - for (int i = 0; i < _internalNodes.length; i++) { - InternalNode node = _internalNodes[i]; - node.setInternalLocation(node.getInternalX()+dx, node.getInternalY()+dy); - node.setLocation(node.getX()+dx, node.getY()+dy); - } - } - } - } - */ - - /** - * Returns true if the layout algorithm is running - * @return boolean if the layout algorithm is running - */ - public synchronized boolean isRunning() { - return !layoutStopped; - } - - /** - * Stops the current layout from running. - * All layout algorithms should constantly check isLayoutRunning - */ - public synchronized void stop() { - layoutStopped = true; - postLayoutAlgorithm(internalNodes, internalRelationships); - fireProgressEnded(getTotalNumberOfLayoutSteps()); - } - - // /** - // * Sleeps while the algorithm is paused. - // */ - // protected void sleepWhilePaused() { - // // do nothing while the algorithm is paused - // boolean wasPaused = false; - // while (isPaused()) { - // try { - // Thread.sleep(200); - // } catch (InterruptedException e) { - // } - // wasPaused = true; - // } - // // update the node positions (they might have been moved while paused) - // if (wasPaused) { - // for (int i = 0; i < internalNodes.length; i++) { - // InternalNode node = internalNodes[i]; - // node.setInternalLocation(node.getPreferredX(), node.getPreferredY()); - // } - // } - // } - - private void setupLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - internalX = x; - internalY = y; - internalHeight = height; - internalWidth = width; - // Filter all the unwanted entities and relationships - entitiesToLayout = (LayoutEntity[]) filterUnwantedObjects(entitiesToLayout); - relationshipsToConsider = (LayoutRelationship[]) filterUnwantedObjects(relationshipsToConsider); - - // Check that the input is valid - if (!verifyInput(entitiesToLayout, relationshipsToConsider)) { - layoutStopped = true; - throw new RuntimeException("The relationships in relationshipsToConsider don't contain the entities in entitiesToLayout"); - } - - // Create the internal nodes and relationship - internalNodes = createInternalNodes(entitiesToLayout); - internalRelationships = createInternalRelationships(relationshipsToConsider); - } - - // public synchronized Stoppable getLayoutThread(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean continuous) { - // //setupLayout( entitiesToLayout, relationshipsToConsider, x, y, width, height ); - // this.layoutStopped = false; - // this.runContinuously = continuous; - // setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); - // preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - // fireProgressStarted(getTotalNumberOfLayoutSteps()); - // return this; - // } - - /** - * Code called before the layout algorithm starts - */ - protected abstract void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height); - - /** - * Code called after the layout algorithm ends - */ - protected abstract void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider); - - /** - * Gets the total number of steps in this layout - */ - protected abstract int getTotalNumberOfLayoutSteps(); - - /** - * Gets the current layout step - * @return - */ - protected abstract int getCurrentLayoutStep(); - - /** - * This actually applies the layout - */ - public synchronized void applyLayout(final LayoutEntity[] entitiesToLayout, final LayoutRelationship[] relationshipsToConsider, final double x, final double y, final double width, final double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration { - checkThread(); - this.internalAsynchronous = asynchronous; - this.internalContinuous = continuous; - - if (!isValidConfiguration(asynchronous, continuous)) { - throw new InvalidLayoutConfiguration(); - } - - clearBendPoints(relationshipsToConsider); - - this.layoutStopped = false; - - // when an algorithm starts, reset the progress event - lastProgressEventFired = Calendar.getInstance(); - if (asynchronous) { - - Thread thread = new Thread(new Runnable() { - - public void run() { - setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); - preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - fireProgressStarted(getTotalNumberOfLayoutSteps()); - - applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - stop(); - } - - }); - thread.setPriority(Thread.MIN_PRIORITY); - thread.start(); - } else { - - // If we are running synchronously then we have to stop this at some - // point? right? - setupLayout(entitiesToLayout, relationshipsToConsider, x, y, width, height); - preLayoutAlgorithm(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - fireProgressStarted(getTotalNumberOfLayoutSteps()); - - applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - stop(); - } - - } - - /** - * Clear out all old bend points before doing a layout - */ - private void clearBendPoints(LayoutRelationship[] relationships) { - for (int i = 0; i < relationships.length; i++) { - LayoutRelationship rel = relationships[i]; - rel.clearBendPoints(); - } - } - - /** - * Update external bend points from the internal bendpoints list. Save the - * source and destination points for later use in scaling and translating - * @param relationshipsToConsider - */ - protected void updateBendPoints(InternalRelationship[] relationshipsToConsider) { - for (int i = 0; i < relationshipsToConsider.length; i++) { - InternalRelationship relationship = relationshipsToConsider[i]; - List bendPoints = relationship.getBendPoints(); - if (bendPoints.size() > 0) { - // We will assume that source/dest coordinates are for center of node - BendPoint[] externalBendPoints = new BendPoint[bendPoints.size() + 2]; - InternalNode sourceNode = relationship.getSource(); - externalBendPoints[0] = new BendPoint(sourceNode.getInternalX(), sourceNode.getInternalY()); - InternalNode destNode = relationship.getDestination(); - externalBendPoints[externalBendPoints.length - 1] = new BendPoint(destNode.getInternalX(), destNode.getInternalY()); - - for (int j = 0; j < bendPoints.size(); j++) { - BendPoint bp = (BendPoint) bendPoints.get(j); - externalBendPoints[j + 1] = new BendPoint(bp.x, bp.y, bp.getIsControlPoint()); - } - relationship.getLayoutRelationship().setBendPoints(externalBendPoints); - } - } - } - - // public void run() { - // - // if (started == true) { - // throw new RuntimeException("Layout has already run!"); - // } - // started = true; - // //layoutStopped = false; - // isLayoutPaused = false; - // applyLayoutInternal(internalNodes, internalRelationships, internalX, internalY, internalWidth, internalHeight); - // stop(); - // layoutStopped = true; - // isLayoutPaused = false; - // } - - /** - * Creates a list of InternalNode objects from the list of LayoutEntity objects the user - * wants layed out. Sets the internal nodes' positions and sizes from the - * external entities. - */ - private InternalNode[] createInternalNodes(LayoutEntity[] nodes) { - InternalNode[] internalNodes = new InternalNode[nodes.length]; - BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint(); - for (int i = 0; i < nodes.length; i++) { - basicEntityConstraint.clear(); - LayoutEntity externalNode = nodes[i]; - InternalNode internalNode = new InternalNode(externalNode); - externalNode.populateLayoutConstraint(basicEntityConstraint); - internalNode.setInternalLocation(externalNode.getXInLayout(), externalNode.getYInLayout()); - internalNodes[i] = internalNode; - } // end of for - return internalNodes; - } - - /** - * Creates a list of InternalRelationship objects from the given list of LayoutRelationship objects. - * @param rels - * @return List of internal relationships - */ - private InternalRelationship[] createInternalRelationships(LayoutRelationship[] rels) { - ArrayList listOfInternalRelationships = new ArrayList(rels.length); - for (int i = 0; i < rels.length; i++) { - LayoutRelationship relation = rels[i]; - InternalNode src = (InternalNode) relation.getSourceInLayout().getLayoutInformation(); - InternalNode dest = (InternalNode) relation.getDestinationInLayout().getLayoutInformation(); - if ((src != null) && (dest != null)) { - InternalRelationship internalRelationship = new InternalRelationship(relation, src, dest); - listOfInternalRelationships.add(internalRelationship); - } else { - throw new RuntimeException("Error creating internal relationship, one of the nodes is null: src=" + src + ", dest=" + dest); - } - } - InternalRelationship[] internalRelationships = new InternalRelationship[listOfInternalRelationships.size()]; - listOfInternalRelationships.toArray(internalRelationships); - return internalRelationships; - } - - /** - * Removes any objects that are currently filtered - */ - private Object[] filterUnwantedObjects(LayoutItem[] objects) { - // first remove any entities or relationships that are filtered. - List unfilteredObjsList = new ArrayList(); - if (filter != null) { - for (int i = 0; i < objects.length; i++) { - LayoutItem object = objects[i]; - if (!filter.isObjectFiltered(object)) { - unfilteredObjsList.add(object); - } - } - //@tag bug.156266-ClassCast.fix : use reflection to create the array. - Object[] unfilteredObjs = (Object[]) java.lang.reflect.Array.newInstance(objects.getClass().getComponentType(), unfilteredObjsList.size()); - unfilteredObjsList.toArray(unfilteredObjs); - return unfilteredObjs; - } - return objects; - } - - /** - * Filters the entities and relationships to apply the layout on - */ - public void setFilter(Filter filter) { - this.filter = filter; - } - - /** - * Determines the order in which the objects should be displayed. - * Note: Some algorithms force a specific order. - */ - public void setComparator(Comparator comparator) { - this.comparator = new InternalComparator(comparator); - } - - /** - * Verifies the endpoints of the relationships are entities in the entitiesToLayout list. - * Allows other classes in this package to use this method to verify the input - */ - public static boolean verifyInput(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider) { - boolean stillValid = true; - for (int i = 0; i < relationshipsToConsider.length; i++) { - LayoutRelationship relationship = relationshipsToConsider[i]; - LayoutEntity source = relationship.getSourceInLayout(); - LayoutEntity destination = relationship.getDestinationInLayout(); - boolean containsSrc = false; - boolean containsDest = false; - int j = 0; - while (j < entitiesToLayout.length && !(containsSrc && containsDest)) { - if (entitiesToLayout[j].equals(source)) { - containsSrc = true; - } - if (entitiesToLayout[j].equals(destination)) { - containsDest = true; - } - j++; - } - stillValid = containsSrc && containsDest; - } - return stillValid; - } - - /** - * Gets the location in the layout bounds for this node - * @param x - * @param y - * @return - */ - protected DisplayIndependentPoint getLocalLocation(InternalNode[] entitiesToLayout, double x, double y, DisplayIndependentRectangle realBounds) { - - double screenWidth = realBounds.width; - double screenHeight = realBounds.height; - DisplayIndependentRectangle layoutBounds = getLayoutBounds(entitiesToLayout, false); - double localX = (x / screenWidth) * layoutBounds.width + layoutBounds.x; - double localY = (y / screenHeight) * layoutBounds.height + layoutBounds.y; - return new DisplayIndependentPoint(localX, localY); - } - - /** - * Find an appropriate size for the given nodes, then fit them into the given bounds. - * The relative locations of the nodes to each other must be preserved. - * Child classes should set flag reresizeEntitiesAfterLayout to false if they - * want to preserve node sizes. - */ - protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, DisplayIndependentRectangle realBounds) { - defaultFitWithinBounds(entitiesToLayout, new InternalRelationship[0], realBounds); - } - - /** - * Find an appropriate size for the given nodes, then fit them into the given bounds. - * The relative locations of the nodes to each other must be preserved. - * Child classes should set flag reresizeEntitiesAfterLayout to false if they - * want to preserve node sizes. - */ - protected void defaultFitWithinBounds(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle realBounds) { - - DisplayIndependentRectangle layoutBounds; - - if (resizeEntitiesAfterLayout) { - layoutBounds = getLayoutBounds(entitiesToLayout, false); - - // Convert node x,y to be in percent rather than absolute coords - convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, false /*do not update size*/); - - // Resize and shift nodes - resizeAndShiftNodes(entitiesToLayout); - } - - // Recalculate layout, allowing for the node width, which we now know - layoutBounds = getLayoutBounds(entitiesToLayout, true); - - // adjust node positions again, to the new coordinate system (still as a percentage) - convertPositionsToPercentage(entitiesToLayout, relationships, layoutBounds, true /*update node size*/); - - DisplayIndependentRectangle screenBounds = calcScreenBounds(realBounds, layoutBounds); - - // Now convert to real screen coordinates - convertPositionsToCoords(entitiesToLayout, relationships, screenBounds); - } - - /** - * Calculate the screen bounds, maintaining the - * @param realBounds - * @return - */ - private DisplayIndependentRectangle calcScreenBounds(DisplayIndependentRectangle realBounds, DisplayIndependentRectangle layoutBounds) { - if (resizeEntitiesAfterLayout) { // OK to alter aspect ratio - double borderWidth = Math.min(realBounds.width, realBounds.height) / 10.0; // use 10% for the border - 5% on each side - return new DisplayIndependentRectangle(realBounds.x + borderWidth / 2.0, realBounds.y + borderWidth / 2.0, realBounds.width - borderWidth, realBounds.height - borderWidth); - } else { // retain layout aspect ratio - double heightAdjustment = realBounds.height / layoutBounds.height; - double widthAdjustment = realBounds.width / layoutBounds.width; - double ratio = Math.min(heightAdjustment, widthAdjustment); - double adjustedHeight = layoutBounds.height * ratio; - double adjustedWidth = layoutBounds.width * ratio; - double adjustedX = realBounds.x + (realBounds.width - adjustedWidth) / 2.0; - double adjustedY = realBounds.y + (realBounds.height - adjustedHeight) / 2.0; - double borderWidth = Math.min(adjustedWidth, adjustedHeight) / 10.0; // use 10% for the border - 5% on each side - return new DisplayIndependentRectangle(adjustedX + borderWidth / 2.0, adjustedY + borderWidth / 2.0, adjustedWidth - borderWidth, adjustedHeight - borderWidth); - } - } - - /** - * Find and set the node size - shift the nodes to the right and down to make - * room for the width and height. - * @param entitiesToLayout - * @param relationships - */ - private void resizeAndShiftNodes(InternalNode[] entitiesToLayout) { - // get maximum node size as percent of screen dimmensions - double nodeSize = getNodeSize(entitiesToLayout); - double halfNodeSize = nodeSize / 2; - - // Resize and shift nodes - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode node = entitiesToLayout[i]; - node.setInternalSize(nodeSize, nodeSize); - node.setInternalLocation(node.getInternalX() + halfNodeSize, node.getInternalY() + halfNodeSize); - } - } - - /** - * Convert all node positions into a percentage of the screen. If includeNodeSize - * is true then this also updates the node's internal size. - * @param entitiesToLayout - */ - private void convertPositionsToPercentage(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle layoutBounds, boolean includeNodeSize) { - - // Adjust node positions and sizes - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode node = entitiesToLayout[i]; - DisplayIndependentPoint location = node.getInternalLocation().convertToPercent(layoutBounds); - node.setInternalLocation(location.x, location.y); - if (includeNodeSize) { // adjust node sizes - double width = node.getInternalWidth() / layoutBounds.width; - double height = node.getInternalHeight() / layoutBounds.height; - node.setInternalSize(width, height); - } - } - - // Adjust bendpoint positions - for (int i = 0; i < relationships.length; i++) { - InternalRelationship rel = relationships[i]; - for (int j = 0; j < rel.getBendPoints().size(); j++) { - BendPoint bp = (BendPoint) rel.getBendPoints().get(j); - DisplayIndependentPoint toPercent = bp.convertToPercent(layoutBounds); - bp.setX(toPercent.x); - bp.setY(toPercent.y); - } - } - } - - /** - * Convert the positions from a percentage of bounds area to fixed - * coordinates. NOTE: ALL OF THE POSITIONS OF NODES UNTIL NOW WERE FOR THE - * CENTER OF THE NODE - Convert it to the left top corner. - * - * @param entitiesToLayout - * @param relationships - * @param realBounds - */ - private void convertPositionsToCoords(InternalNode[] entitiesToLayout, InternalRelationship[] relationships, DisplayIndependentRectangle screenBounds) { - - // Adjust node positions and sizes - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode node = entitiesToLayout[i]; - double width = node.getInternalWidth() * screenBounds.width; - double height = node.getInternalHeight() * screenBounds.height; - DisplayIndependentPoint location = node.getInternalLocation().convertFromPercent(screenBounds); - node.setInternalLocation(location.x - width / 2, location.y - height / 2); - if (resizeEntitiesAfterLayout) { - adjustNodeSizeAndPos(node, height, width); - } else { - node.setInternalSize(width, height); - } - } - - // Adjust bendpoint positions and shift based on source node size - for (int i = 0; i < relationships.length; i++) { - InternalRelationship rel = relationships[i]; - for (int j = 0; j < rel.getBendPoints().size(); j++) { - BendPoint bp = (BendPoint) rel.getBendPoints().get(j); - DisplayIndependentPoint fromPercent = bp.convertFromPercent(screenBounds); - bp.setX(fromPercent.x); - bp.setY(fromPercent.y); - } - } - } - - /** - * Adjust node size to take advantage of space. Reset position to top left corner of node. - * @param node - * @param height - * @param width - */ - private void adjustNodeSizeAndPos(InternalNode node, double height, double width) { - double widthUsingHeight = height * widthToHeightRatio; - if (widthToHeightRatio <= 1.0 && widthUsingHeight <= width) { - double widthToUse = height * widthToHeightRatio; - double leftOut = width - widthToUse; - node.setInternalSize(Math.max(height * widthToHeightRatio, MIN_ENTITY_SIZE), Math.max(height, MIN_ENTITY_SIZE)); - node.setInternalLocation(node.getInternalX() + leftOut / 2, node.getInternalY()); - - } else { - double heightToUse = height / widthToHeightRatio; - double leftOut = height - heightToUse; - - node.setInternalSize(Math.max(width, MIN_ENTITY_SIZE), Math.max(width / widthToHeightRatio, MIN_ENTITY_SIZE)); - node.setInternalLocation(node.getInternalX(), node.getInternalY() + leftOut / 2); - } - - } - - /** - * Returns the maximum possible node size as a percentage of the width or height in current coord system. - */ - private double getNodeSize(InternalNode[] entitiesToLayout) { - double width, height; - if (entitiesToLayout.length == 1) { - width = 0.8; - height = 0.8; - } else { - DisplayIndependentDimension minimumDistance = getMinimumDistance(entitiesToLayout); - width = 0.8 * minimumDistance.width; - height = 0.8 * minimumDistance.height; - } - return Math.max(width, height); - } - - /** - * Find the bounds in which the nodes are located. Using the bounds against the real bounds - * of the screen, the nodes can proportionally be placed within the real bounds. - * The bounds can be determined either including the size of the nodes or not. If the size - * is not included, the bounds will only be guaranteed to include the center of each node. - */ - protected DisplayIndependentRectangle getLayoutBounds(InternalNode[] entitiesToLayout, boolean includeNodeSize) { - double rightSide = Double.MIN_VALUE; - double bottomSide = Double.MIN_VALUE; - double leftSide = Double.MAX_VALUE; - double topSide = Double.MAX_VALUE; - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode entity = entitiesToLayout[i]; - if (entity.hasPreferredLocation()) { - continue; - } - - if (includeNodeSize) { - leftSide = Math.min(entity.getInternalX() - entity.getInternalWidth() / 2, leftSide); - topSide = Math.min(entity.getInternalY() - entity.getInternalHeight() / 2, topSide); - rightSide = Math.max(entity.getInternalX() + entity.getInternalWidth() / 2, rightSide); - bottomSide = Math.max(entity.getInternalY() + entity.getInternalHeight() / 2, bottomSide); - } else { - leftSide = Math.min(entity.getInternalX(), leftSide); - topSide = Math.min(entity.getInternalY(), topSide); - rightSide = Math.max(entity.getInternalX(), rightSide); - bottomSide = Math.max(entity.getInternalY(), bottomSide); - } - } - return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); - } - - /** - * minDistance is the closest that any two points are together. - * These two points become the center points for the two closest nodes, - * which we wish to make them as big as possible without overlapping. - * This will be the maximum of minDistanceX and minDistanceY minus a bit, lets say 20% - * - * We make the recommended node size a square for convenience. - * - * - * _______ - * | | - * | | - * | + | - * | |\ | - * |___|_\_|_____ - * | | \ | - * | | \ | - * +-|---+ | - * | | - * |_______| - * - * - * - */ - private DisplayIndependentDimension getMinimumDistance(InternalNode[] entitiesToLayout) { - DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE); - double minDistance = Double.MAX_VALUE; // the minimum distance between all the nodes - //TODO: Very Slow! - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode layoutEntity1 = entitiesToLayout[i]; - double x1 = layoutEntity1.getInternalX(); - double y1 = layoutEntity1.getInternalY(); - for (int j = i + 1; j < entitiesToLayout.length; j++) { - InternalNode layoutEntity2 = entitiesToLayout[j]; - double x2 = layoutEntity2.getInternalX(); - double y2 = layoutEntity2.getInternalY(); - double distanceX = Math.abs(x1 - x2); - double distanceY = Math.abs(y1 - y2); - double distance = Math.sqrt(Math.pow(distanceX, 2) + Math.pow(distanceY, 2)); - - if (distance < minDistance) { - minDistance = distance; - horAndVertdistance.width = distanceX; - horAndVertdistance.height = distanceY; - } - } - } - return horAndVertdistance; - } - - /** - * Set the width to height ratio you want the entities to use - */ - public void setEntityAspectRatio(double ratio) { - widthToHeightRatio = ratio; - } - - /** - * Returns the width to height ratio this layout will use to set the size of the entities. - */ - public double getEntityAspectRatio() { - return widthToHeightRatio; - } - - /** - * A layout algorithm could take an uncomfortable amout of time to complete. To relieve some of - * the mystery, the layout algorithm will notify each ProgressListener of its progress. - */ - public void addProgressListener(ProgressListener listener) { - if (!progressListeners.contains(listener)) { - progressListeners.add(listener); - } - } - - /** - * Removes the given progress listener, preventing it from receiving any more updates. - */ - public void removeProgressListener(ProgressListener listener) { - if (progressListeners.contains(listener)) { - progressListeners.remove(listener); - } - } - - /** - * Updates the layout locations so the external nodes know about the new locations - */ - protected void updateLayoutLocations(InternalNode[] nodes) { - for (int i = 0; i < nodes.length; i++) { - InternalNode node = nodes[i]; - if (!node.hasPreferredLocation()) { - node.setLocation(node.getInternalX(), node.getInternalY()); - - if ((layout_styles & LayoutStyles.NO_LAYOUT_NODE_RESIZING) != 1) { - // Only set the size if we are supposed to - node.setSize(node.getInternalWidth(), node.getInternalHeight()); - } - } - } - } - - protected void fireProgressStarted(int totalNumberOfSteps) { - ProgressEvent event = new ProgressEvent(0, totalNumberOfSteps); - for (int i = 0; i < progressListeners.size(); i++) { - ProgressListener listener = (ProgressListener) progressListeners.get(i); - - listener.progressStarted(event); - } - } - - protected void fireProgressEnded(int totalNumberOfSteps) { - ProgressEvent event = new ProgressEvent(totalNumberOfSteps, totalNumberOfSteps); - for (int i = 0; i < progressListeners.size(); i++) { - ProgressListener listener = (ProgressListener) progressListeners.get(i); - listener.progressEnded(event); - } - - } - - /** - * Fires an event to notify all of the registered ProgressListeners that another step - * has been completed in the algorithm. - * @param currentStep The current step completed. - * @param totalNumberOfSteps The total number of steps in the algorithm. - */ - protected void fireProgressEvent(int currentStep, int totalNumberOfSteps) { - - // Update the layout locations to the external nodes - Calendar now = Calendar.getInstance(); - now.add(Calendar.MILLISECOND, -MIN_TIME_DELAY_BETWEEN_PROGRESS_EVENTS); - - if (now.after(lastProgressEventFired) || currentStep == totalNumberOfSteps) { - ProgressEvent event = new ProgressEvent(currentStep, totalNumberOfSteps); - - for (int i = 0; i < progressListeners.size(); i++) { - - ProgressListener listener = (ProgressListener) progressListeners.get(i); - listener.progressUpdated(event); - } - lastProgressEventFired = Calendar.getInstance(); - } - - } - - protected int getNumberOfProgressListeners() { - return progressListeners.size(); - } - - private void checkThread() { - if (this.creationThread != Thread.currentThread()) { - throw new RuntimeException("Invalid Thread Access."); - } - } - -} Index: src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/HorizontalLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.6 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Ian Bull - updated and modified - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; - -import org.eclipse.zest.layouts.LayoutStyles; - -/** - * @version 2.0 - * @author Casey Best and Rob Lintern - */ -public class HorizontalLayoutAlgorithm extends GridLayoutAlgorithm { - - - public HorizontalLayoutAlgorithm(int styles) { - super(styles); - } - - /** - * Horizontal Layout Algorithm constructor. Sets the Style to none. - */ - public HorizontalLayoutAlgorithm() { - this( LayoutStyles.NONE ); - } - /** - * Calculates and returns an array containing the number of columns, followed by the number of rows - */ - protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { - int rows = 1; - int cols = numChildren; - int[] result = {cols, rows}; - return result; - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) { - return false; - } else if ( asynchronous && !continueous ) { - return true; - } else if ( !asynchronous && continueous ) { - return false; - } else if ( !asynchronous && !continueous ) { - return true; - } - - return false; - } -} Index: src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java,v retrieving revision 1.7 diff -u -r1.7 TreeLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 +++ src/org/eclipse/zest/layouts/algorithms/TreeLayoutAlgorithm.java 10 Jul 2009 18:49:27 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,574 +7,259 @@ * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ package org.eclipse.zest.layouts.algorithms; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Set; -import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; -import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; - - /** - * The TreeLayoutAlgorithm class implements a simple algorithm to - * arrange graph nodes in a layered vertical tree-like layout. + * The TreeLayoutAlgorithm class implements a simple algorithm to arrange graph + * nodes in a layered tree-like layout. * - * This is by no means an efficiently coded algorithm. - * - * @version 2.0 - * @author Casey Best and Rob Lintern (version 1.0 by Jingwei Wu) + * @version 2.1 + * @author Casey Best and Rob Lintern (version 2.0) + * @author Jingwei Wu (version 1.0) */ -public class TreeLayoutAlgorithm extends AbstractLayoutAlgorithm { - - private final static double DEFAULT_WEIGHT = 0; - private final static boolean DEFAULT_MARKED = false; - - private final static boolean AS_DESTINATION = false; - private final static boolean AS_SOURCE = true; - - private final static int NUM_DESCENDENTS_INDEX = 0; - private final static int NUM_LEVELS_INDEX = 1; - - private ArrayList treeRoots; - - private double boundsX; - private double boundsY; - private double boundsWidth; - private double boundsHeight; - private DisplayIndependentRectangle layoutBounds = null; - - private List [] parentLists; - private List [] childrenLists; - private double [] weights; - private boolean [] markedArr; - - ///////////////////////////////////////////////////////////////////////// - ///// Constructors ///// - ///////////////////////////////////////////////////////////////////////// +public class TreeLayoutAlgorithm implements LayoutAlgorithm { /** - * Constructs a new TreeLayoutAlgorithm object. - */ - public TreeLayoutAlgorithm( int styles ) { - super( styles ); - } - - /** - * Tree layout algorithm Constructor with NO Style - * + * Tree direction constant for which root is placed at the top and branches + * spread downwards */ - public TreeLayoutAlgorithm() { - this( LayoutStyles.NONE ); - } - - ///////////////////////////////////////////////////////////////////////// - ///// Public Methods ///// - ///////////////////////////////////////////////////////////////////////// - - public void setLayoutArea(double x, double y, double width, double height) { - throw new RuntimeException(); - } - - protected int getCurrentLayoutStep() { - // TODO Auto-generated method stub - return 0; - } - - protected int getTotalNumberOfLayoutSteps() { - return 4; - } + public final static int TOP_DOWN = 1; /** - * Executes this TreeLayoutAlgorithm layout algorithm by referencing the - * data stored in the repository system. Once done, the result - * will be saved to the data repository. - * - * @param entitiesToLayout Apply the algorithm to these entities - * @param relationshipsToConsider Only consider these relationships when applying the algorithm. - * @param boundsX The left side of the bounds in which the layout can place the entities. - * @param boundsY The top side of the bounds in which the layout can place the entities. - * @param boundsWidth The width of the bounds in which the layout can place the entities. - * @param boundsHeight The height of the bounds in which the layout can place the entities. - * @throws RuntimeException Thrown if entitiesToLayout doesn't contain all of the endpoints for each relationship in relationshipsToConsider + * Tree direction constant for which root is placed at the bottom and + * branches spread upwards */ - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // Filter unwanted entities and relationships - //super.applyLayout (entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight); - - parentLists = new List [entitiesToLayout.length]; - childrenLists = new List [entitiesToLayout.length]; - weights = new double [entitiesToLayout.length]; - markedArr = new boolean [entitiesToLayout.length]; - for (int i = 0; i < entitiesToLayout.length; i++) { - parentLists[i] = new ArrayList(); - childrenLists[i] = new ArrayList(); - weights[i] = DEFAULT_WEIGHT; - markedArr[i] = DEFAULT_MARKED; - } - - this.boundsHeight = height; - this.boundsWidth = width; - this.boundsX = x; - this.boundsY = y; - layoutBounds = new DisplayIndependentRectangle(boundsX, boundsY, boundsWidth, boundsHeight); - - } - - protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { - - if (entitiesToLayout.length > 0) { - int totalProgress = 4; - fireProgressEvent (1, totalProgress); - - //List roots = new ArrayList(); - treeRoots = new ArrayList(); - buildForest(treeRoots, entitiesToLayout, relationshipsToConsider); - fireProgressEvent (2, totalProgress); - computePositions(treeRoots, entitiesToLayout); - fireProgressEvent (3, totalProgress); - defaultFitWithinBounds(entitiesToLayout, layoutBounds); + public final static int BOTTOM_UP = 2; - } - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - updateLayoutLocations(entitiesToLayout); - fireProgressEvent (4, 4); - } - /** - * Returns the last found roots + * Tree direction constant for which root is placed at the left and branches + * spread to the right */ - public List getRoots () { - return treeRoots; - } + public final static int LEFT_RIGHT = 3; /** - * Finds all the relationships in which the node obj - * plays the specified role. - * @param entity The node that concerns the relations to be found. - * @param role The role played by the obj. Its type - * must be of ACTOR_ROLE or ACTEE_ROLE. - * @see SimpleRelationship + * Tree direction constant for which root is placed at the right and + * branches spread to the left */ - private Collection findRelationships(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) { - Collection foundRels = new ArrayList(); - for (int i = 0; i < relationshipsToConsider.length; i++) { - InternalRelationship rel = relationshipsToConsider[i]; - if (objectAsSource && rel.getSource().equals (entity)) { - foundRels.add(rel); - } else if (!objectAsSource && rel.getDestination().equals (entity)) { - foundRels.add(rel); - } + public final static int RIGHT_LEFT = 4; + + private class EntityInfo { + public EntityInfo(EntityLayout entity) { + this.entity = entity; } - return foundRels; - } - /** - * Finds the relation that has the lowest index in the relation - * repository in which the node obj plays the specified - * role. - * @param obj The node that concerns the relations to be found. - * @param role The role played by the obj. Its type must - * be of ACTOR_ROLE or ACTEE_ROLE. - * @see SimpleRelationship - * @see SimpleRelationship#ACTOR_ROLE - * @see SimpleRelationship#ACTEE_ROLE - */ - private InternalRelationship findRelationship(Object entity, boolean objectAsSource, InternalRelationship [] relationshipsToConsider) { - InternalRelationship relationship = null; - for (int i = 0; i < relationshipsToConsider.length && relationship == null; i++) { - InternalRelationship possibleRel = relationshipsToConsider[i]; - if (objectAsSource && possibleRel.getSource().equals (entity)) { - relationship = possibleRel; - } else if (!objectAsSource && possibleRel.getDestination().equals (entity)) { - relationship = possibleRel; + public void addChild(EntityInfo child) { + children.add(child); + } + + public void precomputeTree() { + if (children.isEmpty()) { + numOfLeaves = 1; + } else + for (Iterator iterator = children.iterator(); iterator.hasNext();) { + EntityInfo child = (EntityInfo) iterator.next(); + child.depth = this.depth + 1; + child.precomputeTree(); + this.numOfLeaves += child.numOfLeaves; + this.height = Math.max(this.height, child.height + 1); } } - return relationship; + + final public EntityLayout entity; + public int height = 0; + public int depth = -1; + public int numOfLeaves = 0; + public final List children = new ArrayList(); } + private int direction = TOP_DOWN; + private boolean resize = false; - ///////////////////////////////////////////////////////////////////////// - ///// Private Methods ///// - ///////////////////////////////////////////////////////////////////////// + private LayoutContext context; + private DisplayIndependentRectangle bounds; - /** - * Builds the tree forest that is used to calculate positions - * for each node in this TreeLayoutAlgorithm. - */ - private void buildForest(List roots, InternalNode [] entities, InternalRelationship [] relationships) { - List unplacedEntities = new ArrayList (Arrays.asList(entities)); - buildForestRecursively(roots, unplacedEntities, entities, relationships); + private double leafSize, layerSize; + + private EntityInfo superRoot; + + public TreeLayoutAlgorithm() { + } + + public TreeLayoutAlgorithm(int direction) { + setDirection(direction); + } + + public int getDirection() { + return direction; + } + + public void setDirection(int direction) { + if (direction == TOP_DOWN || direction == BOTTOM_UP || direction == LEFT_RIGHT || direction == RIGHT_LEFT) + this.direction = direction; + else + throw new RuntimeException("Invalid direction: " + direction); } /** - * Builds the forest recursively. All entities - * will be placed somewhere in the forest. + * + * @return true if this algorithm is set to resize elements */ - private void buildForestRecursively(List roots, List unplacedEntities, InternalNode [] entities, InternalRelationship [] relationships) { - if (unplacedEntities.size() == 0) { - return; // no more entities to place - } - - // get the first entity in the list of unplaced entities, find its root, and build this root's tree - InternalNode layoutEntity = (InternalNode) unplacedEntities.get(0); - InternalNode rootEntity = findRootObjectRecursive(layoutEntity, new HashSet(), relationships); - int rootEntityIndex = indexOfInternalNode(entities, rootEntity); - buildTreeRecursively(rootEntity, rootEntityIndex, 0, entities, relationships); - roots.add(rootEntity); - - // now see which nodes are left to be placed in a tree somewhere - List unmarkedCopy = new ArrayList(unplacedEntities); - for (Iterator iter = unmarkedCopy.iterator(); iter.hasNext();) { - InternalNode tmpEntity = (InternalNode) iter.next(); - int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity); - boolean isMarked = markedArr[tmpEntityIndex]; - if (isMarked) { - unplacedEntities.remove(tmpEntity); - } - } - buildForestRecursively(roots, unplacedEntities, entities, relationships); + public boolean isResizing() { + return resize; } /** - * Finds the root node that can be treated as the root of a tree. - * The found root node should be one of the unmarked nodes. + * + * @param resizing + * true if this algorithm should resize elements (default is + * false) */ - private InternalNode findRootObjectRecursive(InternalNode currentEntity, Set seenAlready, InternalRelationship [] relationshipsToConsider) { - InternalNode rootEntity = null; - InternalRelationship rel = findRelationship(currentEntity, AS_DESTINATION, relationshipsToConsider); - if (rel == null) { - rootEntity = currentEntity; - } else { - InternalNode parentEntity = rel.getSource(); - if (!seenAlready.contains(parentEntity)) { - seenAlready.add(parentEntity); - rootEntity = findRootObjectRecursive(parentEntity, seenAlready, relationshipsToConsider); - } else { - rootEntity = currentEntity; - } - } - return rootEntity; + public void setResizing(boolean resizing) { + resize = resizing; } + public void setLayoutContext(LayoutContext context) { + this.context = context; + } - - /** - * Builds a tree of the passed in entity. - * The entity will pass a weight value to all of its children recursively. - */ - private void buildTreeRecursively(InternalNode layoutEntity, int i, double weight, InternalNode [] entities, final InternalRelationship [] relationships) { - // No need to do further computation! - if (layoutEntity == null) { - return; - } + public void applyLayout() { + EntityLayout[] entities = context.getEntities(); - // A marked entity means that it has been added to the - // forest, and its weight value needs to be modified. - if (markedArr[i]) { - modifyWeightRecursively(layoutEntity, i, weight, new HashSet(), entities, relationships); - return; //No need to do further computation. - } + internalApplyLayout(entities); - // Mark this entity, set its weight value and create a new tree node. - markedArr[i] = true; - weights[i] = weight; - - // collect the children of this entity and put them in order - Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); - List children = new ArrayList (); - for (Iterator iter = rels.iterator(); iter.hasNext();) { - InternalRelationship layoutRel = (InternalRelationship) iter.next(); - InternalNode childEntity = layoutRel.getDestination(); - children.add(childEntity); - } - - if (comparator != null) { - Collections.sort(children, comparator); - } else { - // sort the children by level, then by number of descendents, then by number of children - // TODO: SLOW - Collections.sort(children, new Comparator () { - public int compare(Object o1, Object o2) { - InternalNode node1 = (InternalNode) o1; - InternalNode node2 = (InternalNode) o2; - int [] numDescendentsAndLevel1 = new int [2]; - int [] numDescendentsAndLevel2 = new int [2]; - int level1 = numDescendentsAndLevel1[NUM_LEVELS_INDEX]; - int level2 = numDescendentsAndLevel2[NUM_LEVELS_INDEX]; - if (level1 == level2) { - getNumDescendentsAndLevel(node1, relationships, numDescendentsAndLevel1); - getNumDescendentsAndLevel(node2, relationships, numDescendentsAndLevel2); - int numDescendents1 = numDescendentsAndLevel1[NUM_DESCENDENTS_INDEX]; - int numDescendents2 = numDescendentsAndLevel2[NUM_DESCENDENTS_INDEX]; - if (numDescendents1 == numDescendents2) { - int numChildren1 = getNumChildren(node1, relationships); - int numChildren2 = getNumChildren(node1, relationships); - return numChildren2 - numChildren1; - } else { - return numDescendents2 - numDescendents1; - } - } else { - return level2 - level1; - } - //return getNumChildren(node2, relationships) - getNumChildren(node1, relationships); - } - }); - } - - // map children to this parent, and vice versa - for (Iterator iter = children.iterator(); iter.hasNext();) { - InternalNode childEntity = (InternalNode) iter.next(); - int childEntityIndex = indexOfInternalNode(entities, childEntity); - if (!childrenLists[i].contains(childEntity)) { - childrenLists[i].add(childEntity); - } - if (!parentLists[childEntityIndex].contains(layoutEntity)) { - parentLists[childEntityIndex].add(layoutEntity); - } - } - - for (Iterator iter = children.iterator(); iter.hasNext();) { - InternalNode childEntity = (InternalNode) iter.next(); - int childEntityIndex = indexOfInternalNode(entities, childEntity); - buildTreeRecursively(childEntity, childEntityIndex, weight + 1, entities, relationships); - } + if (resize) + AlgorithmHelper.maximizeSizes(entities); + AlgorithmHelper.fitWithinBounds(entities, bounds, resize); } - - private int getNumChildren (InternalNode layoutEntity, InternalRelationship [] relationships) { - return findRelationships(layoutEntity, AS_SOURCE, relationships).size(); - } - - private void getNumDescendentsAndLevel (InternalNode layoutEntity, InternalRelationship [] relationships, int [] numDescendentsAndLevel) { - getNumDescendentsAndLevelRecursive(layoutEntity, relationships, new HashSet(), numDescendentsAndLevel, 0); - } - - private void getNumDescendentsAndLevelRecursive (InternalNode layoutEntity, InternalRelationship [] relationships, Set seenAlready, int [] numDescendentsAndLevel, int currentLevel) { - if (seenAlready.contains(layoutEntity)) { - return; - } - seenAlready.add(layoutEntity); - numDescendentsAndLevel[NUM_LEVELS_INDEX] = Math.max(numDescendentsAndLevel[NUM_LEVELS_INDEX], currentLevel); - Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); - for (Iterator iter = rels.iterator(); iter.hasNext();) { - InternalRelationship layoutRel = (InternalRelationship) iter.next(); - InternalNode childEntity = layoutRel.getDestination(); - numDescendentsAndLevel[NUM_DESCENDENTS_INDEX]++; - getNumDescendentsAndLevelRecursive(childEntity, relationships, seenAlready, numDescendentsAndLevel, currentLevel + 1); - - } - } - - - /** - * Modifies the weight value of the marked node recursively. - */ - private void modifyWeightRecursively(InternalNode layoutEntity, int i, double weight, Set descendentsSeenSoFar, InternalNode [] entities, InternalRelationship [] relationships) { - // No need to do further computation! - if (layoutEntity == null) { - return; - } - if (descendentsSeenSoFar.contains(layoutEntity)) { - return; //No need to do further computation. + void internalApplyLayout(EntityLayout[] entities) { + superRoot = new EntityInfo(null); + createTrees(entities); + + bounds = context.getBounds(); + if (direction == TOP_DOWN || direction == BOTTOM_UP) { + leafSize = bounds.width / superRoot.numOfLeaves; + layerSize = bounds.height / superRoot.height; + } else { + leafSize = bounds.height / superRoot.numOfLeaves; + layerSize = bounds.width / superRoot.height; } - - descendentsSeenSoFar.add(layoutEntity); - // No need to do further computation! - if (weight < weights[i]) { - return; + int leafCountSoFar = 0; + for (Iterator iterator = superRoot.children.iterator(); iterator.hasNext();) { + EntityInfo rootInfo = (EntityInfo) iterator.next(); + computePositionRecursively(rootInfo, leafCountSoFar); + leafCountSoFar = leafCountSoFar + rootInfo.numOfLeaves; } - weights[i] = weight; - Collection rels = findRelationships(layoutEntity, AS_SOURCE, relationships); - - - for (Iterator iter = rels.iterator(); iter.hasNext();) { - InternalRelationship tmpRel = (InternalRelationship) iter.next(); - InternalNode tmpEntity = tmpRel.getDestination(); - int tmpEntityIndex = indexOfInternalNode(entities, tmpEntity); - modifyWeightRecursively(tmpEntity, tmpEntityIndex, weight + 1, descendentsSeenSoFar, entities, relationships); - } + superRoot = null; } /** - * Gets the maxium weight of a tree in the forest of this TreeLayoutAlgorithm. + * Builds a tree structure using BFS method. Created trees are children of + * {@link #superRoot}. + * + * @param entities */ - private double getMaxiumWeightRecursive(InternalNode layoutEntity, int i, Set seenAlready, InternalNode [] entities) { - double result = 0; - if (seenAlready.contains(layoutEntity)) { - return result; - } - seenAlready.add(layoutEntity); - List children = childrenLists[i]; - if (children.isEmpty()) { - result = weights[i]; - } else { - //TODO: SLOW - for (Iterator iter = children.iterator(); iter.hasNext();) { - InternalNode childEntity = (InternalNode) iter.next(); - int childEntityIndex = indexOfInternalNode(entities, childEntity); - result = Math.max(result, getMaxiumWeightRecursive(childEntity, childEntityIndex, seenAlready, entities)); + private void createTrees(EntityLayout[] entities) { + HashSet alreadyVisited = new HashSet(); + LinkedList nodesToAdd = new LinkedList(); + for (int i = 0; i < entities.length; i++) { + EntityLayout root = findRoot(entities[i], alreadyVisited); + if (root != null) { + alreadyVisited.add(root); + nodesToAdd.addLast(new Object[] { root, superRoot }); } } - return result; - } - - /** - * Computes positions for each node in this TreeLayoutAlgorithm by - * referencing the forest that holds those nodes. - */ - private void computePositions(List roots, InternalNode [] entities) { - // No need to do further computation! - if (roots.size() == 0) { - return; - } - - int totalLeafCount = 0; - double maxWeight = 0; - for (int i = 0; i < roots.size(); i++) { - InternalNode rootEntity = (InternalNode) roots.get(i); - int rootEntityIndex = indexOfInternalNode(entities, rootEntity); - totalLeafCount = totalLeafCount + getNumberOfLeaves(rootEntity, rootEntityIndex, entities); - maxWeight = Math.max(maxWeight, getMaxiumWeightRecursive(rootEntity, rootEntityIndex, new HashSet(), entities) + 1.0); + while (!nodesToAdd.isEmpty()) { + Object[] dequeued = (Object[]) nodesToAdd.removeFirst(); + EntityLayout entity = (EntityLayout) dequeued[0]; + EntityInfo parentEntityInfo = (EntityInfo) dequeued[1]; + EntityInfo currentEntityInfo = new EntityInfo(entity); + parentEntityInfo.addChild(currentEntityInfo); + EntityLayout[] children = entity.getSuccessingEntities(); + for (int i = 0; i < children.length; i++) { + if (!alreadyVisited.contains(children[i])) { + alreadyVisited.add(children[i]); + nodesToAdd.addLast(new Object[] { children[i], currentEntityInfo }); + } + } } - - double width = 1.0 / totalLeafCount; - double height = 1.0 / maxWeight; + superRoot.precomputeTree(); + } - int leafCountSoFar = 0; - - //TODO: SLOW! - for (int i = 0; i < roots.size(); i++) { - InternalNode rootEntity = (InternalNode) roots.get(i); - int rootEntityIndex = indexOfInternalNode(entities, rootEntity); - computePositionRecursively(rootEntity, rootEntityIndex, leafCountSoFar, width, height, new HashSet(), entities); - leafCountSoFar = leafCountSoFar + getNumberOfLeaves(rootEntity, rootEntityIndex, entities); + /** + * Searches for a root of a tree containing given entity by continuously + * grabbing a predecessor of current entity. If it reaches an entity that + * exists in alreadyVisited set, it returns null. If it detects a cycle, it + * returns the first found entity of that cycle. If it reaches an entity + * that has no predecessors, it returns that entity. + * + * @param entityLayout + * starting entity + * @param alreadyVisited + * set of entities that can't lay on path to the root (if one + * does, method stops and returns null). + * @return + */ + private EntityLayout findRoot(EntityLayout entityLayout, Set alreadyVisited) { + HashSet alreadyVisitedRoot = new HashSet(); + while (true) { + if (alreadyVisited.contains(entityLayout)) + return null; + if (alreadyVisitedRoot.contains(entityLayout)) + return entityLayout; + alreadyVisitedRoot.add(entityLayout); + EntityLayout[] predecessingEntities = entityLayout.getPredecessingEntities(); + if (predecessingEntities.length > 0) { + entityLayout = predecessingEntities[0]; + } else { + return entityLayout; + } } } - + /** * Computes positions recursively until the leaf nodes are reached. */ - private void computePositionRecursively(InternalNode layoutEntity, int i, int relativePosition, double width, double height, Set seenAlready, InternalNode [] entities) { - if (seenAlready.contains(layoutEntity)) { - return; - } - seenAlready.add(layoutEntity); - double level = getLevel(layoutEntity, i, entities); - int breadth = getNumberOfLeaves(layoutEntity, i, entities); - double absHPosition = relativePosition + breadth / 2.0; - double absVPosition = (level + 0.5); - - double posx = absHPosition * width; - double posy = absVPosition * height; - double weight = weights[i]; - posy = posy + height * (weight - level); - layoutEntity.setInternalLocation( posx, posy ); - - - int relativeCount = 0; - List children = childrenLists[i]; - //TODO: Slow - for (Iterator iter = children.iterator(); iter.hasNext();) { - InternalNode childEntity = (InternalNode) iter.next(); - int childEntityIndex = indexOfInternalNode(entities, childEntity); - computePositionRecursively(childEntity, childEntityIndex, relativePosition + relativeCount, width, height, seenAlready, entities); - relativeCount = relativeCount + getNumberOfLeaves(childEntity, childEntityIndex, entities); + private void computePositionRecursively(EntityInfo entityInfo, int relativePosition) { + double breadthPosition = relativePosition + entityInfo.numOfLeaves / 2.0; + double depthPosition = (entityInfo.depth + 0.5); + + switch (direction) { + case TOP_DOWN: + entityInfo.entity.setLocation(breadthPosition * leafSize, depthPosition * layerSize); + break; + case BOTTOM_UP: + entityInfo.entity.setLocation(breadthPosition * leafSize, bounds.height - depthPosition * layerSize); + break; + case LEFT_RIGHT: + entityInfo.entity.setLocation(bounds.width - depthPosition * layerSize, breadthPosition * leafSize); + break; + case RIGHT_LEFT: + entityInfo.entity.setLocation(depthPosition * layerSize, breadthPosition * leafSize); + break; + } + + for (Iterator iterator = entityInfo.children.iterator(); iterator.hasNext();) { + EntityInfo childInfo = (EntityInfo) iterator.next(); + computePositionRecursively(childInfo, relativePosition); + relativePosition += childInfo.numOfLeaves; } } - - private int getNumberOfLeaves (InternalNode layoutEntity, int i, InternalNode [] entities) { - return getNumberOfLeavesRecursive(layoutEntity, i, new HashSet(), entities); - } - - private int getNumberOfLeavesRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) { - int numLeaves = 0; - List children = childrenLists[i]; - if (children.size() == 0) { - numLeaves = 1; - } else { - //TODO: SLOW! - for (Iterator iter = children.iterator(); iter.hasNext();) { - InternalNode childEntity = (InternalNode) iter.next(); - if (!seen.contains(childEntity)) { - seen.add (childEntity); - int childEntityIndex = indexOfInternalNode(entities, childEntity); - numLeaves += getNumberOfLeavesRecursive(childEntity, childEntityIndex, seen, entities); - } else { - numLeaves = 1; - } - } - } - return numLeaves; - } - - private int getLevel (InternalNode layoutEntity, int i, InternalNode [] entities) { - return getLevelRecursive(layoutEntity, i, new HashSet(), entities); - } - - private int getLevelRecursive(InternalNode layoutEntity, int i, Set seen, InternalNode [] entities) { - if (seen.contains(layoutEntity)) { - return 0; - } - seen.add(layoutEntity); - List parents = parentLists[i]; - int maxParentLevel = 0; - for (Iterator iter = parents.iterator(); iter.hasNext();) { - InternalNode parentEntity = (InternalNode) iter.next(); - int parentEntityIndex = indexOfInternalNode(entities, parentEntity); - int parentLevel = getLevelRecursive(parentEntity, parentEntityIndex, seen, entities) + 1; - maxParentLevel = Math.max(maxParentLevel, parentLevel); - } - return maxParentLevel; - } - - /** - * Note: Use this as little as possible! - * TODO limit the use of this method - * @param nodes - * @param nodeToFind - * @return - */ - private int indexOfInternalNode (InternalNode [] nodes, InternalNode nodeToFind) { - for (int i = 0; i < nodes.length; i++) { - InternalNode node = nodes[i]; - if (node.equals(nodeToFind)) { - return i; - } - } - throw new RuntimeException("Couldn't find index of internal node: " + nodeToFind); - } - - - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) { - return false; - } else if ( asynchronous && !continueous ) { - return true; - } else if ( !asynchronous && continueous ) { - return false; - } else if ( !asynchronous && !continueous ) { - return true; - } - - return false; - } - } Index: src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/VerticalLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; - -import org.eclipse.zest.layouts.LayoutStyles; - -/** - * @version 2.0 - * @author Casey Best and Rob Lintern (version 1.0 by Rob Lintern) - */ -public class VerticalLayoutAlgorithm extends GridLayoutAlgorithm { - - - /** - * Veertical Layout Algorithm constructor with no styles. - * - */ - public VerticalLayoutAlgorithm() { - this(LayoutStyles.NONE ); - } - - public VerticalLayoutAlgorithm(int styles) { - super(styles); - } - - /** - * Calculates and returns an array containing the number of columns, followed by the number of rows - */ - protected int[] calculateNumberOfRowsAndCols (int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { - int cols = 1; - int rows = numChildren; - int[] result = {cols, rows}; - return result; - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) return false; - else if ( asynchronous && !continueous ) return true; - else if ( !asynchronous && continueous ) return false; - else if ( !asynchronous && !continueous ) return true; - - return false; - } -} Index: src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java,v retrieving revision 1.5 diff -u -r1.5 DirectedGraphLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java 4 May 2009 05:47:36 -0000 1.5 +++ src/org/eclipse/zest/layouts/algorithms/DirectedGraphLayoutAlgorithm.java 10 Jul 2009 18:49:25 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,7 +7,9 @@ * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ + package org.eclipse.zest.layouts.algorithms; import java.lang.reflect.Field; @@ -20,11 +22,12 @@ import org.eclipse.draw2d.graph.DirectedGraphLayout; import org.eclipse.draw2d.graph.Edge; import org.eclipse.draw2d.graph.Node; -import org.eclipse.swt.SWT; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; +import org.eclipse.zest.layouts.ConnectionLayout; +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; -public class DirectedGraphLayoutAlgorithm extends AbstractLayoutAlgorithm { +public class DirectedGraphLayoutAlgorithm implements LayoutAlgorithm { class ExtendedDirectedGraphLayout extends DirectedGraphLayout { @@ -51,28 +54,48 @@ e.printStackTrace(); } } + } + + public static final int HORIZONTAL = 1; + + public static final int VERTICAL = 2; + + private int orientation = VERTICAL; + + private LayoutContext context; + public DirectedGraphLayoutAlgorithm() { } - public DirectedGraphLayoutAlgorithm(int styles) { - super(styles); + public DirectedGraphLayoutAlgorithm(int orientation) { + if (orientation == VERTICAL) + this.orientation = orientation; } - protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { - HashMap mapping = new HashMap(entitiesToLayout.length); + public int getOrientation() { + return orientation; + } + + public void setOrientation(int orientation) { + if (orientation == HORIZONTAL || orientation == VERTICAL) + this.orientation = orientation; + } + + public void applyLayout() { + HashMap mapping = new HashMap(); DirectedGraph graph = new DirectedGraph(); - for (int i = 0; i < entitiesToLayout.length; i++) { - InternalNode internalNode = entitiesToLayout[i]; - Node node = new Node(internalNode); + EntityLayout[] entities = context.getEntities(); + for (int i = 0; i < entities.length; i++) { + Node node = new Node(entities[i]); node.setSize(new Dimension(10, 10)); - mapping.put(internalNode, node); + mapping.put(entities[i], node); graph.nodes.add(node); } - for (int i = 0; i < relationshipsToConsider.length; i++) { - InternalRelationship relationship = relationshipsToConsider[i]; - Node source = (Node) mapping.get(relationship.getSource()); - Node dest = (Node) mapping.get(relationship.getDestination()); - Edge edge = new Edge(relationship, source, dest); + ConnectionLayout[] connections = context.getConnections(); + for (int i = 0; i < connections.length; i++) { + Node source = (Node) mapping.get(connections[i].getSource()); + Node dest = (Node) mapping.get(connections[i].getTarget()); + Edge edge = new Edge(connections[i], source, dest); graph.edges.add(edge); } DirectedGraphLayout directedGraphLayout = new ExtendedDirectedGraphLayout(); @@ -80,45 +103,17 @@ for (Iterator iterator = graph.nodes.iterator(); iterator.hasNext();) { Node node = (Node) iterator.next(); - InternalNode internalNode = (InternalNode) node.data; - // For horizontal layout transpose the x and y coordinates - if ((layout_styles & SWT.HORIZONTAL) == SWT.HORIZONTAL) { - internalNode.setInternalLocation(node.y, node.x); - }else { - internalNode.setInternalLocation(node.x, node.y); + EntityLayout entity = (EntityLayout) node.data; + if (orientation == VERTICAL) { + entity.setLocation(node.x, node.y); + } else { + entity.setLocation(node.y, node.x); } } - updateLayoutLocations(entitiesToLayout); - } - - protected int getCurrentLayoutStep() { - // TODO Auto-generated method stub - return 0; } - protected int getTotalNumberOfLayoutSteps() { - // TODO Auto-generated method stub - return 0; - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { - // TODO Auto-generated method stub - return true; - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - // TODO Auto-generated method stub - - } - - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // TODO Auto-generated method stub - - } - - public void setLayoutArea(double x, double y, double width, double height) { - // TODO Auto-generated method stub - + public void setLayoutContext(LayoutContext context) { + this.context = context; } } Index: src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java,v retrieving revision 1.5 diff -u -r1.5 CompositeLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java 31 Mar 2009 16:42:20 -0000 1.5 +++ src/org/eclipse/zest/layouts/algorithms/CompositeLayoutAlgorithm.java 10 Jul 2009 18:49:25 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,71 +7,31 @@ * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ + package org.eclipse.zest.layouts.algorithms; -import org.eclipse.zest.layouts.InvalidLayoutConfiguration; import org.eclipse.zest.layouts.LayoutAlgorithm; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; +import org.eclipse.zest.layouts.LayoutContext; -public class CompositeLayoutAlgorithm extends AbstractLayoutAlgorithm { +public class CompositeLayoutAlgorithm implements LayoutAlgorithm { - LayoutAlgorithm[] algorithms = null; + private LayoutAlgorithm[] algorithms = null; - public CompositeLayoutAlgorithm(int styles, LayoutAlgorithm[] algoirthms) { - super(styles); - this.algorithms = algoirthms; - } - - public CompositeLayoutAlgorithm( LayoutAlgorithm[] algoirthms) { - this(0, algoirthms); + public CompositeLayoutAlgorithm(LayoutAlgorithm[] algorithms) { + this.algorithms = algorithms; } - protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double boundsX, double boundsY, double boundsWidth, double boundsHeight) { - + public void applyLayout() { for (int i = 0; i < algorithms.length; i++) { - try { - algorithms[i].applyLayout(entitiesToLayout, relationshipsToConsider, boundsX, boundsY, boundsWidth, boundsHeight, this.internalAsynchronous, this.internalContinuous); - } catch (InvalidLayoutConfiguration e) { - e.printStackTrace(); - } + algorithms[i].applyLayout(); } - for (int i = 0; i < entitiesToLayout.length; i++) { - entitiesToLayout[i].getLayoutEntity().setLocationInLayout(entitiesToLayout[i].getXInLayout(), entitiesToLayout[i].getYInLayout()); - } - - //updateLayoutLocations(entitiesToLayout); - } - - protected int getCurrentLayoutStep() { - // TODO Auto-generated method stub - return 0; - } - - protected int getTotalNumberOfLayoutSteps() { - // TODO Auto-generated method stub - return 0; - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { - // TODO Auto-generated method stub - return true; } - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - // TODO Auto-generated method stub - - } - - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // TODO Auto-generated method stub - - } - - public void setLayoutArea(double x, double y, double width, double height) { - // TODO Auto-generated method stub - + public void setLayoutContext(LayoutContext context) { + for (int i = 0; i < algorithms.length; i++) { + algorithms[i].setLayoutContext(context); + } } - } Index: src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java --- src/org/eclipse/zest/layouts/algorithms/HorizontalShift.java 12 Sep 2007 20:44:37 -0000 1.6 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,131 +0,0 @@ -/******************************************************************************* - * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; - -/** - * This layout shifts overlapping nodes to the right. - * @author Ian Bull - */ -public class HorizontalShift extends AbstractLayoutAlgorithm { - - private static final double DELTA = 10; - private static final double VSPACING = 2; - - public HorizontalShift(int styles) { - super(styles); - } - - protected void applyLayoutInternal(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, - double boundsX, double boundsY, double boundsWidth, double boundsHeight) { - - ArrayList row = new ArrayList(); - for ( int i =0; i < entitiesToLayout.length; i++) { - addToRowList(entitiesToLayout[i], row); - } - - int heightSoFar = 0; - - Collections.sort(row, new Comparator() { - - public int compare(Object arg0, Object arg1) { - // TODO Auto-generated method stub - List a0 = (List) arg0; - List a1 = (List) arg1; - LayoutEntity node0 = ((InternalNode)a0.get(0)).getLayoutEntity(); - LayoutEntity node1 = ((InternalNode)a1.get(0)).getLayoutEntity(); - return (int) (node0.getYInLayout() - (node1.getYInLayout())); - } - - }); - - Iterator iterator = row.iterator(); - while (iterator.hasNext() ) { - List currentRow = (List) iterator.next(); - Collections.sort(currentRow, new Comparator() { - public int compare(Object arg0, Object arg1) { - return (int) (((InternalNode)arg1).getLayoutEntity().getYInLayout() - ((InternalNode)arg0).getLayoutEntity().getYInLayout()); - } - }); - Iterator iterator2 = currentRow.iterator(); - int i = 0; - int width = (int) ((boundsWidth / 2) - currentRow.size() * 75); - - heightSoFar += ((InternalNode)currentRow.get(0)).getLayoutEntity().getHeightInLayout() + VSPACING*8 ; - while(iterator2.hasNext()) { - InternalNode currentNode = (InternalNode) iterator2.next(); - - double location = width + 10*++i; - currentNode.setLocation(location , heightSoFar); - width += currentNode.getLayoutEntity().getWidthInLayout(); - } - } - } - - - private void addToRowList( InternalNode node, ArrayList list) { - double layoutY = node.getLayoutEntity().getYInLayout(); - - for ( int i = 0; i < list.size(); i++ ) { - List currentRow = (List) list.get(i); - InternalNode currentRowNode = (InternalNode) currentRow.get(0); - double currentRowY = currentRowNode.getLayoutEntity().getYInLayout(); - //double currentRowHeight = currentRowNode.getLayoutEntity().getHeightInLayout(); - if ( layoutY >= (currentRowY-DELTA) && layoutY <= currentRowY + DELTA ) { - currentRow.add(node); - //list.add(i, currentRow); - return; - } - } - List newRow = new ArrayList(); - newRow.add(node); - list.add(newRow); - } - - protected int getCurrentLayoutStep() { - // TODO Auto-generated method stub - return 0; - } - - protected int getTotalNumberOfLayoutSteps() { - // TODO Auto-generated method stub - return 0; - } - - protected boolean isValidConfiguration(boolean asynchronous, boolean continuous) { - // TODO Auto-generated method stub - return true; - } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - // TODO Auto-generated method stub - } - - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, - double x, double y, double width, double height) { - // TODO Auto-generated method stub - - } - - public void setLayoutArea(double x, double y, double width, double height) { - // TODO Auto-generated method stub - } -} - Index: src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java,v retrieving revision 1.5 diff -u -r1.5 RadialLayoutAlgorithm.java --- src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.5 +++ src/org/eclipse/zest/layouts/algorithms/RadialLayoutAlgorithm.java 10 Jul 2009 18:49:26 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -7,143 +7,90 @@ * * Contributors: * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms; -import java.util.Iterator; -import java.util.List; +package org.eclipse.zest.layouts.algorithms; -import org.eclipse.zest.layouts.LayoutStyles; +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; -import org.eclipse.zest.layouts.dataStructures.InternalRelationship; - /** - * This layout will take the given entities, apply a tree layout to them, and then display the - * tree in a circular fashion with the roots in the center. + * This layout will take the given entities, apply a tree layout to them, and + * then display the tree in a circular fashion with the roots in the center. * * @author Casey Best * @auhtor Rob Lintern */ -public class RadialLayoutAlgorithm extends TreeLayoutAlgorithm { +public class RadialLayoutAlgorithm implements LayoutAlgorithm { + private static final double MAX_DEGREES = Math.PI * 2; - private double startDegree; - private double endDegree; - private TreeLayoutAlgorithm treeLayout; - private List roots; - - - /** - * Creates a radial layout with no style. - */ - public RadialLayoutAlgorithm() { - this ( LayoutStyles.NONE ); - } - - //TODO: This is a really strange pattern. It extends tree layout and it contains a tree layout ? - public RadialLayoutAlgorithm ( int styles ) { - super( styles ); - treeLayout = new TreeLayoutAlgorithm( styles ); - startDegree = 0; - endDegree = MAX_DEGREES; - } - - public void setLayoutArea(double x, double y, double width, double height) { - throw new RuntimeException("Operation not implemented"); - } - + private double startDegree = 0; + private double endDegree = MAX_DEGREES; + + private LayoutContext context; + private boolean resize = false; + + private TreeLayoutAlgorithm treeLayout = new TreeLayoutAlgorithm(); - protected boolean isValidConfiguration(boolean asynchronous, boolean continueous) { - if ( asynchronous && continueous ) return false; - else if ( asynchronous && !continueous ) return true; - else if ( !asynchronous && continueous ) return false; - else if ( !asynchronous && !continueous ) return true; - - return false; + public void applyLayout() { + EntityLayout[] entities = context.getEntities(); + treeLayout.internalApplyLayout(entities); + DisplayIndependentRectangle bounds = context.getBounds(); + computeRadialPositions(entities, bounds); + if (resize) + AlgorithmHelper.maximizeSizes(entities); + AlgorithmHelper.fitWithinBounds(entities, bounds, resize); + } + + private void computeRadialPositions(EntityLayout[] entities, DisplayIndependentRectangle bounds) { + DisplayIndependentRectangle layoutBounds = AlgorithmHelper.getLayoutBounds(entities, false); + layoutBounds.x = bounds.x; + layoutBounds.width = bounds.width; + for (int i = 0; i < entities.length; i++) { + DisplayIndependentPoint location = entities[i].getLocation(); + double percenttheta = (location.x - layoutBounds.x) / layoutBounds.width; + double distance = (location.y - layoutBounds.y) / layoutBounds.height; + double theta = startDegree + Math.abs(endDegree - startDegree) * percenttheta; + location.x = distance * Math.cos(theta); + location.y = distance * Math.sin(theta); + entities[i].setLocation(location.x, location.y); + } } - - DisplayIndependentRectangle layoutBounds = null; - protected void preLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider, double x, double y, double width, double height) { - // TODO Auto-generated method stub - layoutBounds = new DisplayIndependentRectangle(x, y, width, height); - super.preLayoutAlgorithm(entitiesToLayout, relationshipsToConsider, x, y, - width, height); + public void setLayoutContext(LayoutContext context) { + this.context = context; + treeLayout.setLayoutContext(context); } - - protected void postLayoutAlgorithm(InternalNode[] entitiesToLayout, InternalRelationship[] relationshipsToConsider) { - roots = treeLayout.getRoots(); - computeRadialPositions (entitiesToLayout, layoutBounds); - - defaultFitWithinBounds(entitiesToLayout, layoutBounds); - - super.postLayoutAlgorithm(entitiesToLayout, relationshipsToConsider); - } - /** - * Set the range the radial layout will use when applyLayout is called. - * Both values must be in radians. + * Set the range the radial layout will use when applyLayout is called. Both + * values must be in radians. */ - public void setRangeToLayout (double startDegree, double endDegree) { - this.startDegree = startDegree; - this.endDegree = endDegree; + public void setRangeToLayout(double startDegree, double endDegree) { + this.startDegree = startDegree; + this.endDegree = endDegree; } - - /** - * Take the tree and make it round. This is done by determining the location of each entity in terms - * of its percentage in the tree layout. Then apply that percentage to the radius and distance from - * the center. - */ - protected void computeRadialPositions (InternalNode[] entities, DisplayIndependentRectangle bounds2) { //TODO TODO TODO - DisplayIndependentRectangle bounds = new DisplayIndependentRectangle(getLayoutBounds(entities, true)); - bounds.height = bounds2.height; - bounds.y = bounds2.y; - for (int i = 0; i < entities.length; i++) { - InternalNode entity = entities[i]; - double percentTheta = (entity.getInternalX() - bounds.x) / bounds.width; - double distance = (entity.getInternalY() - bounds.y) / bounds.height; - double theta = startDegree + Math.abs(endDegree - startDegree) * percentTheta; - double newX = distance * Math.cos (theta); - double newY = distance * Math.sin (theta); - - entity.setInternalLocation( newX, newY ); - } - } - + /** - * Find the bounds in which the nodes are located. Using the bounds against the real bounds - * of the screen, the nodes can proportionally be placed within the real bounds. - * The bounds can be determined either including the size of the nodes or not. If the size - * is not included, the bounds will only be guaranteed to include the center of each node. + * + * @return true if this algorithm is set to resize elements */ - protected DisplayIndependentRectangle getLayoutBounds (InternalNode[] entitiesToLayout, boolean includeNodeSize) { - DisplayIndependentRectangle layoutBounds = super.getLayoutBounds(entitiesToLayout, includeNodeSize); - DisplayIndependentPoint centerPoint = (roots != null) ? determineCenterPoint(roots) : - new DisplayIndependentPoint (layoutBounds.x + layoutBounds.width / 2, layoutBounds.y + layoutBounds.height / 2); - // The center entity is determined in applyLayout - double maxDistanceX = Math.max( - Math.abs (layoutBounds.x + layoutBounds.width - centerPoint.x), - Math.abs (centerPoint.x - layoutBounds.x)); - double maxDistanceY = Math.max( - Math.abs (layoutBounds.y + layoutBounds.height - centerPoint.y), - Math.abs (centerPoint.y - layoutBounds.y)); - layoutBounds = new DisplayIndependentRectangle (centerPoint.x - maxDistanceX, centerPoint.y - maxDistanceY, maxDistanceX * 2, maxDistanceY * 2); - return layoutBounds; + public boolean isResizing() { + return resize; } - + /** - * Find the center point between the roots + * + * @param resizing + * true if this algorithm should resize elements (default is + * false) */ - private DisplayIndependentPoint determineCenterPoint (List roots) { - double totalX = 0, totalY = 0; - for ( Iterator iterator = roots.iterator(); iterator.hasNext(); ) { - InternalNode entity = (InternalNode)iterator.next(); - totalX += entity.getInternalX(); - totalY += entity.getInternalY(); - } - return new DisplayIndependentPoint (totalX / roots.size(), totalY / roots.size()); + public void setResizing(boolean resizing) { + resize = resizing; + treeLayout.setResizing(resize); } } Index: src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java diff -N src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java --- src/org/eclipse/zest/layouts/algorithms/internal/DynamicScreen.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,126 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.algorithms.internal; - -import java.util.Comparator; -import java.util.TreeSet; - -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; -import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; -import org.eclipse.zest.layouts.dataStructures.InternalNode; - - -public class DynamicScreen { - - private TreeSet XCoords = null; - private TreeSet YCoords = null; - - - - private DisplayIndependentRectangle screenBounds = null; - - - double minX = 0.0; - double minY = 0.0; - double maxX = 0.0; - double maxY = 0.0; - public void cleanScreen() { - minX = 0.0; - minY = 0.0; - maxX = 0.0; - maxY = 0.0; - } - - class XComparator implements Comparator { - public int compare(Object arg0, Object arg1) { - InternalNode n1 = (InternalNode)arg0; - InternalNode n2 = (InternalNode)arg1; - if ( n1.getInternalX() > n2.getInternalX() ) return +1; - else if ( n1.getInternalX() < n2.getInternalX() ) return -1; - else { - return n1.toString().compareTo( n2.toString() ); - } - - } - } - class YComparator implements Comparator { - public int compare(Object arg0, Object arg1) { - InternalNode n1 = (InternalNode)arg0; - InternalNode n2 = (InternalNode)arg1; - if ( n1.getInternalY() > n2.getInternalY() ) return +1; - else if ( n1.getInternalY() < n2.getInternalY() ) return -1; - else { - return n1.toString().compareTo( n2.toString() ); - } - - } - } - - - - - public DynamicScreen(int x, int y, int width, int height) { - XCoords = new TreeSet(new XComparator()); - YCoords = new TreeSet(new YComparator()); - - - - this.screenBounds = new DisplayIndependentRectangle(x,y,width,height); - } - - - public void removeNode( InternalNode node ) { - XCoords.remove( node ); - YCoords.remove( node ); - } - - public void addNode( InternalNode node ) { - XCoords.add( node ); - YCoords.add( node ); - } - - public DisplayIndependentPoint getScreenLocation( InternalNode node ) { - - DisplayIndependentRectangle layoutBounds = calculateBounds(); - - double x = (layoutBounds.width == 0) ? 0 : (node.getInternalX() - layoutBounds.x) / layoutBounds.width; - double y = (layoutBounds.height == 0) ? 0 : (node.getInternalY() - layoutBounds.y) / layoutBounds.height; - - x = screenBounds.x + x * screenBounds.width; - y = screenBounds.y + y * screenBounds.height; - - return new DisplayIndependentPoint( x, y ); - } - - public DisplayIndependentPoint getVirtualLocation( DisplayIndependentPoint point ) { - - DisplayIndependentRectangle layoutBounds = calculateBounds(); - - - double x = (point.x/screenBounds.width) * layoutBounds.width + layoutBounds.x; - double y = (point.y/screenBounds.height) * layoutBounds.height + layoutBounds.y; - - return new DisplayIndependentPoint( x, y ); - } - - private DisplayIndependentRectangle calculateBounds() { - InternalNode n1 = (InternalNode) XCoords.first(); - InternalNode n2 = (InternalNode) XCoords.last(); - InternalNode n3 = (InternalNode) YCoords.first(); - InternalNode n4 = (InternalNode) YCoords.last(); - double x = n1.getInternalX(); - double width = n2.getInternalX(); - double y = n3.getInternalY(); - double height = n4.getInternalY(); - return new DisplayIndependentRectangle(x, y, width - x, height - y); - } - -} Index: src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java diff -N src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java --- src/org/eclipse/zest/layouts/algorithms/internal/CycleChecker.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,119 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ - -package org.eclipse.zest.layouts.algorithms.internal; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.algorithms.AbstractLayoutAlgorithm; -import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; - - -/** - * Checks for cycles in the given graph. - * - * @author Casey Best - */ -public class CycleChecker { - /** - * Tests if there is a directed cirlce in the graph formed by the given entities and relationships. - * @param entities The entities in the graph to check - * @param relationships The relationships in the graph to check - * @param cycle Populated with the cycle encountered, if there is one. - * @throws RuntimeException Thrown if entities doesn't contain all of the endpoints for each relationship in relationships - * @return true if there is a directed circle. - * Otherwise, false. - */ - public static boolean hasDirectedCircles(LayoutEntity[] entities, LayoutRelationship[] relationships, List cycle) { - if (!AbstractLayoutAlgorithm.verifyInput(entities, relationships)) { - throw new RuntimeException ("The endpoints of the relationships aren't contained in the entities list."); - } - //Enumeration enum; - //Iterator iterator; - - Hashtable endPoints = new Hashtable(); - - // Initialize the relation(transitive) vector. - for (int i = 0; i < relationships.length; i++) { - LayoutRelationship rel = relationships[i]; - - //Add the relationship to the source endpoint - Object subject = rel.getSourceInLayout(); - List rels = (List) endPoints.get(subject); - if (rels == null) { - rels = new ArrayList(); - endPoints.put(subject, rels); - } - if (!rels.contains(rel)) - rels.add(rel); - } - boolean hasCyle = hasCycle(new ArrayList(Arrays.asList(entities)), endPoints, cycle); - return hasCyle; - } - - /** - * Check passed in nodes for a cycle - */ - private static boolean hasCycle(List nodesToCheck, Hashtable endPoints, List cycle) { - while (nodesToCheck.size() > 0) { - Object checkNode = nodesToCheck.get(0); - List checkedNodes = new ArrayList(); - if (hasCycle(checkNode, new ArrayList(), null, endPoints, checkedNodes, cycle)) { - return true; - } - nodesToCheck.removeAll(checkedNodes); - } - return false; - } - - /** - * Checks all the nodes attached to the nodeToCheck node for a cycle. All nodes - * checked are placed in nodePathSoFar. - * - * @returns true if there is a cycle - */ - private static boolean hasCycle(Object nodeToCheck, List nodePathSoFar, SimpleRelationship cameFrom, Hashtable endPoints, List nodesChecked, List cycle) { - if (nodePathSoFar.contains(nodeToCheck)) { - cycle.addAll(nodePathSoFar); - cycle.add(nodeToCheck); - return true; - } - nodePathSoFar.add(nodeToCheck); - nodesChecked.add(nodeToCheck); - - List relations = (List) endPoints.get(nodeToCheck); - if (relations != null) { - for (Iterator iter = relations.iterator(); iter.hasNext();) { - SimpleRelationship rel = (SimpleRelationship) iter.next(); - - if (cameFrom == null || !rel.equals(cameFrom)) { - Object currentNode = null; - currentNode = rel.getDestinationInLayout(); - if (hasCycle(currentNode, nodePathSoFar, rel, endPoints, nodesChecked, cycle)) { - return true; - } - } - - } - } - - nodePathSoFar.remove(nodeToCheck); - - return false; - } - -} Index: src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java diff -N src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java --- src/org/eclipse/zest/layouts/constraints/BasicEntityConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.constraints; - -/** - * - * @author Chris Bennett - * - */ -public class BasicEntityConstraint implements LayoutConstraint { - - - public boolean hasPreferredLocation = false; - - public double preferredX; - public double preferredY; - - public boolean hasPreferredSize = false; - public double preferredWidth; - public double preferredHeight; - - public BasicEntityConstraint() { - clear(); - } - - /* - * (non-Javadoc) - * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() - */ - public void clear() { - this.hasPreferredLocation = false; - this.hasPreferredSize = false; - this.preferredX = 0.0; - this.preferredY = 0.0; - this.preferredWidth = 0.0; - this.preferredHeight = 0.0; - } -} Index: src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java diff -N src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java --- src/org/eclipse/zest/layouts/constraints/EntityPriorityConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.constraints; - -/** - * A layout constraint that uses priorities - * @author Ian Bull - */ -public class EntityPriorityConstraint implements LayoutConstraint { - - // A priority that can be set for nodes. This could be used - // for a treemap layout - public double priority = 1.0; - - /* - * (non-Javadoc) - * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() - */ - public void clear() { - this.priority = 1.0; - } - -} Index: src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java diff -N src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java --- src/org/eclipse/zest/layouts/constraints/LayoutConstraint.java 12 Sep 2007 20:44:38 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.constraints; - -/** - * @author Ian Bull - * @author Chris Bennett - */ -public interface LayoutConstraint { - - // Empty interface - - /** - * This method clears all the fields of the layout constraints. - * This should not be called outside the layout package - */ - public void clear(); -} Index: src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java =================================================================== RCS file: src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java diff -N src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java --- src/org/eclipse/zest/layouts/constraints/BasicEdgeConstraints.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.constraints; - -/** - * @author Ian Bull - * @author Chris Bennett - */ -public class BasicEdgeConstraints implements LayoutConstraint { - - // These should all be accessed directly. - public boolean isBiDirectional = false; - public int weight = 1; - - /* - * (non-Javadoc) - * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() - */ - public void clear() { - this.isBiDirectional = false; - this.weight = 1; - } - -} Index: src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java diff -N src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java --- src/org/eclipse/zest/layouts/constraints/LabelLayoutConstraint.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.constraints; - -/** - * @author Ian Bull - * @author Chris Bennett - */ -public class LabelLayoutConstraint implements LayoutConstraint { - - // These should be accessed directly - public String label; - public int pointSize; - - /* - * (non-Javadoc) - * @see org.eclipse.zest.layouts.constraints.LayoutConstraint#clear() - */ - public void clear() { - label = null; - pointSize = 1; - } - -} - Index: src/org/eclipse/zest/layouts/progress/ProgressEvent.java =================================================================== RCS file: src/org/eclipse/zest/layouts/progress/ProgressEvent.java diff -N src/org/eclipse/zest/layouts/progress/ProgressEvent.java --- src/org/eclipse/zest/layouts/progress/ProgressEvent.java 12 Sep 2007 20:44:38 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.progress; - -/** - * When an algorithm wants to notify everyone it has completely part of its task, it - * throws a ProgressEvent. The progress is a number (currentProgress) representing the - * current steps completed out of the total number of steps (totalProgress) - * - * @author Casey Best - */ -public class ProgressEvent { - int stepsCompleted; - int totalSteps; - - /** - * Creates a progress event. - * @param stepsCompleted The current progress out of the total - * @param totalNumberOfSteps The number used to indicate when the algorithm will finish - */ - public ProgressEvent (int stepsCompleted, int totalNumberOfSteps) { - this.stepsCompleted = stepsCompleted; - this.totalSteps = totalNumberOfSteps; - } - - /** - * Returns the number of steps already completed. - */ - public int getStepsCompleted() { - return stepsCompleted; - } - - /** - * Returns the total number of steps to complete. - */ - public int getTotalNumberOfSteps() { - return totalSteps; - } -} Index: src/org/eclipse/zest/layouts/progress/ProgressListener.java =================================================================== RCS file: src/org/eclipse/zest/layouts/progress/ProgressListener.java diff -N src/org/eclipse/zest/layouts/progress/ProgressListener.java --- src/org/eclipse/zest/layouts/progress/ProgressListener.java 12 Sep 2007 20:44:38 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.progress; - - -/** - * Listens for ProgressEvents which are thrown by layout algorithms at frequent intervals. - * - * @author Ian Bull - * @author Casey Best - */ -public interface ProgressListener { - - /** - * - * @param e - */ - public void progressStarted( ProgressEvent e ); - - /** - * Called when the progress of a layout changes - */ - public void progressUpdated (ProgressEvent e); - - /** - * - * @param e - */ - public void progressEnded( ProgressEvent e ); -} Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java --- src/org/eclipse/zest/layouts/exampleStructures/SimpleNode.java 12 Jan 2008 01:44:24 -0000 1.9 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,318 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.exampleStructures; - -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.TreeSet; - -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; -import org.eclipse.zest.layouts.constraints.EntityPriorityConstraint; -import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint; -import org.eclipse.zest.layouts.constraints.LayoutConstraint; - -/** - * Rerpresents a simple node that can be used in the layout algorithms. - * - * @author Ian Bull - * @author Casey Best (Version 1 by Rob Lintern) - * @version 2 - */ -public class SimpleNode implements LayoutEntity { - private static Object NODE_NORMAL_COLOR; - private static Object NODE_SELECTED_COLOR; - private static Object NODE_ADJACENT_COLOR; - private static Object BORDER_NORMAL_COLOR; - private static Object BORDER_SELECTED_COLOR; - private static Object BORDER_ADJACENT_COLOR; - - private static final int BORDER_NORMAL_STROKE = 1; - private static final int BORDER_STROKE_SELECTED = 2; - - /** - * A list of layout dependent attributes - */ - private Map attributes; - - protected double x, y, width, height; - protected Object realObject; - private boolean ignoreInLayout = false; - - private Object colour = null; - private Object borderColor = null; - private int borderWidth; - - private TreeSet listOfRels = null; - - private Object internalNode; - - - /** - * Constructs a new SimpleNode. - */ - public SimpleNode(Object realObject) { - this (realObject, -1, -1, 110, 110); - } - - class UniqueCompare implements Comparator { - public int compare(Object o1, Object o2) { - // TODO this may not always be a unique comparison - return o1.toString().compareTo( o2.toString() ); - } - } - - /** - * Constructs a new SimpleNode. - */ - public SimpleNode(Object realObject, double x, double y, double width, double height) { - this.realObject = realObject; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - this.attributes = new HashMap(); - this.borderWidth = BORDER_NORMAL_STROKE; - listOfRels = new TreeSet( new UniqueCompare() ); - this.colour = NODE_NORMAL_COLOR; - this.borderColor = BORDER_NORMAL_COLOR; - } - - public static void setNodeColors (Object nodeNormalColor, Object borderNormalColor, Object nodeSelectedColor, Object nodeAdjacentColor, Object borderSelectedColor, Object borderAdjacentColor) { - NODE_NORMAL_COLOR = nodeNormalColor; - BORDER_NORMAL_COLOR = borderNormalColor; - NODE_SELECTED_COLOR = nodeSelectedColor; - NODE_ADJACENT_COLOR = nodeAdjacentColor; - BORDER_SELECTED_COLOR = borderSelectedColor; - BORDER_ADJACENT_COLOR = borderAdjacentColor; - } - - public void addRelationship( SimpleRelationship rel ) { - listOfRels.add( rel ); - } - - - public SimpleRelationship[] getRelationships() { - int size = listOfRels.size(); - return (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] ); - } - - public List getRelatedEntities() { - int size = listOfRels.size(); - SimpleRelationship[] a_listOfRels = (SimpleRelationship[]) this.listOfRels.toArray( new SimpleRelationship[ size ] ); - LinkedList listOfRelatedEntities = new LinkedList(); - for (int i = 0; i < a_listOfRels.length; i++) { - SimpleRelationship rel = a_listOfRels[ i ]; - if ( rel.sourceEntity != this && rel.destinationEntity != this ) { - throw new RuntimeException("Problem, we have a relationship and we are not the source or the dest"); - } - if ( rel.sourceEntity != this ) { - listOfRelatedEntities.add( rel.sourceEntity ); - } - if ( rel.destinationEntity != this ) { - listOfRelatedEntities.add( rel.destinationEntity ); - } - - } - return listOfRelatedEntities; - } - - - - /** - * Ignores this entity in the layout - * @param ignore Should this entity be ignored - */ - public void ignoreInLayout( boolean ignore ) { - this.ignoreInLayout = ignore; - } - - public Object getRealObject() { - return realObject; - } - - public boolean hasPreferredLocation() { - return this.ignoreInLayout; - } - - /** - * Gets the x position of this SimpleNode. - */ - public double getX() { - return x; - } - - /** - * Gets the y position of this SimpleNode. - */ - public double getY() { - return y; - } - - /** - * Get the size of this node - */ - public double getWidth() { - return width; - } - - /** - * Get the size of this node - */ - public double getHeight() { - return height; - } - - public void setSizeInLayout(double width, double height) { - if (!ignoreInLayout) { - this.width = width; - this.height = height; - } - } - - public void setLocation( double x, double y ) { - this.x = x; - this.y = y; - } - - public void setLocationInLayout(double x, double y) { - if (!ignoreInLayout) { - this.x = x; - this.y = y; - } - } - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public void setAttributeInLayout (Object attribute, Object value) { - attributes.put (attribute, value); - } - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public Object getAttributeInLayout (Object attribute) { - return attributes.get (attribute); - } - - public boolean equals(Object object) { - boolean result = false; - if (object instanceof SimpleNode) { - SimpleNode node = (SimpleNode)object; - result = realObject.equals (node.getRealObject()); - } - return result; - } - - public int hashCode() { - return realObject.hashCode(); - } - - // all objects are equal - public int compareTo(Object arg0) { - return 0; - } - - public String toString() { - return realObject.toString(); - } - - public void setSelected() { - this.colour = NODE_SELECTED_COLOR; - this.borderColor = BORDER_SELECTED_COLOR; - this.borderWidth = BORDER_STROKE_SELECTED; - } - - public void setUnSelected() { - this.colour = NODE_NORMAL_COLOR; - this.borderColor = BORDER_NORMAL_COLOR; - this.borderWidth = BORDER_NORMAL_STROKE; - } - - public void setAdjacent() { - this.colour = NODE_ADJACENT_COLOR; - this.borderColor = BORDER_ADJACENT_COLOR; - this.borderWidth = BORDER_STROKE_SELECTED; - } - - public Object getColor() { - return this.colour; - } - - public int getBorderWidth() { - return borderWidth; - } - - public Object getBorderColor() { - return borderColor; - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutEntity#getInternalEntity() - */ - public Object getLayoutInformation() { - return internalNode; - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutEntity#setInternalEntity(java.lang.Object) - */ - public void setLayoutInformation(Object internalEntity) { - this.internalNode = internalEntity; - } - - /** - * Populate the specified layout constraint - */ - public void populateLayoutConstraint(LayoutConstraint constraint) { - if ( constraint instanceof LabelLayoutConstraint ) { - LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint; - labelConstraint.label = realObject.toString(); - labelConstraint.pointSize = 18; - } - else if ( constraint instanceof BasicEntityConstraint ) { - // noop - } - else if ( constraint instanceof EntityPriorityConstraint ) { - EntityPriorityConstraint priorityConstraint = (EntityPriorityConstraint) constraint; - priorityConstraint.priority = Math.random() * 10 + 1; - } - } - - public double getHeightInLayout() { - return this.height; - } - - public double getWidthInLayout() { - return this.width; - } - - public double getXInLayout() { - return this.x; - } - - public double getYInLayout() { - return this.y; - } - - public Object getGraphData() { - return null; - } - - public void setGraphData(Object o) { - - } - -} Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java --- src/org/eclipse/zest/layouts/exampleStructures/SimpleGraph.java 12 Sep 2007 20:44:37 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,140 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.exampleStructures; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutGraph; -import org.eclipse.zest.layouts.LayoutRelationship; - - -/** - * Create a very simple graph that can be used in the layout algorithms - * - * @author Casey Best - * @author Chris Callendar - */ -public class SimpleGraph implements LayoutGraph { - - Map objectsToNodes; - List relationships; - - public SimpleGraph() { - objectsToNodes = new HashMap(); - relationships = new ArrayList(); - } - - /** - * Adds the node. - * @param node The node to add. - */ - public void addEntity(LayoutEntity node) { - if (node instanceof SimpleNode) { - objectsToNodes.put(((SimpleNode)node).getRealObject(), node); - } - } - - /** - * Creates a LayoutEntity containing an object. - */ - public LayoutEntity addObjectNode(Object object) { - SimpleNode simpleNode = (SimpleNode) objectsToNodes.get (object); - if (simpleNode == null) { - simpleNode = new SimpleNode(object); - objectsToNodes.put (object, simpleNode); - } - return simpleNode; - - } - - /** - * Add a relationship between two objects. Layout algorithms need to know - * whether a relationship is one way or bi-directional. This method assumes that - * all relationships are bi-direcional and have the same weight. - */ - public void addObjectRelationship (Object sourceNode, Object destinationNode) { - addObjectRelationship(sourceNode, destinationNode, true, 1); - } - - /** - * Add a relationship between two objects. Layout algorithms need to know - * whether a relationship is one way or bi-directional. - */ - public void addObjectRelationship (Object sourceObject, Object destinationObject, boolean bidirectional, int weight) { - addObjectNode(sourceObject); - addObjectNode(destinationObject); - SimpleNode sourceNode = (SimpleNode) objectsToNodes.get(sourceObject); - SimpleNode destinationNode = (SimpleNode) objectsToNodes.get(destinationObject); - SimpleRelationship simpleRelationship = new SimpleRelationship(sourceNode, destinationNode, bidirectional, weight); - relationships.add(simpleRelationship); - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity) - */ - public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode) { - addRelationship(srcNode, destNode, true, 1); - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutEntity, ca.uvic.cs.chisel.layouts.LayoutEntity, boolean, int) - */ - public void addRelationship(LayoutEntity srcNode, LayoutEntity destNode, boolean bidirectional, int weight) { - addEntity(srcNode); - addEntity(destNode); - SimpleRelationship rel = new SimpleRelationship(srcNode, destNode, bidirectional, weight); - relationships.add(rel); - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutGraph#addRelationship(ca.uvic.cs.chisel.layouts.LayoutRelationship) - */ - public void addRelationship(LayoutRelationship relationship) { - relationships.add(relationship); - } - - /** - * Returns a list of SimpleNodes that represent the objects added to this graph using addNode. Note that - * any manipulation to this graph was done on the SimpleNodes, not the real objects. You - * must still manipulate them yourself. - */ - public List getEntities() { - return new ArrayList (objectsToNodes.values()); - } - - /** - * Returns a list of SimpleRelationships that represent the objects added to this graph using addRelationship. - */ - public List getRelationships() { - return relationships; - } - - /** - * Checks the relationships to see if they are all bidirectional. - * @return boolean if all edges are bidirectional. - */ - public boolean isBidirectional() { - boolean isBidirectional = true; - for (Iterator iter = relationships.iterator(); iter.hasNext(); ) { - SimpleRelationship rel = (SimpleRelationship) iter.next(); - if (!rel.isBidirectionalInLayout()) { - isBidirectional = false; - break; - } - } - return isBidirectional; - } -} Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java --- src/org/eclipse/zest/layouts/exampleStructures/SimpleRelationship.java 12 Jan 2008 01:44:24 -0000 1.7 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,274 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.exampleStructures; - -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.zest.layouts.LayoutBendPoint; -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints; -import org.eclipse.zest.layouts.constraints.LabelLayoutConstraint; -import org.eclipse.zest.layouts.constraints.LayoutConstraint; -import org.eclipse.zest.layouts.dataStructures.BendPoint; - - -/** - * The SimpleRelation class describes the relationship between - * two objects: source and destination. Each relationship - * has a weight and direction associated with it. - * Note: The source object is at the beginning of the relationship. - * Note: The destination object is at the end of the relationship. - * - * @version 2.0 - * @author Casey Best (version 1.0 by Jingwei Wu) - * @author Chris Bennett - */ -public class SimpleRelationship implements LayoutRelationship { - - private static int DEFAULT_REL_LINE_WIDTH = 1; - private static int DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2; - private static Object DEFAULT_RELATIONSHIP_COLOR; - private static Object DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR; - - /** The line width for this relationship. */ - private int lineWidth = DEFAULT_REL_LINE_WIDTH; - - /** The color for this relationship. */ - private Object color = DEFAULT_RELATIONSHIP_COLOR; - - /** - * A list of layout dependent attributes - */ - private Map attributes; - - /** - * The sourceEntity of this SimpleRelation. - */ - protected LayoutEntity sourceEntity; - - /** - * The object of this SimpleRelation. - */ - protected LayoutEntity destinationEntity; - - /** - * If directional, algorithms must note the direction of the relationship. - * If not directional, algorithms are to ignore which direction the relationship is going. - * Switching the source and destination should make no difference. - */ - protected boolean bidirectional; - - /** - * The weight given to this relation. - */ - private double weight; - - private Object internalRelationship; - - private LayoutBendPoint[] bendPoints; - - private String label; - - /** - * Constructor. - * @param sourceEntity The sourceEntity of this SimpleRelation. - * @param destinationEntity The object of this SimpleRelation. - * @param bidirectional Determines if the sourceEntity and - * destinationEntity are equal(exchangeable). - * @throws java.lang.NullPointerException If either sourceEntity - * or destinationEntity is null. - */ - public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional) { - this (sourceEntity, destinationEntity, bidirectional, 1); - } - - /** - * Constructor. - * @param sourceEntity The sourceEntity of this SimpleRelation. - * @param destinationEntity The destinationEntity of this SimpleRelation. - * @param exchangeable Determines if the sourceEntity and - * destinationEntity are equal(exchangeable). - * @throws java.lang.NullPointerException If either sourceEntity - * or destinationEntity is null. - */ - public SimpleRelationship(LayoutEntity sourceEntity, LayoutEntity destinationEntity, boolean bidirectional, double weight) { - this.destinationEntity = destinationEntity; - this.sourceEntity = sourceEntity; - this.bidirectional = bidirectional; - this.weight = weight; - this.attributes = new HashMap(); - this.lineWidth = DEFAULT_REL_LINE_WIDTH; - this.color = DEFAULT_RELATIONSHIP_COLOR; - } - - /** - * Gets the sourceEntity of this SimpleRelation whether the relation is - * exchangeable or not. - * @return The sourceEntity. - */ - public LayoutEntity getSourceInLayout() { - return sourceEntity; - } - - /** - * Gets the destinationEntity of this SimpleRelation whether the relation is - * exchangeable or not. - * @return The destinationEntity of this SimpleRelation. - */ - public LayoutEntity getDestinationInLayout() { - return destinationEntity; - } - - /** - * If bidirectional, the direction of the relationship doesn't matter. Switching the source and destination should make no difference. - * If not bidirectional, layout algorithms need to take into account the direction of the relationship. The direction is based on the - * source and destination entities. - */ - public boolean isBidirectionalInLayout() { - return bidirectional; - } - - public void setWeightInLayout(double weight) { - this.weight = weight; - } - - public double getWeightInLayout() { - return weight; - } - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public void setAttributeInLayout (String attribute, Object value) { - attributes.put (attribute, value); - } - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public Object getAttributeInLayout (String attribute) { - return attributes.get (attribute); - } - - public String toString() { - String arrow = (isBidirectionalInLayout() ? " <-> " : " -> "); - return "(" + sourceEntity + arrow + destinationEntity + ")"; - } - - public int getLineWidth() { - return this.lineWidth; - } - - public void setLineWidth( int lineWidth ) { - this.lineWidth = lineWidth; - } - - public void resetLineWidth() { - this.lineWidth = DEFAULT_REL_LINE_WIDTH; - } - - public static void setDefaultSize(int i) { - DEFAULT_REL_LINE_WIDTH = i; - DEFAULT_REL_LINE_WIDTH_SELECTED = DEFAULT_REL_LINE_WIDTH + 2; - } - - public void setSelected() { - this.color = DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR; - this.lineWidth = DEFAULT_REL_LINE_WIDTH_SELECTED; - } - - public void setUnSelected() { - this.color = DEFAULT_RELATIONSHIP_COLOR; - this.lineWidth = DEFAULT_REL_LINE_WIDTH; - } - - public Object getColor() { - return color; - } - - public void setColor(Object c) { - this.color = c; - } - - public static void setDefaultColor(Object c) { - DEFAULT_RELATIONSHIP_COLOR = c; - } - - public static void setDefaultHighlightColor(Object c) { - DEFAULT_RELATIONSHIP_HIGHLIGHT_COLOR = c; - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#getInternalRelationship() - */ - public Object getLayoutInformation() { - return internalRelationship; - } - - /* (non-Javadoc) - * @see ca.uvic.cs.chisel.layouts.LayoutRelationship#setInternalRelationship(java.lang.Object) - */ - public void setLayoutInformation(Object layoutInformation) { - this.internalRelationship = layoutInformation; - } - - - public void setBendPoints(LayoutBendPoint[] bendPoints) { - this.bendPoints = bendPoints; - } - - public LayoutBendPoint[] getBendPoints() { - return this.bendPoints; - } - - public void clearBendPoints() { - this.bendPoints = new BendPoint[0]; - } - - - public void setDestinationInLayout(LayoutEntity destination) { - this.destinationEntity = destination; - } - - /** - * Set the label for this edge (available in the label layout constraint). - */ - public void setLabel(String label) { - this.label = label; - } - - /** - * Populate the specified layout constraint - */ - public void populateLayoutConstraint(LayoutConstraint constraint) { - if ( constraint instanceof LabelLayoutConstraint ) { - LabelLayoutConstraint labelConstraint = (LabelLayoutConstraint) constraint; - labelConstraint.label = this.label; - labelConstraint.pointSize = 18; - } - else if ( constraint instanceof BasicEdgeConstraints ) { - // noop - - } - } - - public Object getGraphData() { - return null; - } - - public void setGraphData(Object o) { - - } - - -} Index: src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java diff -N src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java --- src/org/eclipse/zest/layouts/exampleStructures/SimpleFilter.java 12 Jan 2008 01:44:24 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.exampleStructures; - -import org.eclipse.zest.layouts.Filter; -import org.eclipse.zest.layouts.LayoutItem; - -/** - * A very simple example of a filter. This filter never filters - * any object. - * - * @author Casey Best - */ -public class SimpleFilter implements Filter { - - /** - * Doesn't filter anything - */ - public boolean isObjectFiltered(LayoutItem object) { - return false; - } -} Index: META-INF/MANIFEST.MF =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/META-INF/MANIFEST.MF,v retrieving revision 1.14 diff -u -r1.14 MANIFEST.MF --- META-INF/MANIFEST.MF 23 Apr 2009 03:41:00 -0000 1.14 +++ META-INF/MANIFEST.MF 10 Jul 2009 18:49:25 -0000 @@ -7,14 +7,6 @@ Bundle-Localization: plugin Export-Package: org.eclipse.zest.layouts, org.eclipse.zest.layouts.algorithms, - org.eclipse.zest.layouts.algorithms.internal, - org.eclipse.zest.layouts.constraints, - org.eclipse.zest.layouts.dataStructures, - org.eclipse.zest.layouts.exampleStructures, - org.eclipse.zest.layouts.exampleUses, - org.eclipse.zest.layouts.progress -Require-Bundle: org.eclipse.swt;visibility:=reexport, - org.eclipse.core.runtime, - org.eclipse.jface, - org.eclipse.draw2d + org.eclipse.zest.layouts.dataStructures +Require-Bundle: org.eclipse.draw2d Bundle-RequiredExecutionEnvironment: J2SE-1.4 Index: src/org/eclipse/zest/layouts/LayoutRelationship.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutRelationship.java diff -N src/org/eclipse/zest/layouts/LayoutRelationship.java --- src/org/eclipse/zest/layouts/LayoutRelationship.java 12 Jan 2008 01:42:53 -0000 1.9 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import org.eclipse.zest.layouts.constraints.LayoutConstraint; - - - -/** - * This represents a single relationship, providing the layout algorithms with - * a common interface to run on. - * - * @author Casey Best - * @author Chris Callendar - */ -public interface LayoutRelationship extends LayoutItem { - - - /** - * Gets the sourceEntity of this SimpleRelation whether the relation is - * exchangeable or not. - * @return The sourceEntity. - */ - public LayoutEntity getSourceInLayout(); - - /** - * Gets the destinationEntity of this SimpleRelation whether the relation is - * exchangeable or not. - * @return The destinationEntity of this SimpleRelation. - */ - public LayoutEntity getDestinationInLayout(); - - - /** - * Sets the internal relationship object. - * @param layoutInformation - */ - public void setLayoutInformation(Object layoutInformation); - - /** - * Returns the internal relationship object. - * @return Object - */ - public Object getLayoutInformation(); - - /** - * Specify a set of bend points. The layout algorithm using this will pass - * in an empty array of bendPoints, or not even call this method, - * if there are no bend points associated with this edge. - * - * If you are updating an existing application you can just implement this - * method to do nothing. - * - * @param bendPoints A list of bend points. All bendpoint locations are expressed - * as percentages of the bounds (0,0 to 1,1).The first bendpoint in the list must be the - * source point of this relationship and the last bendpoint the destination point - * for this relationship. This allows the correct positioning of bendpoints - * relative to the source and destination points when drawing the graph. - */ - public void setBendPoints(LayoutBendPoint[] bendPoints); - - /** - * Clear bend points and related bounds - * If you are updating an existing application you can just implement this - * method to do nothing. - */ - public void clearBendPoints(); - - /** - * Classes should update the specirfied layout constraint if recognized - * @return - */ - public void populateLayoutConstraint(LayoutConstraint constraint); -} Index: src/org/eclipse/zest/layouts/LayoutAlgorithm.java =================================================================== RCS file: /cvsroot/tools/org.eclipse.gef/plugins/org.eclipse.zest.layouts/src/org/eclipse/zest/layouts/LayoutAlgorithm.java,v retrieving revision 1.7 diff -u -r1.7 LayoutAlgorithm.java --- src/org/eclipse/zest/layouts/LayoutAlgorithm.java 12 Sep 2007 20:44:37 -0000 1.7 +++ src/org/eclipse/zest/layouts/LayoutAlgorithm.java 10 Jul 2009 18:49:25 -0000 @@ -1,111 +1,39 @@ /******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import java.util.Comparator; -import java.util.List; + * Mateusz Matela - initial API and implementation + ******************************************************************************/ -import org.eclipse.zest.layouts.progress.ProgressListener; +package org.eclipse.zest.layouts; /** - * A simple interface used by all layouts. - * - * Each layout Algorithm must implement the applyLayoutInternal method which actually compute the layout - * - * @author Casey Best - * @author Ian Bull + * An interface for all layout algorithms. */ public interface LayoutAlgorithm { /** - * Apply the layout to the given entities. The entities will be moved and resized based - * on the algorithm. + * Sets the layout context for this algorithm. The receiver will unregister + * from its previous layout context and register to the new one + * (registration means for example adding listeners). After a call to this + * method, the receiving algorithm can compute and cache internal data + * related to given context and perform an initial layout. * - * @param entitiesToLayout Apply the algorithm to these entities - * @param relationshipsToConsider Only consider these relationships when applying the algorithm. - * @param x The left side of the bounds in which the layout can place the entities. - * @param y The top side of the bounds in which the layout can place the entities. - * @param width The width of the bounds in which the layout can place the entities. - * @param height The height of the bounds in which the layout can place the entities. - * @param asynchronous Should the algorithm run Asynchronously - */ - public void applyLayout(LayoutEntity[] entitiesToLayout, LayoutRelationship[] relationshipsToConsider, double x, double y, double width, double height, boolean asynchronous, boolean continuous) throws InvalidLayoutConfiguration; - - /** - * Returns whether or not the algorithm is currenly running - * @return True if a layout algorithm is currenly running, false otherwise - */ - public boolean isRunning(); - - /** - * Determines the order in which the objects should be displayed. - * Note: Some algorithms force a specific order, in which case - * this comparator will be ignored. - */ - public void setComparator(Comparator comparator); - - /** - * Filters the entities and relationships to apply the layout on + * @param context + * a new layout context or null if this algorithm should not + * perform any layout */ - public void setFilter(Filter filter); + public void setLayoutContext(LayoutContext context); /** - * Set the width to height ratio you want the entities to use - * Note: Each layout is responsible for ensuring this ratio is used. - * Note: By default the layout will use a ratio of 1.0 for each entity. + * Makes this algorithm perform layout computation and apply it to its + * context. The receiver should assume that the layout context has changed + * significantly and recompute the layout even if it keeps track of changes + * with listeners. */ - public void setEntityAspectRatio(double ratio); - - /** - * Returns the width to height ratio this layout will use to set the size of the entities. - * Note: By default the layout will use a ratio of 1.0 for each entity. - */ - public double getEntityAspectRatio(); - - /** - * A layout algorithm could take an uncomfortable amout of time to complete. To relieve some of - * the mystery, the layout algorithm will notify each ProgressListener of its progress. - */ - public void addProgressListener(ProgressListener listener); - - /** - * Removes the given progress listener, preventing it from receiving any more updates. - */ - public void removeProgressListener(ProgressListener listener); - - /** - * Makes a request to this layout algorithm to stop running. - */ - public void stop(); - - /** - * Sets the style for this layout algorithm. This will overwrite any other style set. - * @param style - */ - public void setStyle(int style); - - /** - * - * @return - */ - public int getStyle(); - - public void addEntity(LayoutEntity entity); - - public void addRelationship(LayoutRelationship relationship); - - public void removeEntity(LayoutEntity entity); - - public void removeRelationship(LayoutRelationship relationship); - - public void removeRelationships(List relationships); + public void applyLayout(); } Index: src/org/eclipse/zest/layouts/LayoutBendPoint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutBendPoint.java diff -N src/org/eclipse/zest/layouts/LayoutBendPoint.java --- src/org/eclipse/zest/layouts/LayoutBendPoint.java 12 Sep 2007 20:44:37 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,22 +0,0 @@ -/******************************************************************************* - * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -/** - * Specifies a single bend point in a graph relationship. - * @author Ian Bull - * @author Chris Bennett - */ -public interface LayoutBendPoint { - public double getX(); - public double getY(); - public boolean getIsControlPoint(); -} Index: src/org/eclipse/zest/layouts/LayoutItem.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutItem.java diff -N src/org/eclipse/zest/layouts/LayoutItem.java --- src/org/eclipse/zest/layouts/LayoutItem.java 12 Jan 2008 01:42:28 -0000 1.1 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -/** - * Super interface for both Layout Entities and Layout Relationships - * - * @author Ian Bull - * - */ -public interface LayoutItem { - - public void setGraphData(Object o); - public Object getGraphData(); - -} Index: src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java =================================================================== RCS file: src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java diff -N src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java --- src/org/eclipse/zest/layouts/InvalidLayoutConfiguration.java 12 Sep 2007 20:44:37 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -/** - * - * @author Ian Bull - * - */ -public class InvalidLayoutConfiguration extends Exception { - - static final long serialVersionUID = 0; - - -} Index: src/org/eclipse/zest/layouts/LayoutIterationEvent.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutIterationEvent.java diff -N src/org/eclipse/zest/layouts/LayoutIterationEvent.java --- src/org/eclipse/zest/layouts/LayoutIterationEvent.java 12 Sep 2007 20:44:37 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import java.util.List; - -/** - * When a layout completes an iteration, it throws this event - * to allow the application to update. For example, at the - * end of an iteration is can be assumed the layout has placed - * each entity into a new location. This event allows the application - * to update the GUI to represent the new locations - * - * @author Casey Best and Rob Lintern - */ -public class LayoutIterationEvent { - private List relationshipsToLayout, entitiesToLayout; - private int iterationCompleted; - - /** - * Return the relationships used in this layout. - */ - public List getRelationshipsToLayout() { - return relationshipsToLayout; - } - - /** - * Return the entities used in this layout. - */ - public List getEntitiesToLayout() { - return entitiesToLayout; - } - - /** - * Return the iteration of the layout algorithm that was - * just completed. - */ - public int getIterationCompleted() { - return iterationCompleted; - } -} Index: src/org/eclipse/zest/layouts/LayoutStyles.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutStyles.java diff -N src/org/eclipse/zest/layouts/LayoutStyles.java --- src/org/eclipse/zest/layouts/LayoutStyles.java 12 Sep 2007 20:44:38 -0000 1.5 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -/** - * @author Ian Bull - */ -public interface LayoutStyles { - - /** Default layout style constant. */ - public final static int NONE = 0x00; - - /** - * Layout constant indicating that the layout algorithm - * should NOT resize any of the nodes. - */ - public final static int NO_LAYOUT_NODE_RESIZING = 0x01; - - /** - * Some layouts may prefer to expand their bounds beyond those of the requested bounds. This - * flag asks the layout not to do so. - */ - public static final int ENFORCE_BOUNDS = 0X02; - -} Index: src/org/eclipse/zest/layouts/NestedLayoutEntity.java =================================================================== RCS file: src/org/eclipse/zest/layouts/NestedLayoutEntity.java diff -N src/org/eclipse/zest/layouts/NestedLayoutEntity.java --- src/org/eclipse/zest/layouts/NestedLayoutEntity.java 12 Sep 2007 20:44:37 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import java.util.List; - - -/** - * Extends LayoutEntity to provide methods for dealing with nested entities. - * - * @author Chris Callendar - */ -public interface NestedLayoutEntity extends LayoutEntity { - - /** Returns the parent entity. */ - NestedLayoutEntity getParent(); - - /** Returns the list of children. */ - List getChildren(); - - /** Returns true if this entity has children. */ - boolean hasChildren(); - -} Index: src/org/eclipse/zest/layouts/Stoppable.java =================================================================== RCS file: src/org/eclipse/zest/layouts/Stoppable.java diff -N src/org/eclipse/zest/layouts/Stoppable.java --- src/org/eclipse/zest/layouts/Stoppable.java 12 Sep 2007 20:44:37 -0000 1.5 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import org.eclipse.zest.layouts.progress.ProgressListener; - -/** - * @author Ian Bull - */ -public interface Stoppable { - - /** - * This ends the runnable - */ - public void stop(); - - public void addProgressListener(ProgressListener listener); - -} Index: src/org/eclipse/zest/layouts/LayoutGraph.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutGraph.java diff -N src/org/eclipse/zest/layouts/LayoutGraph.java --- src/org/eclipse/zest/layouts/LayoutGraph.java 12 Sep 2007 20:44:37 -0000 1.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import java.util.List; - -/** - * The LayoutGraph interface defines the methods used to add nodes and edges (relationships). - * @author Chris - */ -public interface LayoutGraph { - - /** - * Adds a node to this graph. - * @param node The new node. - * @return LayoutEntity The created node - */ - public void addEntity(LayoutEntity node); - - /** - * Adds the given relationship. - * @param relationship - */ - public void addRelationship(LayoutRelationship relationship); - - /** - * Returns a list of LayoutEntity objects that represent the objects added to this graph using addNode. - * @return List A List of LayoutEntity objects. - */ - public List getEntities(); - - /** - * Returns a list of LayoutRelationship objects that represent the objects added to this graph using addRelationship. - * @return List A List of LayoutRelationship objects. - */ - public List getRelationships(); - - /** - * Determines if the graph is bidirectional. - * @return boolean If the graph is bidirectional. - */ - public boolean isBidirectional(); - -} Index: src/org/eclipse/zest/layouts/LayoutEntity.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutEntity.java diff -N src/org/eclipse/zest/layouts/LayoutEntity.java --- src/org/eclipse/zest/layouts/LayoutEntity.java 12 Jan 2008 01:42:53 -0000 1.6 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -import org.eclipse.zest.layouts.constraints.LayoutConstraint; - -/** - * This represents a single entity, providing the layout algorithms with - * a common interface to run on. - * - * @author Casey Best - * @author Ian Bull - * @author Chris Bennett - */ -public interface LayoutEntity extends Comparable, LayoutItem { - - public final static String ATTR_PREFERRED_WIDTH = "tree-preferred-width"; - public final static String ATTR_PREFERRED_HEIGHT = "tree-preferred-height"; - - public void setLocationInLayout (double x, double y); - public void setSizeInLayout (double width, double height); - - public double getXInLayout(); - public double getYInLayout(); - public double getWidthInLayout(); - public double getHeightInLayout(); - - public Object getLayoutInformation(); - public void setLayoutInformation(Object internalEntity); - - /** - * Classes should update the specified layout constraint if recognized - * @return - */ - public void populateLayoutConstraint(LayoutConstraint constraint); -} Index: src/org/eclipse/zest/layouts/Filter.java =================================================================== RCS file: src/org/eclipse/zest/layouts/Filter.java diff -N src/org/eclipse/zest/layouts/Filter.java --- src/org/eclipse/zest/layouts/Filter.java 12 Jan 2008 01:43:30 -0000 1.4 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts; - -/** - * A filter is used to filter objects. Once implemented, interested - * parties can ask this filter whether or not a specific object - * is filtered. - * - * For example, in a visualization tool, only unfiltered objects should - * be displayed. Before displaying an object, the display can ask - * this filter if the object is filtered. - * - * @author Casey Best - */ -public interface Filter { - - /** - * Returns true if the object is filtered, or false if it's not filtered. - */ - public boolean isObjectFiltered (LayoutItem object); -} Index: src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java =================================================================== RCS file: src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java diff -N src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java --- src/org/eclipse/zest/layouts/dataStructures/InternalRelationship.java 12 Jan 2008 01:44:03 -0000 1.10 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,138 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.dataStructures; - -import java.util.LinkedList; -import java.util.List; - -import org.eclipse.zest.layouts.LayoutBendPoint; -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.constraints.BasicEdgeConstraints; -import org.eclipse.zest.layouts.constraints.LayoutConstraint; - -/** - * @author Ian Bull - */ -public class InternalRelationship implements LayoutRelationship{ - - private LayoutRelationship externalRelationship; - private InternalNode source; - private InternalNode destination; - private Object layoutInfo; - private List bendPoints = new LinkedList(); - BasicEdgeConstraints basicEdgeConstraints = new BasicEdgeConstraints(); - - public InternalRelationship( LayoutRelationship externalRelationship, InternalNode source, InternalNode destination) { - this.externalRelationship = externalRelationship; - this.externalRelationship.setLayoutInformation(this); - this.source = source; - this.destination = destination; - this.externalRelationship.populateLayoutConstraint(basicEdgeConstraints); - } - - public LayoutRelationship getLayoutRelationship() { - return externalRelationship; - } - - public InternalNode getSource() { - if ( this.source == null ) { - throw new RuntimeException("Source is null"); - } - return this.source; - } - - public InternalNode getDestination() { - if ( this.destination == null ) { - throw new RuntimeException("Dest is null"); - } - return this.destination; - } - - public double getWeight() { - return this.basicEdgeConstraints.weight; - } - - public boolean isBidirectional() { - return this.basicEdgeConstraints.isBiDirectional; - } - - /** - * Ensure this is called in order of source to target node position. - * @param x - * @param y - */ - public void addBendPoint(double x, double y) { - bendPoints.add(new BendPoint(x, y)); - } - - /** - * Ensure this is called in order of source to target node position. - * Specifies if the bendpoint is a curve control point - * @param x - * @param y - * @param isControlPoint - */ - public void addBendPoint(double x, double y, boolean isControlPoint) { - bendPoints.add(new BendPoint(x, y, isControlPoint)); - } - - public List getBendPoints() { - return this.bendPoints; - } - - public void clearBendPoints() { - // TODO Auto-generated method stub - - } - - - - public LayoutEntity getDestinationInLayout() { - // TODO Auto-generated method stub - return destination; - } - - - public Object getLayoutInformation() { - // TODO Auto-generated method stub - return layoutInfo; - } - - public LayoutEntity getSourceInLayout() { - // TODO Auto-generated method stub - return source; - } - - public void populateLayoutConstraint(LayoutConstraint constraint) { - // TODO Auto-generated method stub - - } - - public void setBendPoints(LayoutBendPoint[] bendPoints) { - // TODO Auto-generated method stub - - } - - public void setLayoutInformation(Object layoutInformation) { - this.layoutInfo = layoutInformation; - - } - - public Object getGraphData() { - return null; - } - - public void setGraphData(Object o) { - - } - -} Index: src/org/eclipse/zest/layouts/dataStructures/BendPoint.java =================================================================== RCS file: src/org/eclipse/zest/layouts/dataStructures/BendPoint.java diff -N src/org/eclipse/zest/layouts/dataStructures/BendPoint.java --- src/org/eclipse/zest/layouts/dataStructures/BendPoint.java 12 Sep 2007 20:44:37 -0000 1.5 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,54 +0,0 @@ -/******************************************************************************* - * Copyright 2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.dataStructures; - -import org.eclipse.zest.layouts.LayoutBendPoint; - -/** - * Implements a single bend point in a graph relationship. - * - * @author Ian Bull - * @author Chris Bennett - */ -public class BendPoint extends DisplayIndependentPoint implements LayoutBendPoint { - - private boolean isControlPoint = false; // is this a control point (for use in curves) - - public BendPoint(double x, double y) { - super(x, y); - } - - public BendPoint(double x, double y, boolean isControlPoint) { - this(x, y); - this.isControlPoint = isControlPoint; - } - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public void setX(double x) { - this.x = x; - } - - public void setY(double y) { - this.y = y; - } - - public boolean getIsControlPoint() { - return isControlPoint; - } - -} Index: src/org/eclipse/zest/layouts/dataStructures/InternalNode.java =================================================================== RCS file: src/org/eclipse/zest/layouts/dataStructures/InternalNode.java diff -N src/org/eclipse/zest/layouts/dataStructures/InternalNode.java --- src/org/eclipse/zest/layouts/dataStructures/InternalNode.java 12 Jan 2008 01:44:03 -0000 1.7 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,240 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.dataStructures; - -import java.util.HashMap; - -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.constraints.BasicEntityConstraint; -import org.eclipse.zest.layouts.constraints.LayoutConstraint; - -/** - * @author Ian Bull - */ -public class InternalNode implements Comparable, LayoutEntity { - - private LayoutEntity entity = null; - private HashMap attributeMap = new HashMap(); - BasicEntityConstraint basicEntityConstraint = new BasicEntityConstraint(); - - public InternalNode( LayoutEntity entity ) { - this.entity = entity; - this.entity.setLayoutInformation(this); - this.layoutWidth = entity.getWidthInLayout(); - this.layoutHeight = entity.getHeightInLayout(); - entity.populateLayoutConstraint(basicEntityConstraint); - } - - public LayoutEntity getLayoutEntity() { - return this.entity; - } - - public double getPreferredX() { - return basicEntityConstraint.preferredX; - - } - - public double getPreferredY() { - return basicEntityConstraint.preferredY; - } - - public boolean hasPreferredLocation() { - return basicEntityConstraint.hasPreferredLocation; - } - - double dx, dy; - public void setDx( double x ) { - this.dx = x; - } - public void setDy( double y ) { - this.dy = y; - } - public double getDx() { - return this.dx; - } - public double getDy() { - return this.dy; - } - - public double getCurrentX() { - return entity.getXInLayout(); - } - public double getCurrentY() { - return entity.getYInLayout(); - } - - public void setLocation( double x, double y ) { - entity.setLocationInLayout( x, y ); - } - public void setSize( double width, double height ) { - entity.setSizeInLayout( width, height ); - } - - double normalizedX = 0.0; - double normalizedY = 0.0; - double normalizedWidth = 0.0; - double normalizedHeight = 0.0; - - public void setInternalLocation( double x, double y ) { - //entity.setLocationInLayout(x,y); - - normalizedX = x; - normalizedY = y; - - } - - public DisplayIndependentPoint getInternalLocation() { - return new DisplayIndependentPoint(getInternalX(),getInternalY()); - } - - public void setInternalSize( double width, double height ) { - normalizedWidth = width; - normalizedHeight = height; - } - - public double getInternalX() { - //return entity.getXInLayout(); - return normalizedX; - } - public double getInternalY() { - //return entity.getYInLayout(); - return normalizedY; - } - public double getInternalWidth() { - return normalizedWidth; - } - public double getInternalHeight() { - return normalizedHeight; - } - - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public void setAttributeInLayout (Object attribute, Object value) { - attributeMap.put(attribute, value); - } - - /** - * An algorithm may require a place to store information. Use this structure for that purpose. - */ - public Object getAttributeInLayout (Object attribute) { - return attributeMap.get( attribute ); - } - - - //TODO: Fix all these preferred stuff!!!!! NOW! - - public boolean hasPreferredWidth () { - return false; - //return enity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH) != null; - } - - public double getPreferredWidth () { - return 0.0; -// if (hasPreferredWidth()) { -// return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_WIDTH)).doubleValue(); -// } else { -// return 10.0; -// } - } - - public boolean hasPreferredHeight () { - return false; - // return entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT) != null; - } - - public double getPreferredHeight () { - return 0.0; -// if (hasPreferredHeight()) { -// return ((Double)entity.getAttributeInLayout(LayoutEntity.ATTR_PREFERRED_HEIGHT)).doubleValue(); -// } else { -// return 10.0; -// } - } - - /* (non-Javadoc) - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - public int compareTo(Object arg0) { - return 0; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - public String toString() { - return (entity != null ? entity.toString() : ""); - } - - double layoutHeight; - double layoutWidth; - double layoutX; - double layoutY; - Object layoutInfo; - - public double getHeightInLayout() { - // TODO Auto-generated method stub - return layoutHeight; - } - - public Object getLayoutInformation() { - // TODO Auto-generated method stub - return this.layoutInfo; - } - - public double getWidthInLayout() { - // TODO Auto-generated method stub - return layoutWidth; - } - - public double getXInLayout() { - // TODO Auto-generated method stub - return layoutX; - } - - public double getYInLayout() { - // TODO Auto-generated method stub - return layoutY; - } - - public void populateLayoutConstraint(LayoutConstraint constraint) { - // TODO Auto-generated method stub - - } - - public void setLayoutInformation(Object internalEntity) { - this.layoutInfo = internalEntity; - - } - - public void setLocationInLayout(double x, double y) { - // TODO Auto-generated method stub - this.layoutX = x; - this.layoutY = y; - - } - - public void setSizeInLayout(double width, double height) { - this.layoutWidth = width; - this.layoutHeight = height; - } - - public Object getGraphData() { - return null; - } - - public void setGraphData(Object o) { - // TODO Auto-generated method stub - - } - -} Index: src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java diff -N src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java --- src/org/eclipse/zest/layouts/exampleUses/SimpleSwingExample.java 12 Sep 2007 20:44:38 -0000 1.15 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,702 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * The Chisel Group, University of Victoria - *******************************************************************************/ -package org.eclipse.zest.layouts.exampleUses; - -import java.awt.BasicStroke; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Polygon; -import java.awt.RenderingHints; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseMotionListener; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.GeneralPath; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Iterator; -import java.util.List; - -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JToggleButton; -import javax.swing.JToolBar; -import javax.swing.SwingUtilities; - -import org.eclipse.zest.layouts.InvalidLayoutConfiguration; -import org.eclipse.zest.layouts.LayoutAlgorithm; -import org.eclipse.zest.layouts.LayoutBendPoint; -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.LayoutStyles; -import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm; -import org.eclipse.zest.layouts.exampleStructures.SimpleNode; -import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; -import org.eclipse.zest.layouts.progress.ProgressEvent; -import org.eclipse.zest.layouts.progress.ProgressListener; - -/** - * @author Rob Lintern - * @author Chris Bennett - * - * A simple example of using layout algorithms with a Swing application. - */ -public class SimpleSwingExample { - private static final Color NODE_NORMAL_COLOR = new Color(225, 225, 255); - private static final Color NODE_SELECTED_COLOR = new Color(255, 125, 125); - //private static final Color NODE_ADJACENT_COLOR = new Color (255, 200, 125); - private static final Color BORDER_NORMAL_COLOR = new Color(0, 0, 0); - private static final Color BORDER_SELECTED_COLOR = new Color(255, 0, 0); - //private static final Color BORDER_ADJACENT_COLOR = new Color (255, 128, 0); - private static final Stroke BORDER_NORMAL_STROKE = new BasicStroke(1.0f); - private static final Stroke BORDER_SELECTED_STROKE = new BasicStroke(2.0f); - private static final Color RELATIONSHIP_NORMAL_COLOR = Color.BLUE; - //private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color (255, 200, 125); - - public static SpringLayoutAlgorithm SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE); - public static TreeLayoutAlgorithm TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE); - public static HorizontalTreeLayoutAlgorithm TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE); - public static RadialLayoutAlgorithm RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE); - public static GridLayoutAlgorithm GRID = new GridLayoutAlgorithm(LayoutStyles.NONE); - public static HorizontalLayoutAlgorithm HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE); - public static VerticalLayoutAlgorithm VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE); - - private List algorithms = new ArrayList(); - private List algorithmNames = new ArrayList(); - - private static final int INITIAL_PANEL_WIDTH = 700; - private static final int INITIAL_PANEL_HEIGHT = 500; - - private static final boolean RENDER_HIGH_QUALITY = true; - - private static final double INITIAL_NODE_WIDTH = 20; - private static final double INITIAL_NODE_HEIGHT = 20; - private static final int ARROW_HALF_WIDTH = 4; - private static final int ARROW_HALF_HEIGHT = 6; - private static final Shape ARROW_SHAPE = new Polygon(new int[] { -ARROW_HALF_HEIGHT, ARROW_HALF_HEIGHT, -ARROW_HALF_HEIGHT }, new int[] { -ARROW_HALF_WIDTH, 0, ARROW_HALF_WIDTH }, 3); - private static final Stroke ARROW_BORDER_STROKE = new BasicStroke(0.5f); - private static final Color ARROW_HEAD_FILL_COLOR = new Color(125, 255, 125); - private static final Color ARROW_HEAD_BORDER_COLOR = Color.BLACK; - - public static final String DEFAULT_NODE_SHAPE = "oval"; - - private long updateGUICount = 0; - - private JFrame mainFrame; - private JPanel mainPanel; - private List entities; - private List relationships; - private JToolBar toolBar; - private JLabel lblProgress; - private JToggleButton btnContinuous; - private JToggleButton btnAsynchronous; - private JButton btnStop; - - private LayoutAlgorithm currentLayoutAlgorithm; - protected String currentLayoutAlgorithmName; - protected SimpleNode selectedEntity; - protected Point mouseDownPoint; - protected Point selectedEntityPositionAtMouseDown; - private long idCount; - protected String currentNodeShape = DEFAULT_NODE_SHAPE; // e.g., oval, rectangle - - public SimpleSwingExample() { - - } - - protected void addAlgorithm(LayoutAlgorithm algorithm, String name, boolean animate) { - algorithms.add(algorithm); - algorithmNames.add(name); - } - - public void start() { - - mainFrame = new JFrame("Simple Swing Layout Example"); - toolBar = new JToolBar(); - mainFrame.getContentPane().setLayout(new BorderLayout()); - mainFrame.getContentPane().add(toolBar, BorderLayout.NORTH); - lblProgress = new JLabel("Progress: "); - mainFrame.getContentPane().add(lblProgress, BorderLayout.SOUTH); - - createMainPanel(); - mainFrame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - stop(); - mainFrame.dispose(); - - } - }); - - btnContinuous = new JToggleButton("continuous", false); - btnAsynchronous = new JToggleButton("asynchronous", false); - - toolBar.add(btnContinuous); - toolBar.add(btnAsynchronous); - - btnStop = new JButton("Stop"); - btnStop.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - stop(); - } - }); - toolBar.add(btnStop); - - JButton btnCreateGraph = new JButton("New graph"); - btnCreateGraph.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - stop(); - createGraph(true); - } - }); - toolBar.add(btnCreateGraph); - JButton btnCreateTree = new JButton("New tree"); - btnCreateTree.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - stop(); - createGraph(false); - } - }); - toolBar.add(btnCreateTree); - - createGraph(false); - - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - mainFrame.setLocation((int) (screenSize.getWidth() - INITIAL_PANEL_WIDTH) / 2, (int) (screenSize.getHeight() - INITIAL_PANEL_HEIGHT) / 2); - mainFrame.pack(); - mainFrame.setVisible(true); - mainFrame.repaint(); - - try { - SwingUtilities.invokeAndWait(new Runnable() { - - public void run() { - SPRING = new SpringLayoutAlgorithm(LayoutStyles.NONE); - TREE_VERT = new TreeLayoutAlgorithm(LayoutStyles.NONE); - TREE_HORIZ = new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE); - RADIAL = new RadialLayoutAlgorithm(LayoutStyles.NONE); - GRID = new GridLayoutAlgorithm(LayoutStyles.NONE); - HORIZ = new HorizontalLayoutAlgorithm(LayoutStyles.NONE); - VERT = new VerticalLayoutAlgorithm(LayoutStyles.NONE); - - SPRING.setIterations(1000); - // initialize layouts - TREE_VERT.setComparator(new Comparator() { - public int compare(Object o1, Object o2) { - if (o1 instanceof Comparable && o2 instanceof Comparable) { - return ((Comparable) o1).compareTo(o2); - } - return 0; - } - - }); - GRID.setRowPadding(20); - addAlgorithm(SPRING, "Spring", false); - addAlgorithm(TREE_VERT, "Tree-V", false); - addAlgorithm(TREE_HORIZ, "Tree-H", false); - addAlgorithm(RADIAL, "Radial", false); - addAlgorithm(GRID, "Grid", false); - addAlgorithm(HORIZ, "Horiz", false); - addAlgorithm(VERT, "Vert", false); - - for (int i = 0; i < algorithms.size(); i++) { - final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i); - final String algorithmName = (String) algorithmNames.get(i); - //final boolean algorithmAnimate = ((Boolean)algorithmAnimates.get(i)).booleanValue(); - JButton algorithmButton = new JButton(algorithmName); - algorithmButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - currentLayoutAlgorithm = algorithm; - currentLayoutAlgorithmName = algorithmName; - algorithm.setEntityAspectRatio((double) mainPanel.getWidth() / (double) mainPanel.getHeight()); - //animate = algorithmAnimate; - performLayout(); - } - }); - toolBar.add(algorithmButton); - } - } - }); - } catch (InterruptedException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } catch (InvocationTargetException e1) { - // TODO Auto-generated catch block - e1.printStackTrace(); - } - - } - - private void stop() { - if (currentLayoutAlgorithm != null && currentLayoutAlgorithm.isRunning()) { - currentLayoutAlgorithm.stop(); - } - } - - protected void performLayout() { - stop(); - final Cursor cursor = mainFrame.getCursor(); - updateGUICount = 0; - placeRandomly(); - final boolean continuous = btnContinuous.isSelected(); - final boolean asynchronous = btnAsynchronous.isSelected(); - ProgressListener progressListener = new ProgressListener() { - public void progressUpdated(final ProgressEvent e) { - //if (asynchronous) { - updateGUI(); - //} - lblProgress.setText("Progress: " + e.getStepsCompleted() + " of " + e.getTotalNumberOfSteps() + " completed ..."); - lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); - } - - public void progressStarted(ProgressEvent e) { - if (!asynchronous) { - mainFrame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - } - lblProgress.setText("Layout started ..."); - lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); - } - - public void progressEnded(ProgressEvent e) { - lblProgress.setText("Layout completed ..."); - lblProgress.paintImmediately(0, 0, lblProgress.getWidth(), lblProgress.getHeight()); - currentLayoutAlgorithm.removeProgressListener(this); - if (!asynchronous) { - mainFrame.setCursor(cursor); - } - } - }; - - currentLayoutAlgorithm.addProgressListener(progressListener); - - try { - final LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()]; - entities.toArray(layoutEntities); - final LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()]; - relationships.toArray(layoutRelationships); - SwingUtilities.invokeLater(new Runnable() { - public void run() { - try { - currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainPanel.getWidth(), mainPanel.getHeight(), asynchronous, continuous); - } catch (InvalidLayoutConfiguration e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - } - - }); - //if (!animate) { - updateGUI(); - //} - // reset - currentNodeShape = DEFAULT_NODE_SHAPE; - } catch (StackOverflowError e) { - e.printStackTrace(); - } finally { - } - } - - private void createMainPanel() { - - mainPanel = new MainPanel(); // see below for class definition - mainPanel.setPreferredSize(new Dimension(INITIAL_PANEL_WIDTH, INITIAL_PANEL_HEIGHT)); - mainPanel.setBackground(Color.WHITE); - mainPanel.setLayout(null); - mainFrame.getContentPane().add(new JScrollPane(mainPanel), BorderLayout.CENTER); - - mainPanel.addMouseListener(new MouseAdapter() { - public void mousePressed(MouseEvent e) { - selectedEntity = null; - for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { - SimpleNode entity = (SimpleNode) iter.next(); - double x = entity.getX(); - double y = entity.getY(); - double w = entity.getWidth(); - double h = entity.getHeight(); - Rectangle2D.Double rect = new Rectangle2D.Double(x, y, w, h); - if (rect.contains(e.getX(), e.getY())) { - selectedEntity = entity; - } - } - if (selectedEntity != null) { - mouseDownPoint = e.getPoint(); - selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY()); - } else { - mouseDownPoint = null; - selectedEntityPositionAtMouseDown = null; - } - updateGUI(); - } - - public void mouseReleased(MouseEvent e) { - selectedEntity = null; - mouseDownPoint = null; - selectedEntityPositionAtMouseDown = null; - updateGUI(); - } - }); - - mainPanel.addMouseMotionListener(new MouseMotionListener() { - public void mouseDragged(MouseEvent e) { - // if (selectedEntity != null) { - // //TODO: Add mouse moving - // //selectedEntity.setLocationInLayout(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy); - // updateGUI(); - // } - } - - public void mouseMoved(MouseEvent e) { - } - }); - } - - private void createGraph(boolean addNonTreeRels) { - entities = new ArrayList(); - relationships = new ArrayList(); - selectedEntity = null; - - createTreeGraph(2, 4, 2, 5, true, true, addNonTreeRels); - // createCustomGraph(); - - placeRandomly(); - mainPanel.repaint(); - } - - /** - * - * @param maxLevels Max number of levels wanted in tree - * @param maxChildren Max number of children for each node in the tree - * @param randomNumChildren Whether or not to pick random number of levels (from 1 to maxLevels) and - * random number of children (from 1 to maxChildren) - */ - private void createTreeGraph(int minChildren, int maxChildren, int minLevels, int maxLevels, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) { - LayoutEntity currentParent = createSimpleNode(getNextID()); - entities.add(currentParent); - createTreeGraphRecursive(currentParent, minChildren, maxChildren, minLevels, maxLevels, 1, randomNumChildren, randomLevels, addNonTreeRels); - } - - private void createTreeGraphRecursive(LayoutEntity currentParentNode, int minChildren, int maxChildren, int minLevel, int maxLevel, int level, boolean randomNumChildren, boolean randomLevels, boolean addNonTreeRels) { - if (level > maxLevel) { - return; - } - if (randomLevels) { - if (level > minLevel) { - double zeroToOne = Math.random(); - if (zeroToOne < 0.75) { - return; - } - } - } - int numChildren = randomNumChildren ? Math.max(minChildren, (int) (Math.random() * maxChildren + 1)) : maxChildren; - for (int i = 0; i < numChildren; i++) { - LayoutEntity newNode = createSimpleNode(getNextID()); - entities.add(newNode); - if (addNonTreeRels && entities.size() % 5 == 0) { - int index = (int) (Math.random() * entities.size()); - LayoutRelationship rel = new SimpleRelationship((LayoutEntity) entities.get(index), newNode, false); - relationships.add(rel); - } - LayoutRelationship rel = new SimpleRelationship(currentParentNode, newNode, false); - relationships.add(rel); - createTreeGraphRecursive(newNode, minChildren, maxChildren, minLevel, maxLevel, level + 1, randomNumChildren, randomLevels, addNonTreeRels); - } - } - - /** - * Call this from createGraph in place of createTreeGraph - * this for debugging and testing. - */ - /* private void createCustomGraph() { - LayoutEntity A = createSimpleNode("1"); - LayoutEntity B = createSimpleNode("10"); - LayoutEntity _1 = createSimpleNode("100"); - entities.add(A); - entities.add(B); - entities.add(_1); - relationships.add(new SimpleRelationship (A, B, false)); - relationships.add(new SimpleRelationship (A, _1, false)); - relationships.add(new SimpleRelationship (_1, A, false)); - } - */ - private String getNextID() { - String id = "" + idCount; - idCount++; - return id; - } - - /** Places nodes randomly on the screen **/ - private void placeRandomly() { - for (Iterator iter = entities.iterator(); iter.hasNext();) { - SimpleNode simpleNode = (SimpleNode) iter.next(); - double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH; - double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT; - simpleNode.setLocationInLayout(x, y); - simpleNode.setSizeInLayout(INITIAL_NODE_WIDTH, INITIAL_NODE_HEIGHT); - } - } - - /** - * Creates a SimpleNode - * @param name - * @return - */ - private SimpleNode createSimpleNode(String name) { - SimpleNode simpleNode = new SimpleNode(name); - return simpleNode; - } - - private void updateGUI() { - updateGUICount++; - if (updateGUICount > 0) { - mainPanel.paintImmediately(0, 0, mainPanel.getWidth(), mainPanel.getHeight()); - } - } - - private static Point2D.Double getEllipseIntersectionPoint(double theta, double ellipseWidth, double ellipseHeight) { - double nhalfw = ellipseWidth / 2.0; // half elllipse width - double nhalfh = ellipseHeight / 2.0; // half ellipse height - double tanTheta = Math.tan(theta); - - double a = nhalfw; - double b = nhalfh; - double x = (a * b) / Math.sqrt(Math.pow(b, 2) + Math.pow(a, 2) * Math.pow(tanTheta, 2)); - if ((theta > Math.PI / 2.0 && theta < 1.5 * Math.PI) || (theta < -Math.PI / 2.0 && theta > -1.5 * Math.PI)) { - x = -x; - } - double y = tanTheta * x; - Point2D.Double p = new Point2D.Double(x, y); - return p; - } - - public static void main(String[] args) { - (new SimpleSwingExample()).start(); - } - - /** - * A JPanel that provides entity and relationship rendering - * Instead of letting Swing paint all the JPanels for us, we will just do our own painting here - */ - private class MainPanel extends JPanel { - - private static final long serialVersionUID = 1; - - protected void paintChildren(Graphics g) { - if (g instanceof Graphics2D && RENDER_HIGH_QUALITY) { - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); - } - - // paint the nodes - for (Iterator iter = entities.iterator(); iter.hasNext();) { - paintEntity((SimpleNode) iter.next(), g); - } - - // paint the relationships - for (Iterator iter = relationships.iterator(); iter.hasNext();) { - paintRelationship((LayoutRelationship) iter.next(), g); - } - } - - private void paintEntity(SimpleNode entity, Graphics g) { - boolean isSelected = selectedEntity != null && selectedEntity.equals(entity); - g.setColor(isSelected ? NODE_SELECTED_COLOR : NODE_NORMAL_COLOR); - if (currentNodeShape.equals("rectangle")) { - g.fillRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); - } else { // default - g.fillOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); - } - g.setColor(isSelected ? BORDER_SELECTED_COLOR : BORDER_NORMAL_COLOR); - String name = entity.toString(); - Rectangle2D nameBounds = g.getFontMetrics().getStringBounds(name, g); - g.drawString(name, (int) (entity.getX() + entity.getWidth() / 2.0 - nameBounds.getWidth() / 2.0), (int) (entity.getY() + entity.getHeight() / 2.0 + nameBounds.getHeight() / 2.0));//- nameBounds.getHeight() - nameBounds.getY())); - if (g instanceof Graphics2D) { - ((Graphics2D) g).setStroke(isSelected ? BORDER_SELECTED_STROKE : BORDER_NORMAL_STROKE); - } - if (currentNodeShape.equals("rectangle")) { - g.drawRect((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); - } else { // default - g.drawOval((int) entity.getX(), (int) entity.getY(), (int) entity.getWidth(), (int) entity.getHeight()); - } - } - - private void paintRelationship(LayoutRelationship rel, Graphics g) { - - SimpleNode src = (SimpleNode) rel.getSourceInLayout(); - SimpleNode dest = (SimpleNode) rel.getDestinationInLayout(); - - // Add bend points if required - if (((SimpleRelationship) rel).getBendPoints() != null && ((SimpleRelationship) rel).getBendPoints().length > 0) { - drawBendPoints(rel, g); - } else { - double srcX = src.getX() + src.getWidth() / 2.0; - double srcY = src.getY() + src.getHeight() / 2.0; - double destX = dest.getX() + dest.getWidth() / 2.0; - double destY = dest.getY() + dest.getHeight() / 2.0; - double dx = getLength(srcX, destX); - double dy = getLength(srcY, destY); - double theta = Math.atan2(dy, dx); - drawRelationship(src, dest, theta, srcX, srcY, destX, destY, g); - - // draw an arrow in the middle of the line - drawArrow(theta, srcX, srcY, dx, dy, g); - } - } - - /** - * Draw a line from the edge of the src node to the edge of the destination node - */ - private void drawRelationship(SimpleNode src, SimpleNode dest, double theta, double srcX, double srcY, double destX, double destY, Graphics g) { - - double reverseTheta = theta > 0.0d ? theta - Math.PI : theta + Math.PI; - - Point2D.Double srcIntersectionP = getEllipseIntersectionPoint(theta, src.getWidth(), src.getHeight()); - - Point2D.Double destIntersectionP = getEllipseIntersectionPoint(reverseTheta, dest.getWidth(), dest.getHeight()); - - drawRelationship(srcX + srcIntersectionP.getX(), srcY + srcIntersectionP.getY(), destX + destIntersectionP.getX(), destY + destIntersectionP.getY(), g); - } - - /** - * Draw a line from specified source to specified destination - */ - private void drawRelationship(double srcX, double srcY, double destX, double destY, Graphics g) { - g.setColor(RELATIONSHIP_NORMAL_COLOR); - g.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY); - } - - private void drawArrow(double theta, double srcX, double srcY, double dx, double dy, Graphics g) { - AffineTransform tx = new AffineTransform(); - double arrX = srcX + (dx) / 2.0; - double arrY = srcY + (dy) / 2.0; - tx.translate(arrX, arrY); - tx.rotate(theta); - Shape arrowTx = tx.createTransformedShape(ARROW_SHAPE); - if (g instanceof Graphics2D) { - g.setColor(ARROW_HEAD_FILL_COLOR); - ((Graphics2D) g).fill(arrowTx); - ((Graphics2D) g).setStroke(ARROW_BORDER_STROKE); - g.setColor(ARROW_HEAD_BORDER_COLOR); - ((Graphics2D) g).draw(arrowTx); - } - } - - /** - * Get the length of a line ensuring it is not too small to render - * @param start - * @param end - * @return - */ - private double getLength(double start, double end) { - double length = end - start; - // make sure dx is not zero or too small - if (length < 0.01 && length > -0.01) { - if (length > 0) { - length = 0.01; - } else if (length < 0) { - length = -0.01; - } - } - return length; - } - - /** - * Draw a line from specified source to specified destination - */ - private void drawCurvedRelationship(double srcX, double srcY, double control1X, double control1Y, double control2X, double control2Y, double destX, double destY, Graphics g) { - GeneralPath shape = new GeneralPath(); - shape.moveTo((float) srcX, (float) srcY); - shape.curveTo((float) control1X, (float) control1Y, (float) control2X, (float) control2Y, (float) destX, (float) destY); - g.setColor(RELATIONSHIP_NORMAL_COLOR); - ((Graphics2D) g).draw(shape); - } - - /** - * Draws a set of lines between bendpoints, returning the last bendpoint - * drawn. Note that this assumes the first and last bendpoints are actually - * the source node and destination node centre points. - * @param relationship - * @param bendNodes - * @param bendEdges - * @return the last bendpoint entity or null if there are no bendpoints - */ - private void drawBendPoints(LayoutRelationship rel, Graphics g) { - final String DUMMY_TITLE = "dummy"; - LayoutBendPoint bp; - - SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout(); - SimpleNode destEntity = (SimpleNode) rel.getDestinationInLayout(); - double srcX = startEntity.getX(); - double srcY = startEntity.getY(); - - // Transform the bendpoints to this coordinate system - LayoutBendPoint[] bendPoints = ((SimpleRelationship) rel).getBendPoints(); - - srcX = bendPoints[1].getX(); - srcY = bendPoints[1].getY(); - int bpNum = 2; - while (bpNum < bendPoints.length - 1) { // ignore first and last bendpoints (src and dest) - int currentBpNum = bpNum; - bp = bendPoints[bpNum]; - if (bp.getIsControlPoint()) { - if (bendPoints[bpNum + 1].getIsControlPoint()) { - destEntity = new SimpleNode(DUMMY_TITLE, bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), 0.01, 0.01); - drawCurvedRelationship(srcX, srcY, bp.getX(), bp.getY(), bendPoints[bpNum + 1].getX(), bendPoints[bpNum + 1].getY(), bendPoints[bpNum + 2].getX(), bendPoints[bpNum + 2].getY(), g); - bpNum += 4; - } else { - destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); - } - } else { - drawRelationship(srcX, srcY, bp.getX(), bp.getY(), g); - bpNum++; - destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); - } - startEntity = destEntity; - if (currentBpNum == bendPoints.length - 2) { // last point - // draw an arrow in the middle of the line - double dx = getLength(srcX, destEntity.getX()); - double dy = getLength(srcY, destEntity.getY()); - double theta = Math.atan2(dy, dx); - drawArrow(theta, srcX, srcY, dx, dy, g); - } else { - srcX = startEntity.getX(); - srcY = startEntity.getY(); - } - } - - } - } -} Index: src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java =================================================================== RCS file: src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java diff -N src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java --- src/org/eclipse/zest/layouts/exampleUses/SimpleSWTExample.java 12 Sep 2007 20:44:38 -0000 1.14 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,785 +0,0 @@ -/******************************************************************************* - * Copyright 2005, CHISEL Group, University of Victoria, Victoria, BC, Canada. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of the Eclipse Public License v1.0 which - * accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: The Chisel Group, University of Victoria - ******************************************************************************/ -package org.eclipse.zest.layouts.exampleUses; - -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jface.dialogs.ProgressMonitorDialog; -import org.eclipse.zest.layouts.InvalidLayoutConfiguration; -import org.eclipse.zest.layouts.LayoutAlgorithm; -import org.eclipse.zest.layouts.LayoutBendPoint; -import org.eclipse.zest.layouts.LayoutEntity; -import org.eclipse.zest.layouts.LayoutRelationship; -import org.eclipse.zest.layouts.LayoutStyles; -import org.eclipse.zest.layouts.algorithms.GridLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.HorizontalLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.HorizontalTreeLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.RadialLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.SpringLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.TreeLayoutAlgorithm; -import org.eclipse.zest.layouts.algorithms.VerticalLayoutAlgorithm; -import org.eclipse.zest.layouts.exampleStructures.SimpleNode; -import org.eclipse.zest.layouts.exampleStructures.SimpleRelationship; -import org.eclipse.zest.layouts.progress.ProgressEvent; -import org.eclipse.zest.layouts.progress.ProgressListener; -import org.eclipse.swt.SWT; -import org.eclipse.swt.SWTError; -import org.eclipse.swt.events.ControlEvent; -import org.eclipse.swt.events.ControlListener; -import org.eclipse.swt.events.DisposeEvent; -import org.eclipse.swt.events.DisposeListener; -import org.eclipse.swt.events.MouseEvent; -import org.eclipse.swt.events.MouseListener; -import org.eclipse.swt.events.MouseMoveListener; -import org.eclipse.swt.events.PaintEvent; -import org.eclipse.swt.events.PaintListener; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.events.SelectionListener; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.graphics.GC; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.graphics.Point; -import org.eclipse.swt.graphics.Rectangle; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Canvas; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Display; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.ToolBar; -import org.eclipse.swt.widgets.ToolItem; - -/** - * @author Rob Lintern - * @author Ian Bull - * A simple example of using layout algorithms with a SWT application. - */ -public class SimpleSWTExample { - - private static final Color BLACK = new Color(Display.getDefault(), 0, 0, 0); - private static final Color NODE_NORMAL_COLOR = new Color(Display.getDefault(), 225, 225, 255); - private static final Color NODE_SELECTED_COLOR = new Color(Display.getDefault(), 255, 125, 125); - private static final Color NODE_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 200, 125); - private static final Color BORDER_NORMAL_COLOR = new Color(Display.getDefault(), 0, 0, 0); - private static final Color BORDER_SELECTED_COLOR = new Color(Display.getDefault(), 255, 0, 0); - private static final Color BORDER_ADJACENT_COLOR = new Color(Display.getDefault(), 255, 128, 0); - private static final Color RELATIONSHIP_COLOR = new Color(Display.getDefault(), 192, 192, 225); - private static final Color RELATIONSHIP_HIGHLIGHT_COLOR = new Color(Display.getDefault(), 255, 200, 125); - - private static final String[] NAMES = new String[] { "Peggy", "Rob", "Ian", "Chris", "Simon", "Wendy", "Steven", "Kim", "Neil", "Dave", "John", "Suzanne", "Jody", "Casey", "Bjorn", "Peter", "Erin", "Lisa", "Jennie", "Liz", "Bert", "Ryan", "Nick", "Amy", "Lee", "Me", "You", "Max", "NCI", "OWL", - "Ed", "Jamie", "Protege", "Matt", "Bryan", "Pete", "Sam", "Bob", "Katie", "Bill", "Josh", "Davor", "Ken", "Jacob", "Norm", "Jim", "Maya", "Jill", "Kit", "Jo", "Joe", "Andrew", "Charles", "Pat", "Patrick", "Jeremy", "Mike", "Michael", "Patricia", "Marg", "Terry", "Emily", "Ben", "Holly", - "Joanna", "Joanne", "Evan", "Tom", "Dan", "Eric", "Corey", "Meghan", "Kevin", "Nina", "Ron", "Daniel", "David", "Jeff", "Nathan", "Amanda", "Phil", "Tricia", "Steph", "Stewart", "Stuart", "Bull", "Lintern", "Callendar", "Thompson", "Rigby", "Adam", "Judith", "Cynthia", "Sarah", "Sara", - "Roger", "Andy", "Kris", "Mark", "Shane", "Spence", "Ivy", "Ivanna", "Julie", "Justin", "Emile", "Toby", "Robin", "Rich", "Kathy", "Cathy", "Nicky", "Ricky", "Danny", "Anne", "Ann", "Jen", "Robert", "Calvin", "Alvin", "Scott", "Kumar" }; - - //private static final boolean RENDER_HIGH_QUALITY = true; - - private static final int INITIAL_PANEL_WIDTH = 800; - private static final int INITIAL_PANEL_HEIGHT = 600; - private static final double INITIAL_NODE_WIDTH = 20; - private static final double INITIAL_NODE_HEIGHT = 15; - - protected static ArrayList algorithms = new ArrayList(); - { - algorithms.add(new SpringLayoutAlgorithm(LayoutStyles.NO_LAYOUT_NODE_RESIZING)); - algorithms.add(new TreeLayoutAlgorithm(LayoutStyles.NONE)); - algorithms.add(new HorizontalTreeLayoutAlgorithm(LayoutStyles.NONE)); - algorithms.add(new RadialLayoutAlgorithm(LayoutStyles.NONE)); - algorithms.add(new GridLayoutAlgorithm(LayoutStyles.NONE)); - algorithms.add(new HorizontalLayoutAlgorithm(LayoutStyles.NONE)); - algorithms.add(new VerticalLayoutAlgorithm(LayoutStyles.NONE)); - } - - protected static ArrayList algorithmNames = new ArrayList(); - { - algorithmNames.add("Spring"); - algorithmNames.add("Fade"); - algorithmNames.add("Tree - V"); - algorithmNames.add("Tree - H"); - algorithmNames.add("Radial"); - algorithmNames.add("Grid"); - algorithmNames.add("Horizontal"); - algorithmNames.add("Vertical"); - } - - protected static ArrayList algorithmAnimates = new ArrayList(); - { - algorithmAnimates.add(Boolean.TRUE); - algorithmAnimates.add(Boolean.TRUE); - algorithmAnimates.add(Boolean.FALSE); - algorithmAnimates.add(Boolean.FALSE); - algorithmAnimates.add(Boolean.FALSE); - algorithmAnimates.add(Boolean.FALSE); - algorithmAnimates.add(Boolean.FALSE); - algorithmAnimates.add(Boolean.FALSE); - } - - //private long updateGUICount = 0; - private boolean animate = true; - private static boolean continuous = false; - private static boolean asynchronously = false; - - private Shell mainShell; - private Composite mainComposite; - private List entities; - private List relationships; - - private ToolBar toolBar; - private Label lblProgress; - - private LayoutAlgorithm currentLayoutAlgorithm; - protected SimpleNode selectedEntity; - protected SimpleNode hoverEntity; - - protected Point mouseDownPoint; - protected Point selectedEntityPositionAtMouseDown; - private long idCount = 0; - - public SimpleSWTExample(Display display) { - mainShell = new Shell(display); - mainShell.addControlListener(new ControlListener() { - public void controlMoved(ControlEvent e) { - } - - public void controlResized(ControlEvent e) { - mainShell.layout(true); - } - }); - GridLayout gridLayout = new GridLayout(1, true); - mainShell.setLayout(gridLayout); - GridData toolbarGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_BEGINNING, true, true); - toolBar = new ToolBar(mainShell, SWT.HORIZONTAL); - toolBar.setLayoutData(toolbarGridData); - toolBar.setLayout(new FillLayout(SWT.HORIZONTAL)); - - GridData progressGridData = new GridData(GridData.HORIZONTAL_ALIGN_CENTER, GridData.VERTICAL_ALIGN_END, true, false); - progressGridData.widthHint = 300; - lblProgress = new Label(mainShell, SWT.NONE); - lblProgress.setLayoutData(progressGridData); - lblProgress.setText("Progress: "); - - for (int i = 0; i < algorithms.size(); i++) { - final LayoutAlgorithm algorithm = (LayoutAlgorithm) algorithms.get(i); - String algorithmName = (String) algorithmNames.get(i); - final boolean algorithmAnimate = ((Boolean) algorithmAnimates.get(i)).booleanValue(); - ToolItem algorithmButton = new ToolItem(toolBar, SWT.PUSH); - algorithmButton.setText(algorithmName); - - new ToolItem(toolBar, SWT.SEPARATOR); - - algorithmButton.addSelectionListener(new SelectionListener() { - public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) { - currentLayoutAlgorithm = algorithm; - algorithm.setEntityAspectRatio((double) mainComposite.getClientArea().width / (double) mainComposite.getClientArea().height); - animate = algorithmAnimate; - performLayout(false); - } - - public void widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent e) { - } - }); - } - - ToolItem redrawButton = new ToolItem(toolBar, SWT.PUSH); - redrawButton.setText("Redraw"); - redrawButton.addSelectionListener(new SelectionListener() { - public void widgetSelected(SelectionEvent e) { - mainComposite.redraw(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - - ToolItem stopButton = new ToolItem(toolBar, SWT.PUSH); - stopButton.setText("Stop"); - stopButton.addSelectionListener(new SelectionListener() { - public void widgetSelected(SelectionEvent e) { - currentLayoutAlgorithm.stop(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - - ToolItem continuousButton = new ToolItem(toolBar, SWT.CHECK); - continuousButton.setText("Continuous"); - continuousButton.addSelectionListener(new SelectionListener() { - public void widgetSelected(SelectionEvent e) { - setContinuous(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - - ToolItem asynchronousButton = new ToolItem(toolBar, SWT.CHECK); - asynchronousButton.setText("Asynchronous"); - asynchronousButton.addSelectionListener(new SelectionListener() { - public void widgetSelected(SelectionEvent e) { - setAsynchronously(); - } - - public void widgetDefaultSelected(SelectionEvent e) { - } - }); - - createMainPanel(); - SimpleNode.setNodeColors(NODE_NORMAL_COLOR, BORDER_NORMAL_COLOR, NODE_SELECTED_COLOR, NODE_ADJACENT_COLOR, BORDER_SELECTED_COLOR, BORDER_ADJACENT_COLOR); - SimpleRelationship.setDefaultColor(RELATIONSHIP_COLOR); - SimpleRelationship.setDefaultHighlightColor(RELATIONSHIP_HIGHLIGHT_COLOR); - createTreeGraph(4, 3, false); - mainShell.pack(); - //mainShell.setSize(INITIAL_PANEL_WIDTH + 100, INITIAL_PANEL_HEIGHT + 200); - } - - public void setAsynchronously() { - if (asynchronously) { - asynchronously = false; - } else { - asynchronously = true; - } - - } - - public void setContinuous() { - if (continuous) { - continuous = false; - } else { - continuous = true; - } - } - - IProgressMonitor progressMonitor = null; - ProgressMonitorDialog pmd = null; - boolean GUI_UPDATING = false; - - private void performLayout(boolean placeRandomly) { - - if (!continuous) { - } - - if (currentLayoutAlgorithm.isRunning()) { - throw new RuntimeException("Layout is already running"); - } - if (placeRandomly) { - placeRandomly(); - } - ProgressListener progressListener = new ProgressListener() { - - int lastStep = 0; - - class progressSync implements Runnable { - - public static final int PROGRESS_UPDATED = 1; - public static final int PROGRESS_STARTED = 2; - public static final int PROGRESS_ENDED = 3; - public static final int UPDATE_GUI = 4; - - private int progressState = -1; - private ProgressEvent e; - - public progressSync(int progressState, final ProgressEvent e) { - this.progressState = progressState; - this.e = e; - - } - - public void run() { - - switch (progressState) { - case PROGRESS_STARTED: - if (!continuous) { - pmd = new ProgressMonitorDialog(getShell()); - progressMonitor = pmd.getProgressMonitor(); - pmd.open(); - progressMonitor.beginTask("Layout Running...", e.getTotalNumberOfSteps()); - } - break; - - case PROGRESS_UPDATED: - if (!continuous) { - progressMonitor.worked(e.getStepsCompleted() - lastStep); - lastStep = e.getStepsCompleted(); - } - break; - - case PROGRESS_ENDED: - if (!continuous) { - progressMonitor.done(); - pmd.close(); - } - updateGUI(); - mainShell.redraw(); - break; - case UPDATE_GUI: - updateGUI(); - GUI_UPDATING = false; - break; - } - mainComposite.redraw(); - - } - - } - - public void progressUpdated(final ProgressEvent e) { - if (asynchronously) { - if (!mainComposite.isDisposed()) { - Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e)); - if (!GUI_UPDATING) { - GUI_UPDATING = true; - Display.getDefault().asyncExec(new progressSync(progressSync.UPDATE_GUI, e)); - } - } - } else { - if (!mainComposite.isDisposed()) { - new progressSync(progressSync.PROGRESS_UPDATED, e).run(); - } - } - - } - - public void progressStarted(ProgressEvent e) { - if (asynchronously) { - if (!mainComposite.isDisposed()) { - Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_STARTED, e)); - } - } else { - if (!mainComposite.isDisposed()) { - new progressSync(progressSync.PROGRESS_STARTED, e).run(); - } - } - - } - - public void progressEnded(ProgressEvent e) { - if (asynchronously) { - if (!mainComposite.isDisposed()) { - Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_ENDED, e)); - } - } else { - if (!mainComposite.isDisposed()) { - new progressSync(progressSync.PROGRESS_ENDED, e).run(); - } - } - currentLayoutAlgorithm.removeProgressListener(this); - Display.getDefault().asyncExec(new progressSync(progressSync.PROGRESS_UPDATED, e)); - } - }; - currentLayoutAlgorithm.addProgressListener(progressListener); - - try { - LayoutEntity[] layoutEntities = new LayoutEntity[entities.size()]; - entities.toArray(layoutEntities); - LayoutRelationship[] layoutRelationships = new LayoutRelationship[relationships.size()]; - relationships.toArray(layoutRelationships); - currentLayoutAlgorithm.applyLayout(layoutEntities, layoutRelationships, 0, 0, mainComposite.getClientArea().width - 30, mainComposite.getClientArea().height - 17, asynchronously, continuous); - if (!animate) { - updateGUI(); - } - } catch (InvalidLayoutConfiguration e) { - e.printStackTrace(); - } - } - - private Shell getShell() { - return mainShell; - } - - private void createMainPanel() { - mainComposite = new Canvas(mainShell, SWT.NO_BACKGROUND); - GridData mainGridData = new GridData(GridData.HORIZONTAL_ALIGN_FILL, GridData.VERTICAL_ALIGN_FILL, true, true); - mainGridData.widthHint = INITIAL_PANEL_WIDTH; - mainGridData.heightHint = INITIAL_PANEL_HEIGHT; - mainComposite.setLayoutData(mainGridData); - mainComposite.addPaintListener(new GraphPaintListener()); - - mainComposite.setBackground(new Color(Display.getCurrent(), 255, 255, 255)); - mainComposite.setLayout(null); - - mainComposite.addMouseMoveListener(new MouseMoveListener() { - - public void mouseMove(MouseEvent e) { - - if (selectedEntity == null) { - // Nothing selected, lets use a mouse hover - SimpleNode oldEntity = hoverEntity; - hoverEntity = null; - - for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { - SimpleNode entity = (SimpleNode) iter.next(); - double x = entity.getX(); - double y = entity.getY(); - double w = entity.getWidth(); - double h = entity.getHeight(); - Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); - if (rect.contains(e.x, e.y)) { - hoverEntity = entity; - hoverEntity.ignoreInLayout(true); - hoverEntity.setSelected(); - break; - } - } - if (oldEntity != null && oldEntity != hoverEntity) { - oldEntity.ignoreInLayout(false); - oldEntity.setUnSelected(); - } - } - - } - - }); - mainComposite.addMouseListener(new MouseListener() { - - public void mouseDoubleClick(MouseEvent e) { - } - - public void mouseDown(MouseEvent e) { - selectedEntity = null; - hoverEntity = null; - for (Iterator iter = entities.iterator(); iter.hasNext() && selectedEntity == null;) { - SimpleNode entity = (SimpleNode) iter.next(); - double x = entity.getX(); - double y = entity.getY(); - double w = entity.getWidth(); - double h = entity.getHeight(); - Rectangle rect = new Rectangle((int) x, (int) y, (int) w, (int) h); - if (rect.contains(e.x, e.y)) { - selectedEntity = entity; - } - } - if (selectedEntity != null) { - mouseDownPoint = new Point(e.x, e.y); - selectedEntityPositionAtMouseDown = new Point((int) selectedEntity.getX(), (int) selectedEntity.getY()); - selectedEntity.ignoreInLayout(true); - selectedEntity.setSelected(); - } else { - mouseDownPoint = null; - selectedEntityPositionAtMouseDown = null; - } - } - - public void mouseUp(MouseEvent e) { - if (selectedEntity != null) { - selectedEntity.ignoreInLayout(false); - selectedEntity.setUnSelected(); - List relatedNodes = selectedEntity.getRelatedEntities(); - for (Iterator iter = relatedNodes.iterator(); iter.hasNext();) { - SimpleNode element = (SimpleNode) iter.next(); - element.setUnSelected(); - } - SimpleRelationship[] rels = selectedEntity.getRelationships(); - for (int i = 0; i < rels.length; i++) { - rels[i].resetLineWidth(); - } - } - selectedEntity = null; - mouseDownPoint = null; - selectedEntityPositionAtMouseDown = null; - } - }); - - // stops the algorithm when the window is closed - mainComposite.addDisposeListener(new DisposeListener() { - public void widgetDisposed(DisposeEvent e) { - if (currentLayoutAlgorithm != null) { - currentLayoutAlgorithm.stop(); - } - } - }); - - mainComposite.addMouseMoveListener(new MouseMoveListener() { - - public void mouseMove(MouseEvent e) { - if (selectedEntity != null && mouseDownPoint != null) { - double dx = e.x - mouseDownPoint.x; - double dy = e.y - mouseDownPoint.y; - - selectedEntity.setLocation(selectedEntityPositionAtMouseDown.x + dx, selectedEntityPositionAtMouseDown.y + dy); - mainComposite.redraw(); - } - } - }); - } - - static int lastUpdateCall = 0; - - /** - * - * @param maxLevels Max number of levels wanted in tree - * @param maxChildren Max number of children for each node in the tree - * @param random Whether or not to pick random number of levels (from 1 to maxLevels) and - * random number of children (from 1 to maxChildren) - */ - private void createTreeGraph(int maxLevels, int maxChildren, boolean random) { - entities = new ArrayList(); - relationships = new ArrayList(); - - // ccallendar - testing out having 2 roots - SimpleNode root = createSimpleNode(getNextID()); - entities.add(root); - SimpleNode root2 = createSimpleNode(getNextID()); - entities.add(root2); - // end - - SimpleNode currentParent = createSimpleNode(getNextID()); - entities.add(currentParent); - - // ccallendar - adding relationships from the parent to the 2 roots - SimpleRelationship rel = new SimpleRelationship(root, currentParent, false); - root.addRelationship(rel); - currentParent.addRelationship(rel); - relationships.add(rel); - rel = new SimpleRelationship(root2, currentParent, false); - root2.addRelationship(rel); - currentParent.addRelationship(rel); - relationships.add(rel); - // end - - int levels = random ? (int) (Math.random() * maxLevels + 1) : maxLevels; - createTreeGraphRecursive(currentParent, maxChildren, levels, 1, random); - } - - private void createTreeGraphRecursive(SimpleNode currentParentNode, int maxChildren, int maxLevel, int level, boolean random) { - if (level > maxLevel) { - return; - } - - int numChildren = random ? (int) (Math.random() * maxChildren + 1) : maxChildren; - for (int child = 0; child < numChildren; child++) { - SimpleNode childNode = createSimpleNode(getNextID()); - entities.add(childNode); - SimpleRelationship rel = new SimpleRelationship(currentParentNode, childNode, false); - childNode.addRelationship(rel); - currentParentNode.addRelationship(rel); - relationships.add(rel); - SimpleRelationship.setDefaultSize(2); - createTreeGraphRecursive(childNode, maxChildren, maxLevel, level + 1, random); - } - } - - private int repeats = 0; - - /** - * Gets the next name from the names list. - * Once all the names have been used up the names are - * repeated with a '1' after the name. - * @return String name - */ - private String getNextID() { - if (idCount >= NAMES.length) { - idCount = 0; - repeats++; - } - String id = NAMES[(int) idCount]; - if (repeats > 0) { - id += "_" + repeats; - } - idCount++; - return id; - } - - /** Places nodes randomly on the screen **/ - private void placeRandomly() { - for (Iterator iter = entities.iterator(); iter.hasNext();) { - SimpleNode simpleNode = (SimpleNode) iter.next(); - double x = Math.random() * INITIAL_PANEL_WIDTH - INITIAL_NODE_WIDTH; - double y = Math.random() * INITIAL_PANEL_HEIGHT - INITIAL_NODE_HEIGHT; - simpleNode.setLocationInLayout(x, y); - } - } - - /** - * Creates a SimpleNode - * @param name - * @return SimpleNode - */ - private SimpleNode createSimpleNode(String name) { - SimpleNode simpleNode = new SimpleNode(name); - int w = name.length() * 8; // an initial approximation of the width - simpleNode.setSizeInLayout(Math.max(w, INITIAL_NODE_WIDTH), INITIAL_NODE_HEIGHT); - return simpleNode; - } - - private void updateGUI() { - if (!mainComposite.isDisposed()) { - mainComposite.redraw(); - //mainComposite.update(); - } - } - - static Display display = null; - - public static void main(String[] args) { - display = Display.getDefault(); - SimpleSWTExample simpleSWTExample = new SimpleSWTExample(display); - Shell shell = simpleSWTExample.getShell(); - //shell.pack(); - shell.open(); - while (!shell.isDisposed()) { - if (!display.readAndDispatch()) { - display.sleep(); - } - } - display.dispose(); - } - - /** - * Implements a paint listener to display nodes and edges - */ - private class GraphPaintListener implements PaintListener { - - long lastPaint; - - public void paintControl(PaintEvent e) { - Date date = new Date(); - long currentTime = date.getTime(); - if (currentTime - lastPaint < 40) { - return; - } else { - lastPaint = currentTime; - } - if (Display.getDefault() == null || e.width == 0 || e.height == 0) { - return; - } - long startTime = date.getTime(); - - // do a bit of our own double-buffering to stop flickering - Image imageBuffer; - - try { - imageBuffer = new Image(Display.getDefault(), e.width, e.height); - } catch (SWTError noMoreHandles) { - imageBuffer = null; - noMoreHandles.printStackTrace(); - return; - } catch (IllegalArgumentException tooBig) { - imageBuffer = null; - tooBig.printStackTrace(); - return; - } - - GC gcBuffer = new GC(imageBuffer); - - // paint the relationships - for (Iterator iter = relationships.iterator(); iter.hasNext();) { - SimpleRelationship rel = (SimpleRelationship) iter.next(); - SimpleNode src = (SimpleNode) rel.getSourceInLayout(); - SimpleNode dest = (SimpleNode) rel.getDestinationInLayout(); - - // highlight the adjacent nodes if one of the nodes is selected - if (src.equals(selectedEntity)) { - dest.setAdjacent(); - rel.setSelected(); - } else if (dest.equals(selectedEntity)) { - src.setAdjacent(); - rel.setSelected(); - } else { - rel.setUnSelected(); - } - - // Add bend points if required - if ((rel).getBendPoints() != null && (rel).getBendPoints().length > 0) { - src = drawBendPoints(rel, gcBuffer); // change source to last bendpoint - } - - double srcX = src.getX() + src.getWidth() / 2.0; - double srcY = src.getY() + src.getHeight() / 2.0; - double destX = dest.getX() + dest.getWidth() / 2.0; - double destY = dest.getY() + dest.getHeight() / 2.0; - drawEdge(srcX, srcY, destX, destY, rel, gcBuffer); - - } - - // paint the nodes - for (Iterator iter = entities.iterator(); iter.hasNext();) { - SimpleNode entity = (SimpleNode) iter.next(); - - String name = entity.toString(); - Point textSize = gcBuffer.stringExtent(name); - int entityX = (int) entity.getX(); - int entityY = (int) entity.getY(); - //TODO: What about resize from the layout algorithm - int entityWidth = Math.max((int) entity.getWidth(), textSize.x + 8); - int entityHeight = Math.max((int) entity.getHeight(), textSize.y + 2); - - gcBuffer.setBackground((Color) entity.getColor()); - gcBuffer.fillRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8); - - // position the text in the middle of the node - int x = (int) (entityX + (entityWidth / 2.0)) - (textSize.x / 2); - gcBuffer.setForeground(BLACK); - gcBuffer.drawString(name, x, entityY); - gcBuffer.setForeground((Color) entity.getBorderColor()); - gcBuffer.setLineWidth(entity.getBorderWidth()); - gcBuffer.drawRoundRectangle(entityX, entityY, entityWidth, entityHeight, 8, 8); - } - - e.gc.drawImage(imageBuffer, 0, 0); - imageBuffer.dispose(); - gcBuffer.dispose(); - - long time = date.getTime() - startTime; - if (time > 200) { - } - } - - /** - * Draw an edge - * @param gcBuffer - * @param srcX - * @param srcY - * @param destX - * @param destY - * @param rel - */ - private void drawEdge(double srcX, double srcY, double destX, double destY, SimpleRelationship rel, GC gcBuffer) { - gcBuffer.setForeground((Color) rel.getColor()); - gcBuffer.setLineWidth(rel.getLineWidth()); - gcBuffer.drawLine((int) srcX, (int) srcY, (int) destX, (int) destY); - } - - /** - * Draws a set of lines between bendpoints - * TODO - This does not always draw outside the node. - * @param relationship - * @param bendNodes - * @param bendEdges - * @return the last bendpoint entity or null if there are no bendpoints - */ - private SimpleNode drawBendPoints(SimpleRelationship rel, GC gcBuffer) { - final String DUMMY_TITLE = "dummy"; - LayoutBendPoint[] bendPoints = (rel).getBendPoints(); - LayoutBendPoint bp; - SimpleNode startEntity = (SimpleNode) rel.getSourceInLayout(); - SimpleNode destEntity = null; - - double srcX = startEntity.getX() + startEntity.getWidth() / 2.0; - double srcY = startEntity.getY() + startEntity.getHeight() / 2.0; - for (int i = 1; i < bendPoints.length - 1; i++) { - bp = bendPoints[i]; - destEntity = new SimpleNode(DUMMY_TITLE, bp.getX(), bp.getY(), 0.01, 0.01); - drawEdge(srcX, srcY, bp.getX(), bp.getY(), rel, gcBuffer); - startEntity = destEntity; - srcX = startEntity.getX(); - srcY = startEntity.getY(); - } - return destEntity; - } - - } - -} Index: src/org/eclipse/zest/layouts/ExpandCollapseManager.java =================================================================== RCS file: src/org/eclipse/zest/layouts/ExpandCollapseManager.java diff -N src/org/eclipse/zest/layouts/ExpandCollapseManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/ExpandCollapseManager.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - Initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Layout algorithms that want to handle expanding and collapsing of nodes + * in trees (or graphs) should pass an implementation of this interface + * to {@link LayoutContext#setExpandCollapseManager(ExpandCollapseManager)}. + */ +public interface ExpandCollapseManager { + + /** + * A request to change expansion state of a given node. The receiver + * should prune or unprune appropriate nodes in the layout. + * + * @param node the node that should be expanded or collapsed + * @param expanded true - node should be expanded; false - node should be collapsed + */ + public void setExpanded(NodeLayout node, boolean expanded); + +} \ No newline at end of file Index: src/org/eclipse/zest/layouts/LayoutContext.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutContext.java diff -N src/org/eclipse/zest/layouts/LayoutContext.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/LayoutContext.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; + +/** + * Objects implementing LayoutContext interface are used for exchanging of + * information between layout algorithms and graphical objects displaying + * graphs. + */ +public interface LayoutContext { + + /** + * Returns all the nodes that should be laid out. Replacing elements in the + * returned array does not affect this context. + * + * @return array of nodes to lay out + */ + public NodeLayout[] getNodes(); + + /** + * Returns all the connections between nodes that should be laid out. + * Replacing elements in the returned array does not affect this context. + * + * @return array of connections between nodes + */ + public ConnectionLayout[] getConnections(); + + /** + * Returns all entities that are currently placed on the graph, that is + * subgraphs and unpruned nodes. Replacing elements in the returned array + * does not affect this context. + * + * @return array of entities to layout + */ + public EntityLayout[] getEntities(); + + /** + * Returns all the connections between given source and target entities. If + * given entity is a subgraph, connections adjacent to each of its nodes + * will be included in the result. All the undirected nodes connecting the + * two nodes will be also included in the result. Replacing elements in the + * returned array does not affect this context. + * + * @param layoutEntity1 + * @param layoutEntity2 + * @return + */ + public ConnectionLayout[] getConnections(EntityLayout layoutEntity1, EntityLayout layoutEntity2); + + /** + * + * @return bounds in which the graph elements can be placed + */ + public DisplayIndependentRectangle getBounds(); + + /** + * + * @return true if a layout algorithm is allowed to place graph elements + * outside of suggested bounds + */ + public boolean isBoundsExpandable(); + + /** + * Returns all the subgraphs this context's nodes were pruned to. Replacing + * elements in the returned array does not affect this context. + * + * @return array of subgraphs (may be empty) + */ + public SubgraphLayout[] getSubgraphs(); + + /** + * Creates a subgraph containing given nodes and adds it to this context. If + * given nodes already belong to another subgraphs, they are removed from + * them prior to adding to the new subgraph. + * + * @param nodes + * nodes to add to the new subgraph + */ + public SubgraphLayout addSubgraph(NodeLayout[] nodes); + + /** + * + * @return true if this layout context allows pruning nodes into subgraphs + */ + public boolean isPruningEnabled(); + + /** + * + * @return true if this layout context allows layout algorithm to work + * continuously in background and change the layout with time or in + * reaction to some events. + */ + public boolean isContinuousLayoutEnabled(); + + /** + * Sets the main layout algorithm for this context. Main algorithm will be + * used to relayout graph items using {@link LayoutAlgorithm#applyLayout()} + * after every event that is not intercepted by any listener. + * + * @param algorithm + */ + public void setMainLayoutAlgorithm(LayoutAlgorithm algorithm); + + /** + * + * @return the main algorithm of this context (see + * {@link #setMainLayoutAlgorithm(LayoutAlgorithm)} for details) + */ + public LayoutAlgorithm getMainLayoutAlgorithm(); + + /** + * Sets the expand/collapse manager for this context. The manger will be + * used to handle expansion related methods called on the owner of this + * context. + * + * @param pruningManager + */ + public void setExpandCollapseManager(ExpandCollapseManager pruningManager); + + /** + * + * @return current expand/collapse manager (can be null, which means that + * pruning is not enabled). + */ + public ExpandCollapseManager getExpandCollapseManager(); + + /** + * Adds a listener to the context that will be notified about changes in + * this context's layout, that is movement and resizing of nodes / + * subgraphs. The notifications will not include changes made with API + * included in layout related interfaces, so that layout algorithms won't be + * notified about changes they invoke. Only internal changes of the system + * will fire events. + * + * @param listener + * listener to add + */ + public void addLayoutListener(LayoutListener listener); + + /** + * Removes a layout listener from this context. + * + * @param listener + * listener to remove + */ + public void removeLayoutListener(LayoutListener listener); + + /** + * Adds a listener to the context that will be notified about changes in + * graph structure, that is addition and removal of nodes and connections. + * The notifications will not include changes made with API included in + * layout related interfaces, so that layout algorithms won't be notified + * about changes they invoke. Only internal changes of the system will fire + * events. + * + * @param listener + * listener to add + */ + public void addGraphStructureListener(GraphStructureListener listener); + + /** + * Removes a graph structure listener from this context. + * + * @param listener + * listener to remove + */ + public void removeGraphStructureListener(GraphStructureListener listener); + + /** + * Adds a listener to the context that will be notified about changes + * related to its configuration. + * + * @param listener + * listener to add + */ + public void addContextListener(ContextListener listener); + + /** + * Removes a context listener from this context. + * + * @param listener + * listener to remove + */ + public void removeContextListener(ContextListener listener); + + /** + * Causes all the changes made to elements in this context to affect the + * display. + * + * @param animationHint + * a hint for display mechanism indicating whether changes are + * major and should be animated (if true) or not. + */ + public void flushChanges(boolean animationHint); +} Index: src/org/eclipse/zest/layouts/NodeLayout.java =================================================================== RCS file: src/org/eclipse/zest/layouts/NodeLayout.java diff -N src/org/eclipse/zest/layouts/NodeLayout.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/NodeLayout.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Interface used by layout algorithms to retrieve and change information + * about nodes. + * + * There should exist exactly one object implementing this interface for + * each node that should be considered by a layout algorithm (reference + * comparisons may be used for identification of NodeLayouts). + */ +public interface NodeLayout extends EntityLayout { + + /** + * + * @return true if this node can be pruned + */ + public boolean isPrunable(); + + /** + * + * @return true if this node is currently pruned + */ + public boolean isPruned(); + + /** + * + * @return a subgraph this node belongs to or null if this node is not + * pruned + */ + public SubgraphLayout getSubgraph(); + + /** + * + * @param subgraph + * a subgraph this node should belong to or null if this node + * should not be pruned + */ + public void prune(SubgraphLayout subgraph); + + /** + * Returns all nodes that are direct successors of this node. Nodes + * connected with this node by a bidirectional connection are considered + * both successors and predecessors. Any subsequent changes to the returned + * array do not affect this node. + * + * @return array of successors of this node + */ + public NodeLayout[] getSuccessingNodes(); + + /** + * Returns all nodes that are direct predecessors of this node. Nodes + * connected with this node by a bidirectional connection are considered + * both successors and predecessors. Any subsequent changes to the returned + * array do not affect this node. + * + * @return array of predecessors of this node + */ + public NodeLayout[] getPredecessingNodes(); + + /** + * Returns all connections that have this node as a target. All connections + * that are bidirectional and are adjacent to this node will be also + * included in the result. Any subsequent changes to the returned array do + * not affect this node. + * + * @return array of connections entering this node + */ + public ConnectionLayout[] getIncomingConnections(); + + /** + * Returns all connections that have this node as a source. All connections + * that are bidirectional and are adjacent to this node will be also + * included in the result. Any subsequent changes to the returned array do + * not affect this node. + * + * @return array of connections leaving this node + */ + public ConnectionLayout[] getOutgoingConnections(); + +} Index: src/org/eclipse/zest/layouts/ConnectionLayout.java =================================================================== RCS file: src/org/eclipse/zest/layouts/ConnectionLayout.java diff -N src/org/eclipse/zest/layouts/ConnectionLayout.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/ConnectionLayout.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: Mateusz Matela - Initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Interface used by layout algorithms to retrieve and change information + * about connections. + * + * There should exist exactly one object implementing this interface for + * each connection that should be considered by a layout algorithm (reference + * comparisons may be used for identification of ConnectionLayouts). + */ +public interface ConnectionLayout { + + /** + * + * @return object containing information about source node of this connection + */ + public NodeLayout getSource(); + + /** + * + * @return object containing information about target node of this connection + */ + public NodeLayout getTarget(); + + /** + * + * @return weight assigned to this connection + */ + public double getWeight(); + + /** + * Checks if this connection is directed. For undirected connections, source + * and target nodes should be considered just adjacent nodes without + * dividing to source/target. + * + * @return true if this connection is directed + */ + public boolean isDirected(); + + /** + * Changes the visibility state of this connection. + * + * @param visible + * true if this connection should be visible, false otherwise + */ + public void setVisible(boolean visible); + + /** + * Checks the visibility state of this connection. + * + * @return true if this connection is visible, false otherwise + */ + public boolean isVisible(); +} Index: src/org/eclipse/zest/layouts/SubgraphLayout.java =================================================================== RCS file: src/org/eclipse/zest/layouts/SubgraphLayout.java diff -N src/org/eclipse/zest/layouts/SubgraphLayout.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/SubgraphLayout.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * An interface for subgraphs in layout. A subgraph is a set of pruned nodes + * that will be displayed as one element (or not displayed at all if the subgraph + * doesn't support it). A subgraph must contain at least one node (empty + * subgraphs will be removed from its context). Every node can belong to at + * most one subgraph. + */ +public interface SubgraphLayout extends EntityLayout { + + /** + * Returns all the nodes belonging to this subgraph. Replacing elements in + * the returned array does not affect this subgraph. + * + * @return array of nodes + */ + public NodeLayout[] getNodes(); + + /** + * Adds nodes to this subgraph. If given nodes already belong to another + * subgraph, they are first removed from them. + * + * @param nodes + * array of nodes to add + */ + public void addNodes(NodeLayout[] nodes); + + /** + * Removes nodes from this subgraph. + * + * @param nodes + * array of nodes to remove + */ + public void removeNodes(NodeLayout[] nodes); + + /** + * Returns true if this subgraph is visualized as a particular object on the + * graph. If this method returns false, it means that this subgraph will not + * be visible so all methods related to location and size should be ignored. + * + * @return whether or not this subgraph is a graph entity that should be + * laid out. + */ + public boolean isGraphEntity(); + +} Index: src/org/eclipse/zest/layouts/ContextListener.java =================================================================== RCS file: src/org/eclipse/zest/layouts/ContextListener.java diff -N src/org/eclipse/zest/layouts/ContextListener.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/ContextListener.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + *******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Interface for listeners that are notified by LayoutContext about important + * changes affecting the layout process. + */ +public interface ContextListener { + public class Stub implements ContextListener { + + public boolean boundsChanged(LayoutContext context) { + return false; + } + + public void continuousLayoutChanged(LayoutContext context) { + // do nothing + } + + public boolean pruningEnablementChanged(LayoutContext context) { + return false; + } + + } + + /** + * This method is called whenever the bounds available in a layout context + * change. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the layout context that fired the event + * @return true if no further operations after this event are required + */ + public boolean boundsChanged(LayoutContext context); + + /** + * This method is called whenever graph pruning is enabled or disabled in a + * layout context. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the layout context that fired the event + * @return true if no further operations after this event are required + */ + public boolean pruningEnablementChanged(LayoutContext context); + + /** + * This method is called whenever continuous layout is enabled or disabled + * in a layout context. If the receiving listener is related to a layout + * algorithm that supports continuous layout, it should react accordingly by + * starting or stopping its thread. + * + * @param context + * the layout context that fired the event + */ + public void continuousLayoutChanged(LayoutContext context); +} Index: src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java diff -N src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/algorithms/AlgorithmHelper.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 + *******************************************************************************/ + +package org.eclipse.zest.layouts.algorithms; + +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; +import org.eclipse.zest.layouts.EntityLayout; + +/** + * A class containing methods that can be used by layout algorithm for common + * tasks. + */ +class AlgorithmHelper { + + public static int MIN_NODE_SIZE = 8; + + public static double PADDING_PERCENT = 0.8; + + /** + * Fits given entities within given bounds, preserving their relative + * locations. + * + * @param entities + * @param destinationBounds + * @param resize + */ + public static void fitWithinBounds(EntityLayout[] entities, DisplayIndependentRectangle destinationBounds, boolean resize) { + DisplayIndependentRectangle startingBounds = getLayoutBounds(entities, false); + double sizeScale = Math.min(destinationBounds.width / startingBounds.width, destinationBounds.height / startingBounds.height); + if (entities.length == 1) { + fitSingleEntity(entities[0], destinationBounds, resize); + return; + } + for (int i = 0; i < entities.length; i++) { + EntityLayout entity = entities[i]; + DisplayIndependentDimension size = entity.getSize(); + if (entity.isMovable()) { + DisplayIndependentPoint location = entity.getLocation(); + double percentX = (location.x - startingBounds.x) / (startingBounds.width); + double percentY = (location.y - startingBounds.y) / (startingBounds.height); + + if (resize && entity.isResizable()) { + size.width *= sizeScale; + size.height *= sizeScale; + entity.setSize(size.width, size.height); + } + + location.x = destinationBounds.x + size.width / 2 + percentX * (destinationBounds.width - size.width); + location.y = destinationBounds.y + size.height / 2 + percentY * (destinationBounds.height - size.height); + entity.setLocation(location.x, location.y); + } else if (resize && entity.isResizable()) { + entity.setSize(size.width * sizeScale, size.height * sizeScale); + } + } + } + + private static void fitSingleEntity(EntityLayout entity, DisplayIndependentRectangle destinationBounds, boolean resize) { + if (entity.isMovable()) { + entity.setLocation(destinationBounds.x + destinationBounds.width / 2, destinationBounds.y + destinationBounds.height / 2); + } + if (resize && entity.isResizable()) { + double width = destinationBounds.width; + double height = destinationBounds.height; + double preferredAspectRatio = entity.getPreferredAspectRatio(); + if (preferredAspectRatio > 0) { + DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredAspectRatio); + entity.setSize(fixedSize.width, fixedSize.height); + } else { + entity.setSize(width, height); + } + } + } + + /** + * Resizes the entities so that they have a maximal area without overlapping + * each other, with additional empty space of 20% of node's width (or + * height, if bigger). It does nothing if there's less than two entities. + * + * @param entities + */ + public static void maximizeSizes(EntityLayout[] entities) { + if (entities.length > 1) { + DisplayIndependentDimension minDistance = getMinimumDistance(entities); + double nodeSize = Math.max(minDistance.width, minDistance.height) * PADDING_PERCENT; + double width = nodeSize; + double height = nodeSize; + for (int i = 0; i < entities.length; i++) { + EntityLayout entity = entities[i]; + if (entity.isResizable()) { + double preferredRatio = entity.getPreferredAspectRatio(); + if (preferredRatio > 0) { + DisplayIndependentDimension fixedSize = fixAspectRatio(width, height, preferredRatio); + entity.setSize(fixedSize.width, fixedSize.height); + } else { + entity.setSize(width, height); + } + } + } + } + } + + private static DisplayIndependentDimension fixAspectRatio(double width, double height, double preferredRatio) { + double actualRatio = width / height; + if (actualRatio > preferredRatio) { + width = height * preferredRatio; + if (width < MIN_NODE_SIZE) { + width = MIN_NODE_SIZE; + height = width / preferredRatio; + } + } + if (actualRatio < preferredRatio) { + height = width / preferredRatio; + if (height < MIN_NODE_SIZE) { + height = MIN_NODE_SIZE; + width = height * preferredRatio; + } + } + return new DisplayIndependentDimension(width, height); + } + + /** + * Find the bounds in which the entities are located. Using the bounds against + * the real bounds of the screen, the entities can proportionally be placed + * within the real bounds. The bounds can be determined either including the + * size of the entities or not. If the size is not included, the bounds will + * only be guaranteed to include the center of each entity. + */ + public static DisplayIndependentRectangle getLayoutBounds(EntityLayout[] entities, boolean includeNodeSize) { + double rightSide = Double.NEGATIVE_INFINITY; + double bottomSide = Double.NEGATIVE_INFINITY; + double leftSide = Double.POSITIVE_INFINITY; + double topSide = Double.POSITIVE_INFINITY; + for (int i = 0; i < entities.length; i++) { + EntityLayout entity = entities[i]; + DisplayIndependentPoint location = entity.getLocation(); + DisplayIndependentDimension size = entity.getSize(); + if (includeNodeSize) { + leftSide = Math.min(location.x - size.width / 2, leftSide); + topSide = Math.min(location.y - size.height / 2, topSide); + rightSide = Math.max(location.x + size.width / 2, rightSide); + bottomSide = Math.max(location.y + size.height / 2, bottomSide); + } else { + leftSide = Math.min(location.x, leftSide); + topSide = Math.min(location.y, topSide); + rightSide = Math.max(location.x, rightSide); + bottomSide = Math.max(location.y, bottomSide); + } + } + return new DisplayIndependentRectangle(leftSide, topSide, rightSide - leftSide, bottomSide - topSide); + } + + /** + * minDistance is the closest that any two points are together. These two + * points become the center points for the two closest nodes, which we wish + * to make them as big as possible without overlapping. This will be the + * maximum of minDistanceX and minDistanceY minus a bit, lets say 20% + * + * We make the recommended node size a square for convenience. + * + *
+	 *    _______
+	 *   |       | 
+	 *   |       |
+	 *   |   +   |
+	 *   |   |\  |
+	 *   |___|_\_|_____
+	 *       | | \     |
+	 *       | |  \    |
+	 *       +-|---+   |
+	 *         |       |
+	 *         |_______|
+	 * 
+ * + * + */ + public static DisplayIndependentDimension getMinimumDistance(EntityLayout[] entities) { + DisplayIndependentDimension horAndVertdistance = new DisplayIndependentDimension(Double.MAX_VALUE, Double.MAX_VALUE); + double minDistance = Double.MAX_VALUE; + + // TODO: Very Slow! + for (int i = 0; i < entities.length; i++) { + DisplayIndependentPoint location1 = entities[i].getLocation(); + for (int j = i + 1; j < entities.length; j++) { + DisplayIndependentPoint location2 = entities[j].getLocation(); + double distanceX = location1.x - location2.x; + double distanceY = location1.y - location2.y; + double distance = distanceX * distanceX + distanceY * distanceY; + + if (distance < minDistance) { + minDistance = distance; + horAndVertdistance.width = Math.abs(distanceX); + horAndVertdistance.height = Math.abs(distanceY); + } + } + } + return horAndVertdistance; + } +} Index: src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/algorithms/BoxLayoutAlgorithm.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts.algorithms; + +/** + * Places all entities of the graph in one row or one column (depending on + * orientation). + */ +public class BoxLayoutAlgorithm extends GridLayoutAlgorithm { + + /** + * Constant for horizontal orientation of box algorithm + */ + public static final int HORIZONTAL = 1; + + /** + * Constant for vertical orientation of box algorithm + */ + public static final int VERTICAL = 2; + + private int orientation = HORIZONTAL; + + public BoxLayoutAlgorithm() { + } + + /** + * Constructs layout algorithm and sets its orientation + * + * @param orientation + */ + public BoxLayoutAlgorithm(int orientation) { + setOrientation(orientation); + } + + /** + * + * @return current orientation of this algorithm (default value is HORIZONTAL) + */ + public int getOrientation() { + return orientation; + } + + /** + * Changes orientation of this algorithm. + * @param orientation valid values are {@link BoxLayoutAlgorithm#HORIZONTAL HORIZONTAL} and + * {@link BoxLayoutAlgorithm#VERTICAL VERTICAL} + */ + public void setOrientation(int orientation) { + if (orientation == HORIZONTAL || orientation == VERTICAL) + this.orientation = orientation; + else + throw new RuntimeException("Invalid orientation: " + orientation); + } + + protected int[] calculateNumberOfRowsAndCols(int numChildren, double boundX, double boundY, double boundWidth, double boundHeight) { + if (orientation == HORIZONTAL) + return new int[] { numChildren, 1 }; + else + return new int[] { 1, numChildren }; + } +} Index: src/org/eclipse/zest/layouts/EntityLayout.java =================================================================== RCS file: src/org/eclipse/zest/layouts/EntityLayout.java diff -N src/org/eclipse/zest/layouts/EntityLayout.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/EntityLayout.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - Initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentPoint; + +/** + * A common interface for entities that are displayed on a graph, that is + * {@link NodeLayout nodes} and {@link SubgraphLayout subgraphs}. + */ +public interface EntityLayout { + + /** + * Returns a point laying in the center of this entity. Any subsequent + * changes to the returned point won't affect this entity. + * + * @return position of the center of this entity + */ + public DisplayIndependentPoint getLocation(); + + /** + * Sets the position of this entity. The entity will be moved so that it's + * center is located in the given point. + * + * @param x + * the x-position + * @param y + * the y-position + */ + public void setLocation(double x, double y); + + /** + * Returns the size of this entity this entity. Any subsequent + * changes to the returned point won't affect this entity. + * + * @return size of this entity + */ + public DisplayIndependentDimension getSize(); + + /** + * Changes the size of this entity (if it is resizable). + * + * @param width new width + * @param height new height + */ + public void setSize(double width, double height); + + /** + * Returns aspect ratio that is preferred for this entity. Can be 0 if this + * entity can't be resized anyway or it doesn't care about about its ratio. + * + * @return aspect ratio (width / height) + */ + public double getPreferredAspectRatio(); + + /** + * If an entity is not resizable, calls to {@link #setSize(double, double)} + * will have no effect. + * + * @return true if this entity can be resized + */ + public boolean isResizable(); + + /** + * If an entity is not movable, calls to {@link #setLocation(double, double)} + * will have no effect. + * + * @return true if this entity can be moved + */ + public boolean isMovable(); + + /** + * Returns all entities that are direct successors of this entity. Successor + * entities of an unpruned node N are: + *
    + *
  • all unpruned successor nodes of node N
  • + *
  • all subgraphs containing at least one successor node of node N
  • + *
+ * Successor entities of a subgraph S are: + *
    + *
  • all unpruned nodes that are successor of at least one node from + * subgraph S
  • + *
  • all subgraphs containing at least one node that is a successor of at + * least one node from subgraph S
  • + *
+ * Entities connected with this node by a bidirectional connection are + * considered both successors and predecessors. Any subsequent changes to + * the returned array do not affect this node. + * + * @return array of successors of this node + */ + public EntityLayout[] getSuccessingEntities(); + + /** + * Returns all entities that are direct predecessors of this entity. + * Predecessor entities of an unpruned node A are: + *
    + *
  • all unpruned predecessor nodes of node N
  • + *
  • all subgraphs containing at least one predecessor node of node N
  • + *
+ * Successor entities of a subgraph S are: + *
    + *
  • all unpruned nodes that are predecessor of at least one node from + * subgraph S
  • + *
  • all subgraphs containing at least one node that is a predecessor of + * at least one node from subgraph S
  • + *
+ * Entities connected with this node by a bidirectional connection are + * considered both successors and predecessors. Any subsequent changes to + * the returned array do not affect this node. + * + * @return array of predecessors of this node + */ + public EntityLayout[] getPredecessingEntities(); + + /** + * Sets the minimized state of this entity. Entity that is minimized resizes + * its figure to (0, 0). When it is unminimized, it resizes back to previous + * dimension. The layout's size property is not affected by minimized state, + * so an entity can be minimized even if it's not resizable. + * + * @param minimized + * new minimized state + */ + public void setMinimized(boolean minimized); + + /** + * @see #setMinimized(boolean) + * + * @return true if this entity is minimized + */ + public boolean isMinimized(); +} Index: src/org/eclipse/zest/layouts/LayoutListener.java =================================================================== RCS file: src/org/eclipse/zest/layouts/LayoutListener.java diff -N src/org/eclipse/zest/layouts/LayoutListener.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/LayoutListener.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This + * program and the accompanying materials are made available under the terms of + * the Eclipse Public License v1.0 which accompanies this distribution, and is + * available at http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Mateusz Matela - initial API and implementation + ******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Interface for listeners that are notified by LayoutContext about changes + * in layout caused by external actors (for example a user) and not by + * layout algorithm itself. + */ +public interface LayoutListener { + + /** + * This method is called whenever location of a particular node is changed + * within observed context. If true is returned, it means that the receiving + * listener has intercepted this event. Intercepted events will not be + * passed to the rest of the listeners. If the event is not intercepted by + * any listener, {@link LayoutAlgorithm#applyLayout() applyLayout()} will be + * called on the context's main algorithm. + * + * @param context + * the layout context that fired the event + * @param node + * the node that has moved + * @return true if no further operations after this event are required + */ + public boolean nodeMoved(LayoutContext context, NodeLayout node); + + /** + * This method is called whenever size of a particular node is changed + * within observed context. If true is returned, it means that the receiving + * listener has intercepted this event. Intercepted events will not be + * passed to the rest of the listeners. If the event is not intercepted by + * any listener, {@link LayoutAlgorithm#applyLayout() applyLayout()} will be + * called on the context's main algorithm. + * + * @param context + * the layout context that fired the event + * @param node + * the node that was resized + * @return true if no further operations after this event are required + */ + public boolean nodeResized(LayoutContext context, NodeLayout node); + + /** + * This method is called whenever location of a particular subgraph is + * changed within observed context. If true is returned, it means that the + * receiving listener has intercepted this event. Intercepted events will + * not be passed to the rest of the listeners. If the event is not + * intercepted by any listener, {@link LayoutAlgorithm#applyLayout() + * applyLayout()} will be called on the context's main algorithm. + * + * @param context + * the layout context that fired the event + * @param node + * the node that has moved + * @return true if no further operations after this event are required + */ + public boolean subgraphMoved(LayoutContext context, SubgraphLayout node); + + /** + * This method is called whenever size of a particular subgraph is changed + * within observed context. If true is returned, it means that the receiving + * listener has intercepted this event. Intercepted events will not be + * passed to the rest of the listeners. If the event is not intercepted by + * any listener, {@link LayoutAlgorithm#applyLayout() applyLayout()} will be + * called on the context's main algorithm. + * + * @param context + * the layout context that fired the event + * @param node + * the node that was resized + * @return true if no further operations after this event are required + */ + public boolean subgraphResized(LayoutContext context, SubgraphLayout node); +} Index: src/org/eclipse/zest/layouts/GraphStructureListener.java =================================================================== RCS file: src/org/eclipse/zest/layouts/GraphStructureListener.java diff -N src/org/eclipse/zest/layouts/GraphStructureListener.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/GraphStructureListener.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,122 @@ +/******************************************************************************* +* Copyright (c) 2009 Mateusz Matela and others. All rights reserved. This +* program and the accompanying materials are made available under the terms of +* the Eclipse Public License v1.0 which accompanies this distribution, and is +* available at http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Mateusz Matela - initial API and implementation +******************************************************************************/ + +package org.eclipse.zest.layouts; + +/** + * Interface for listeners that are notified by LayoutContext about changes in + * graph structure (that is adding or removing nodes and connections). + */ +public interface GraphStructureListener { + + public class Stub implements GraphStructureListener { + + public boolean nodeAdded(LayoutContext context, NodeLayout node) { + return false; + } + + public boolean nodeRemoved(LayoutContext context, NodeLayout node) { + return false; + } + + public boolean connectionAdded(LayoutContext context, ConnectionLayout connection) { + return false; + } + + public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection) { + return false; + } + } + + /** + * This method is called whenever a node is added to a context. No separate + * events will be fired for eventual connections adjacent to the added node. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the layout context that fired the event + * @param node + * the added node + * @return true if no further operations after this event are required + */ + public boolean nodeAdded(LayoutContext context, NodeLayout node); + + /** + * This method is called whenever a node is removed from a context. No + * separate events will be fired for eventual connections adjacent to the + * removed node. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the context that fired the event + * @param node + * the removed node + * @return true if no further operations after this event are required + */ + public boolean nodeRemoved(LayoutContext context, NodeLayout node); + + /** + * This method is called whenever a connection is added to a context. It can + * be assumed that both source and target nodes of the added connection + * already exist in the context. + * + * This method will be called only if both nodes connected by added + * connection lay directly in the node container owned by the notifying + * layout context. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the context that fired the event + * @param connection + * the added connection + * @return true if no further operations after this event are required + */ + public boolean connectionAdded(LayoutContext context, ConnectionLayout connection); + + /** + * This method is called whenever a connection is removed from a context. It + * can be assumed that both source and target nodes of the removed + * connection still exist in the context and will not be removed along with + * it. + * + * This method will be called only if both nodes connected by removed + * connection lay directly in the node container owned by the notifying + * layout context. + * + * If true is returned, it means that the receiving listener has intercepted + * this event. Intercepted events will not be passed to the rest of the + * listeners. If the event is not intercepted by any listener, + * {@link LayoutAlgorithm#applyLayout() applyLayout()} will be called on the + * context's main algorithm. + * + * @param context + * the context that fired the event + * @param connection + * the added connection + * @return true if no further operations after this event are required + */ + public boolean connectionRemoved(LayoutContext context, ConnectionLayout connection); + +} Index: src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java =================================================================== RCS file: src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java diff -N src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/zest/layouts/algorithms/HorizontalShiftAlgorithm.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright 2005, 2009 CHISEL Group, University of Victoria, Victoria, BC, Canada. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * The Chisel Group, University of Victoria + * Mateusz Matela - Refactor Zest layouts - https://bugs.eclipse.org/bugs/show_bug.cgi?id=283083 + *******************************************************************************/ + +package org.eclipse.zest.layouts.algorithms; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.zest.layouts.EntityLayout; +import org.eclipse.zest.layouts.LayoutAlgorithm; +import org.eclipse.zest.layouts.LayoutContext; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentDimension; +import org.eclipse.zest.layouts.dataStructures.DisplayIndependentRectangle; + +/** + * This layout shifts overlapping nodes to the right. + * + * @author Ian Bull + */ +public class HorizontalShiftAlgorithm implements LayoutAlgorithm { + + private static final double DELTA = 10; + + private static final double VSPACING = 16; + + private LayoutContext context; + + public void applyLayout() { + ArrayList rowsList = new ArrayList(); + EntityLayout[] entities = context.getEntities(); + + for (int i = 0; i < entities.length; i++) { + addToRowList(entities[i], rowsList); + } + + Collections.sort(rowsList, new Comparator() { + public int compare(Object o1, Object o2) { + List a0 = (List) o1; + List a1 = (List) o2; + EntityLayout entity0 = (EntityLayout) a0.get(0); + EntityLayout entity1 = (EntityLayout) a1.get(0); + return (int) (entity0.getLocation().y - entity1.getLocation().y); + } + }); + + Comparator entityComparator = new Comparator() { + public int compare(Object o1, Object o2) { + return (int) (((EntityLayout) o1).getLocation().y - ((EntityLayout) o2).getLocation().y); + } + }; + DisplayIndependentRectangle bounds = context.getBounds(); + int heightSoFar = 0; + + for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) { + List currentRow = (List) iterator.next(); + Collections.sort(currentRow, entityComparator); + + int i = 0; + int width = (int) (bounds.width / 2 - currentRow.size() * 75); + + heightSoFar += ((EntityLayout) currentRow.get(0)).getSize().height + VSPACING; + for (Iterator iterator2 = currentRow.iterator(); iterator2.hasNext();) { + EntityLayout entity = (EntityLayout) iterator2.next(); + DisplayIndependentDimension size = entity.getSize(); + entity.setLocation(width + 10 * ++i + size.width / 2, heightSoFar + size.height / 2); + width += size.width; + } + } + } + + public void setLayoutContext(LayoutContext context) { + this.context = context; + } + + private void addToRowList(EntityLayout entity, ArrayList rowsList) { + double layoutY = entity.getLocation().y; + + for (Iterator iterator = rowsList.iterator(); iterator.hasNext();) { + List currentRow = (List) iterator.next(); + EntityLayout currentRowEntity = (EntityLayout) currentRow.get(0); + double currentRowY = currentRowEntity.getLocation().y; + if (layoutY >= currentRowY - DELTA && layoutY <= currentRowY + DELTA) { + currentRow.add(entity); + return; + } + } + List newRow = new ArrayList(); + newRow.add(entity); + rowsList.add(newRow); + } +}