/*
* Copyright 2011 Christian Thiemann <christian@spato.net>
* Developed at Northwestern University <http://rocs.northwestern.edu>
*
* This file is part of the SPaTo Visual Explorer (SPaTo).
*
* SPaTo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SPaTo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with SPaTo. If not, see <http://www.gnu.org/licenses/>.
*/
package net.spato.sve.app.layout;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Vector;
import processing.core.PApplet;
import net.spato.sve.app.SPaTo_Visual_Explorer;
public class Layout {
protected SPaTo_Visual_Explorer app = null;
public Projection proj;
public Scaling scal;
public String projection, scaling, order;
protected int NN = -1;
protected int pred[][]; // input data
protected float phi[][]; // posphi[r][i] is angular position of node i if r is root
protected int r = -1; // currently loaded slice
protected static HashMap<String,Object> layoutCache = new HashMap<String,Object>(); // FIXME: layout handling is still screwed up...
private class Cache {
Vector<Vector<Integer>> children;
int numTotalChildren[];
float sortValue[];
Cache(int r, float val[], boolean sortRecursively) {
children = new Vector<Vector<Integer>>();
for (int i = 0; i < NN; i++) children.add(new Vector<Integer>());
numTotalChildren = new int[NN];
if (val != null) sortValue = new float[NN];
// setup children vector
for (int i = 0; i < NN; i++)
if (pred[r][i] >= 0)
children.get(pred[r][i]).add(new Integer(i)); // i is a child of pred[r][i] (by definition)
// setup numTotalChildren and sortValue vectors
setupRecursively(r, val, sortRecursively);
// sort children vector
if (val != null)
for (int i = 0; i < NN; i++)
Collections.sort(children.get(i), new Comparator<Integer>() { public int compare(Integer o1, Integer o2) {
float v1 = sortValue[o1.intValue()], v2 = sortValue[o2.intValue()];
return (v1 < v2) ? -1 : (v1 > v2) ? +1 : 0; } });
}
void setupRecursively(int j, float val[], boolean sortRecursively) {
numTotalChildren[j] = children.get(j).size();
if (val != null) sortValue[j] = val[j];
float sortTmp = 0;
for (int ii = 0; ii < children.get(j).size(); ii++) {
int i = children.get(j).get(ii).intValue();
setupRecursively(i, val, sortRecursively);
numTotalChildren[j] += numTotalChildren[i];
if (sortRecursively) sortTmp += sortValue[i];
}
if (sortRecursively) sortValue[j] += sortTmp/10000;
}
}
public Layout(SPaTo_Visual_Explorer app, int[][] pred, String spec) {
this.app = app;
this.pred = pred;
NN = pred.length;
parseSpecification(spec);
setupProjection(projection);
setupScaling(scaling);
String hash = spec + "##" + pred.toString();
if (!layoutCache.containsKey(hash)) {
phi = new float[NN][NN];
float sortData[] = getSortData();
calculateLayout(sortData);
layoutCache.put(hash, phi);
}
phi = (float[][])layoutCache.get(hash);
}
public String getSpecification() { return projection + "_" + scaling + (!order.equals("unsorted") ? "__" + order : ""); }
public void setupProjection(String projection) {
if (!TomProjectionFactory.canProduce(projection)) {
app.console.logWarning("Unknown tomogram projection " + projection + ", using default");
projection = TomProjectionFactory.getDefaultProduct();
}
proj = TomProjectionFactory.produce(projection, NN);
this.projection = projection;
}
public void setupScaling(String scaling) { setupScaling(scaling, 1); }
public void setupScaling(String scaling, float x0) {
if (!ScalingFactory.canProduce(scaling)) {
app.console.logWarning("Unknown scaling " + scaling + ", using default");
scaling = ScalingFactory.getDefaultProduct();
}
scal = ScalingFactory.produce(scaling, NN);
if (scaling.equals("log")) ((LogScaling)scal).x0 = x0;
this.scaling = scaling;
}
public void updateProjection(int r, float D[][]) { proj.setPoints(scal.f(D[r]), phi[r]); this.r = r; }
private void parseSpecification(String spec) {
order = "unsorted";
String pieces[] = PApplet.split(spec, "__");
if (pieces.length > 1) order = pieces[1];
pieces = PApplet.split(pieces[0], '_');
projection = pieces[0];
scaling = (pieces.length > 1) ? pieces[1] : "id";
}
private float[] getSortData() {
return null; // FIXME: re-implement this feature...
/*if (order.equals("unsorted")) return null;
try {
String pieces[] = split(order, '_');
SPaToView.Dataset ds = null;
for (int d = 0; d < view.ND; d++)
if (view.data[d].xml.getString("id").equals(pieces[0]))
ds = view.data[d];
if (ds == null) throw new Exception("Dataset " + pieces[0] + " not found");
if (pieces.length < 2) throw new Exception("Quantity not specified");
SPaToView.Data data = null;
for (int q = 0; q < ds.NQ; q++)
if (ds.data[q].xml.getString("id").equals(pieces[1]))
data = ds.data[q];
return data.data;
} catch (Exception e) {
console.logWarning("" + e.getMessage() + ", defaulting to unsorted layout");
order = "unsorted";
return null;
}*/
}
private void calculateLayout(float sortData[]) {
app.console.logProgress("Calculating layout " + getSpecification());
for (int r = 0; r < NN; r++) {
Cache cache = new Cache(r, sortData, order.endsWith("_r"));
phi[r][r] = PApplet.PI; // we do this to move the root in the middle in LinearProjection
calculateLayoutRecursively(cache, r, r, 0, 0, 2*PApplet.PI);
app.console.updateProgress(r, NN);
}
app.console.finishProgress();
}
// cache, root node, current layout node, tree depth of node j, min and max angles
private void calculateLayoutRecursively(Cache cache, int r, int j, int d, float phimin, float phimax) {
int sumNTC = cache.numTotalChildren[j], cumNTC = 0; // sum of total children, cumulative sum
Vector<Integer> I = cache.children.get(j);
for (int ii = 0; ii < I.size(); ii++) {
int i = I.get(ii).intValue();
float iphimin = phimin + (phimax - phimin)*cumNTC/sumNTC;
cumNTC += cache.numTotalChildren[i] + 1; // sum of i's children plus i itself
float iphimax = phimin + (phimax - phimin)*cumNTC/sumNTC;
phi[r][i] = (iphimin + iphimax)/2;
//doc.slices[r].d[i] = d;
calculateLayoutRecursively(cache, r, i, d + 1, iphimin, iphimax);
}
}
}