/*
* TabularLayout.java
*
* Created on October 8, 2008, 2:32 PM
*
* Copyright 2003-2010 Tufts University Licensed under the
* Educational Community License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.osedu.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/**
*
* @author akumar03
*/
package edu.tufts.vue.layout;
import java.io.*;
import java.util.*;
import edu.tufts.vue.dataset.*;
import tufts.vue.*;
import java.awt.geom.Point2D;
/*
* This layout has been designed specifically for the search results. It takes
* all the search results and displaces them in a filled circle layout. At the
* same time all the nodes that interfere are pushed from the center.
*/
public class SearchLayout extends Layout {
public static final double FACTOR = VueResources.getDouble("layout.space_ratio");
public static final int MAX_COLLISION_CHECK = VueResources.getInt("layout.check_overlap_number");
public static final double MIN_AVOID_DIST = VueResources.getDouble("layout.avoid_distance");
public static final double RIPPLE_RANGE = VueResources.getDouble("layout.ripple_range");
public LWMap createMap(Dataset ds, String mapName) throws Exception {
LWMap map = new LWMap(mapName);
return map;
}
public void layout(LWSelection selection) {
double centerX = 0;
double centerY = 0;
double totalNodeWidth = 0;
double totalNodeHeight = 0;
double xAdd = X_COL_SIZE; // default horizontal distance between the nodes
double yAdd = Y_COL_SIZE; //default vertical distance between nodes
int count = 0;
int total = 0;
// determine the mean node size
Iterator<LWComponent> iter = selection.iterator();
while (iter.hasNext()) {
LWComponent c = iter.next();
if (c.isManagedLocation())
continue;
if (c instanceof LWNode) {
LWNode node = (LWNode) c;
centerX += node.getLocation().getX();
centerY += node.getLocation().getY();
totalNodeWidth += node.getWidth();
totalNodeHeight += node.getHeight();
total++;
}
}
double meanNodeWidth = totalNodeWidth / total;
double meanNodeHeight = totalNodeHeight / total;
centerX = centerX / total;
centerY = centerY / total;
double radius = Math.sqrt(FACTOR * total * meanNodeWidth * meanNodeWidth / Math.PI);
double maxShift = radius ; //maximum shift of a node
double rippleRange= RIPPLE_RANGE*radius;
// generate a ripple with center(centerX,centerY) and radius
Iterator<LWComponent> i = VUE.getActiveMap().getAllDescendents(LWContainer.ChildKind.PROPER).iterator();
while (i.hasNext()) {
LWComponent c = i.next();
if (!selection.contains(c) && c instanceof LWNode) {
double x = c.getLocation().getX();
double y = c.getLocation().getY();
double angle = Math.atan2(centerY - y, x - centerX);
double dist = Point2D.distance(centerX, centerY, x, y);
if (dist < rippleRange && dist >MIN_AVOID_DIST) {
double newDist = dist + (rippleRange - dist) * maxShift/ rippleRange;
double newX = centerX + newDist * Math.cos(angle);
double newY = centerY - newDist * Math.sin(angle);
c.setLocation(newX, newY);
}
}
}
// layout out the selection nodes
i = selection.iterator();
while (i.hasNext()) {
LWComponent c = i.next();
if (c.isManagedLocation())
continue;
if (c instanceof LWNode) {
LWNode node = (LWNode) c;
double angle = Math.PI * 2 * Math.random();
double r = radius * (1 - Math.pow(Math.random(), 2.0));
double x = centerX + r * Math.cos(angle);
double y = centerY + r * Math.sin(angle);
boolean flag = true;
int col_count = 0;
while (flag && col_count < MAX_COLLISION_CHECK) {
final MapViewer viewer = VUE.getActiveViewer();
if ((viewer.pickNode((float) x, (float) y) != null) || (viewer.pickNode((float) x + node.getWidth(), (float) y + node.getHeight()) != null) || (viewer.pickNode((float) x, (float) y + node.getHeight()) != null) || (viewer.pickNode((float) x + node.getWidth(), (float) y) != null)) {
angle = Math.PI * 2 * Math.random();
r = radius * (1 - Math.pow(Math.random(), 2.0));
x = centerX + r * Math.cos(angle);
y = centerY + r * Math.sin(angle);
} else {
flag = false;
}
col_count++;
System.out.println("Checking collision for "+c.getLabel()+" count:"+col_count);
}
node.setLocation(x, y);
}
}
}
}