/*******************************************************************************
* Copyright (c) 2005, 2016 The Chisel Group 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: Casey Best, Ian Bull, Rob Lintern (The Chisel Group) - initial API and implementation
* Mateusz Matela - "Tree Views for Zest" contribution, Google Summer of Code 2009
* Matthias Wienand (itemis AG) - refactorings
******************************************************************************/
package org.eclipse.gef.layout.algorithms;
import org.eclipse.gef.geometry.planar.Point;
import org.eclipse.gef.geometry.planar.Rectangle;
import org.eclipse.gef.graph.Node;
import org.eclipse.gef.layout.ILayoutAlgorithm;
import org.eclipse.gef.layout.LayoutContext;
import org.eclipse.gef.layout.LayoutProperties;
/**
* 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
* @author Ian Bull
* @author Rob Lintern
* @author Mateusz Matela
* @author mwienand
*/
public class RadialLayoutAlgorithm implements ILayoutAlgorithm {
private static final double MAX_DEGREES = Math.PI * 2;
private double startDegree = 0;
private double endDegree = MAX_DEGREES;
private boolean resize = false;
private TreeLayoutAlgorithm treeLayout = new TreeLayoutAlgorithm();
/**
* Default constructor.
*/
public RadialLayoutAlgorithm() {
}
public void applyLayout(LayoutContext layoutContext, boolean clean) {
if (!clean)
return;
treeLayout.internalApplyLayout(layoutContext);
Node[] entities = layoutContext.getNodes();
Rectangle bounds = LayoutProperties.getBounds(layoutContext.getGraph());
computeRadialPositions(entities, bounds);
if (resize)
AlgorithmHelper.maximizeSizes(entities);
int insets = 4;
bounds.setX(bounds.getX() + insets);
bounds.setY(bounds.getY() + insets);
bounds.setWidth(bounds.getWidth() - 2 * insets);
bounds.setHeight(bounds.getHeight() - 2 * insets);
AlgorithmHelper.fitWithinBounds(entities, bounds, resize);
}
private void computeRadialPositions(Node[] entities, Rectangle bounds) {
Rectangle layoutBounds = AlgorithmHelper.getLayoutBounds(entities,
false);
layoutBounds.setX(bounds.getX());
layoutBounds.setWidth(bounds.getWidth());
for (int i = 0; i < entities.length; i++) {
Point location = LayoutProperties.getLocation(entities[i]);
if (layoutBounds.getWidth() == 0 || layoutBounds.getHeight() == 0) {
location.x = 0;
location.y = 0;
} else {
double percenttheta = (location.x - layoutBounds.getX())
/ layoutBounds.getWidth();
double distance = (location.y - layoutBounds.getY())
/ layoutBounds.getHeight();
double theta = startDegree
+ Math.abs(endDegree - startDegree) * percenttheta;
location.x = distance * Math.cos(theta);
location.y = distance * Math.sin(theta);
}
LayoutProperties.setLocation(entities[i], location);
}
}
/**
* Set the range the radial layout will use when
* {@link #applyLayout(LayoutContext, boolean)} is called. Both values must
* be in radians.
*
* @param startDegree
* The start angle for this algorithm (in degree).
* @param endDegree
* The end angle for this algorithm (in degree).
*/
// TODO: Could use GEF Geometry Angle instead
public void setRangeToLayout(double startDegree, double endDegree) {
this.startDegree = startDegree;
this.endDegree = endDegree;
}
/**
*
* @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;
treeLayout.setResizing(resize);
}
}