| /******************************************************************************* |
| * 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.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.zest.layouts.LayoutStyles; |
| 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. |
| * |
| * @author Casey Best |
| * @auhtor Rob Lintern |
| */ |
| public class RadialLayoutAlgorithm extends TreeLayoutAlgorithm { |
| 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"); |
| } |
| |
| 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; |
| } |
| |
| 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); |
| } |
| |
| 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. |
| */ |
| 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. |
| */ |
| 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; |
| } |
| |
| /** |
| * Find the center point between the roots |
| */ |
| 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()); |
| } |
| } |