package edu.tufts.vue.layout;
import java.util.*;
import edu.tufts.vue.dataset.Dataset;
import tufts.vue.LWComponent;
import tufts.vue.LWContainer;
import tufts.vue.LWLink;
import tufts.vue.LWMap;
import tufts.vue.LWNode;
import tufts.vue.LWSelection;
import tufts.vue.VUE;
public class HierarchicalLayout extends Layout {
public static final int MAX_DEPTH = 5; // Restricted max depth to 5
public static final int MIN_RADIUS_INCREASE = 80;
public LWMap createMap(Dataset ds, String mapName) throws Exception {
LWMap map = new LWMap(mapName);
return map;
}
public void layout(LWSelection selection) throws Exception {
HashMap<String, LWNode> processedNodes = new HashMap<String, LWNode>();
Iterator<LWComponent> iter = selection.iterator();
while (iter.hasNext()) {
LWComponent c = iter.next();
if (c.isManagedLocation())
continue;
if (c instanceof LWNode) {
LWNode node = (LWNode) c;
double centerX = c.getX() + c.getWidth() / 2;
double centerY = c.getY() + c.getHeight() / 2;
double angle = 0.0;
List<LWNode> relatedNodes = getRelated(node, processedNodes);
double radius = relatedNodes.size()
* (c.getHeight() + c.getWidth()) / 2 / Math.PI;
if (radius < 3 * c.getWidth())
radius = 3 * c.getWidth(); // want to make the radius
// atleast 3 times the width
if (radius < MIN_RADIUS_INCREASE)
radius = MIN_RADIUS_INCREASE;
int count = 0;
for (LWNode related : relatedNodes) {
related.setLocation(centerX + radius * Math.cos(angle)
- related.getWidth() / 2, centerY + radius
* Math.sin(angle) - related.getHeight() / 2);
layoutChildren(node, related, relatedNodes.size(),
processedNodes);
count++;
angle = Math.PI * 2 * count / relatedNodes.size();
// System.out.println("Completed Layout for: "+ related.getLabel());
}
}
}
}
private void layoutChildren(LWNode parentNode, LWNode currentNode,
int size, HashMap<String, LWNode> processedNodes) {
double centerX = currentNode.getX() + currentNode.getWidth() / 2;
double centerY = currentNode.getY() + currentNode.getHeight() / 2;
double centerParentX = parentNode.getX() + parentNode.getWidth() / 2;
double centerParentY = parentNode.getY() + parentNode.getHeight() / 2;
double angle = Math.atan2(centerY - centerParentY, centerX
- centerParentX);
List<LWNode> relatedNodes = getRelated(currentNode, processedNodes);
// System.out.println("Applying Layout to: "+currentNode.getLabel()+" parent:"+parentNode.getLabel()+" size:"+size+" related:"+relatedNodes.size());
if (relatedNodes.size() < 1)
return;
double radius = relatedNodes.size()
* (currentNode.getHeight() + currentNode.getWidth()) / 2
/ Math.PI;
if (radius < 2 * currentNode.getWidth())
radius = 2 * currentNode.getWidth(); // want to make the radius
// atleast 2 times the width
if (radius < MIN_RADIUS_INCREASE)
radius = MIN_RADIUS_INCREASE;
if (relatedNodes.size() == 1) {
for (LWNode related : relatedNodes) {
// System.out.println("Setting location for: "+related.getLabel());
related.setLocation(centerX + radius * Math.cos(angle)
- related.getWidth() / 2, centerY + radius
* Math.sin(angle) - related.getHeight() / 2);
if(relatedNodes.size()>0) {
layoutChildren(currentNode, related, relatedNodes.size(),
processedNodes);
}
}
} else if(relatedNodes.size()>1) {
int count = 0;
// computing the arc for plotting the children
double dist2 = Math.pow(centerX - centerParentX, 2)
+ Math.pow(centerY - centerParentY, 2);
double r1 = Math.sqrt(dist2);
double alpha = Math.PI / (size+1);
double beta = alpha + Math.asin(r1 * Math.sin(alpha) / radius);
if(Math.abs(r1* Math.sin(alpha) / radius) >=1) {
beta = alpha;
}
// if(alpha == Math.PI/2) beta= Math.PI/2;
// if(alpha == -Math.PI/2) beta = - Math.PI/2;
beta = beta *0.95; // avoid collision of two branches
for (LWNode related : relatedNodes) {
// double childAngle = angle+(relatedNodes.size()/2 - count)*2*beta/relatedNodes.size();
double childAngle = angle-beta*(2*count+1-relatedNodes.size())/relatedNodes.size();
count++;
// System.out.println("Setting location for: "+related.getLabel()+" alpha:"+alpha+" beta:"+beta+" childAngle:"+childAngle+" r1:"+r1+" radius:"+radius+" dist2:"+dist2 );
related.setLocation(centerX + radius * Math.cos(childAngle)
- related.getWidth() / 2, centerY + radius
* Math.sin(childAngle) - related.getHeight() / 2);
if(relatedNodes.size()>0) {
layoutChildren(currentNode, related, relatedNodes.size(),processedNodes);
}
}
}
}
private List<LWNode> getRelated(LWNode node, HashMap<String, LWNode> processedNodes) {
processedNodes.put(node.getID(), node);
List<LWNode> relatedNodes = new ArrayList<LWNode>();
Iterator<LWLink> i = node.getLinks().iterator();
while (i.hasNext()) {
LWLink link = i.next();
LWComponent head = link.getHead();
LWComponent tail = link.getTail();
if (head instanceof LWNode && tail instanceof LWNode) {
LWNode headNode = (LWNode) head;
LWNode tailNode = (LWNode) tail;
if ((processedNodes.get(tailNode.getID()) == null) && headNode == node) {
processedNodes.put(tailNode.getID(), tailNode);
relatedNodes.add(tailNode);
}
if ((processedNodes.get(headNode.getID()) == null) && tailNode == node) {
processedNodes.put(headNode.getID(), headNode);
relatedNodes.add(headNode);
}
}
}
return relatedNodes;
}
}