package org.chesmapper.view.cluster; import java.awt.Dimension; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.Set; import javax.vecmath.Vector3f; import org.chesmapper.map.dataInterface.CompoundData; import org.chesmapper.map.main.Settings; import org.chesmapper.view.gui.LaunchCheSMapper; import org.mg.javalib.gui.MessagePanel; import org.mg.javalib.jitter.Jittering; import org.mg.javalib.jitter.NNComputer; import org.mg.javalib.util.ArrayUtil; import org.mg.javalib.util.DoubleKeyHashMap; import org.mg.javalib.util.MathUtil; import org.mg.javalib.util.SetUtil; import org.mg.javalib.util.SwingUtil; public class JitteringProvider { /** * stores the compound-set that has been used for this jittering level */ private List<Set<Compound>> sets = new ArrayList<Set<Compound>>(); /** * stores the level that has last been used for a compound-set */ private HashMap<Set<Compound>, Integer> levels = new HashMap<Set<Compound>, Integer>(); private DoubleKeyHashMap<CompoundData, Integer, Vector3f> positions = new DoubleKeyHashMap<CompoundData, Integer, Vector3f>(); private int currentLevel; private float[] minDistances; private static JitteringProvider staticInstance; public static final int STEPS = 10; public JitteringProvider(Clustering c) { staticInstance = null; /* * jittering in ches-mapper has fixed number of steps * each step has a min-distance that compounds should have * the min-distance is computed based on the entire data, within interval: * [ min-min-dist , min-min-dist + delta(max-min-distance,min-min-dist)/2 ] * the interval chunks are not equi-distant but using a log-scale */ minDistances = new float[STEPS + 1]; Vector3f v[] = new Vector3f[c.getCompounds().size()]; for (int i = 0; i < v.length; i++) v[i] = c.getCompounds().get(i).getPosition(); NNComputer nn = new NNComputer(v); nn.computeNaive(); float dist = nn.getMinMinDist(); float add = (nn.getMaxMinDist() - nn.getMinMinDist()) * 0.5f; double log[] = ArrayUtil.toPrimitiveDoubleArray(MathUtil.logBinning(STEPS, 1.2)); for (int i = 1; i <= STEPS; i++) minDistances[i] = dist + add * (float) log[i]; Settings.LOGGER .info("Initiated min-distances per level for jittering: " + ArrayUtil.toNiceString(minDistances)); } public Vector3f getPosition(CompoundData c, int level) { if (level == 0) return c.getPosition(); if (positions.containsKeyPair(c, level)) return positions.get(c, level); else return c.getPosition(); } public static Vector3f getPosition(CompoundData c) { if (staticInstance == null) return c.getPosition(); else return staticInstance.getPosition(c, staticInstance.currentLevel); } private Jittering createFromCompounds(List<Compound> compounds, int level, float minDist) { List<CompoundData> d = new ArrayList<CompoundData>(); for (Compound c : compounds) d.add(c.getCompoundData()); return create(d, level, minDist); } private Jittering create(List<CompoundData> d, int level, float minDist) { if (level <= 0) throw new IllegalArgumentException(); Vector3f v[] = new Vector3f[d.size()]; if (level == 1) for (int i = 0; i < v.length; i++) v[i] = new Vector3f(d.get(i).getPosition()); else for (int i = 0; i < v.length; i++) v[i] = new Vector3f(JitteringProvider.this.getPosition(d.get(i), level - 1)); return new Jittering(v, minDist, new Random()); } public void updateJittering(int level, Set<Compound> compounds) { Settings.LOGGER.info("Set jittering level to " + level); if (level == 0) { //do nothing, use orig positions } else if (sets.size() > level && SetUtil.isSubSet(sets.get(level), compounds)) { //do nothing, positions have already been computed } else { jitter(compounds, level); while (sets.size() < level + 1) sets.add(null); sets.set(level, compounds); } while (sets.size() > level + 1) sets.remove(level + 1); levels.put(compounds, level); staticInstance = this; currentLevel = level; } private void jitter(Set<Compound> c, int level) { jitter(new ArrayList<Compound>(c), level); } private void jitter(List<Compound> c, final int level) { Settings.LOGGER.info("Compute jittering for " + c.size() + " compounds for level " + level); Jittering j = createFromCompounds(c, level, minDistances[level]); j.jitter(); for (int i = 0; i < c.size(); i++) positions.put(c.get(i).getCompoundData(), level, j.getPosition(i)); } public int getJitteringResetLevel(Set<Compound> compounds) { int currentLevel = Math.max(0, (sets.size() - 1)); // System.err.println("current jittering level is " + currentLevel); int lastLevelForSet = levels.containsKey(compounds) ? levels.get(compounds) : 0; // System.err.println("last jittering level for this set is " + lastLevelForSet); if (lastLevelForSet == currentLevel) { if (lastLevelForSet == 0) return -1; // System.err.println("current jittering level is equal, check if sets are complient"); if (SetUtil.isSubSet(sets.get(currentLevel), compounds)) { // System.err.println("yes, nothing todo"); return -1; } else { // System.err.println("no, check lower level"); currentLevel = -1; } } // else // System.err.println("current jittering level differs"); for (int j = currentLevel; j >= 1; j--) { // System.err.println("check if this set is subset of level " + j); if (SetUtil.isSubSet(sets.get(j), compounds)) { // System.err.println("yes, use level " + j); return j; } } // System.err.println("not compatible, reset to 0"); return 0; } private static boolean showWarning = true; public static void showJitterWarning() { if (showWarning) { MessagePanel p = new MessagePanel(); p.addWarning(Settings.text("spread.warning"), Settings.text("spread.warning.details")); SwingUtil.showInDialog(p, "Warning", new Dimension(600, 300), null, Settings.TOP_LEVEL_FRAME); showWarning = false; } } public static void main(String[] args) { LaunchCheSMapper.init(); showJitterWarning(); } }