package org.chesmapper.view.gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import javax.vecmath.Vector3f;
import org.chesmapper.map.data.ClusteringData;
import org.chesmapper.map.dataInterface.CompoundGroupWithProperties;
import org.chesmapper.map.dataInterface.CompoundProperty;
import org.chesmapper.map.dataInterface.CompoundPropertyOwner;
import org.chesmapper.map.dataInterface.CompoundPropertyUtil;
import org.chesmapper.map.dataInterface.CompoundPropertyUtil.NominalColoring;
import org.chesmapper.map.dataInterface.FragmentProperty;
import org.chesmapper.map.dataInterface.NominalProperty;
import org.chesmapper.map.dataInterface.NumericProperty;
import org.chesmapper.map.dataInterface.SingleCompoundPropertyOwner;
import org.chesmapper.map.dataInterface.SubstructureSmartsType;
import org.chesmapper.map.gui.CheSMapperWizard;
import org.chesmapper.map.main.ScreenSetup;
import org.chesmapper.map.main.Settings;
import org.chesmapper.map.main.TaskProvider;
import org.chesmapper.view.cluster.Cluster;
import org.chesmapper.view.cluster.ClusterController;
import org.chesmapper.view.cluster.Clustering;
import org.chesmapper.view.cluster.ClusteringImpl;
import org.chesmapper.view.cluster.ClusteringUtil;
import org.chesmapper.view.cluster.Compound;
import org.chesmapper.view.cluster.CompoundFilter;
import org.chesmapper.view.cluster.CompoundFilterImpl;
import org.chesmapper.view.cluster.JitteringProvider;
import org.chesmapper.view.gui.View.AnimationSpeed;
import org.chesmapper.view.gui.swing.ComponentFactory;
import org.chesmapper.view.gui.util.CompoundPropertyHighlighter;
import org.chesmapper.view.gui.util.HighlightAutomatic;
import org.chesmapper.view.gui.util.Highlighter;
import org.chesmapper.view.gui.util.SortFilterDialog;
import org.chesmapper.view.gui.util.SubstructureHighlighter;
import org.jmol.adapter.smarter.SmarterJmolAdapter;
import org.jmol.api.JmolAdapter;
import org.jmol.api.JmolSimpleViewer;
import org.mg.javalib.gui.property.ColorGradient;
import org.mg.javalib.task.Task;
import org.mg.javalib.task.TaskDialog;
import org.mg.javalib.util.ArrayUtil;
import org.mg.javalib.util.ColorUtil;
import org.mg.javalib.util.ObjectUtil;
import org.mg.javalib.util.StringUtil;
import org.mg.javalib.util.SwingUtil;
import org.mg.javalib.util.ThreadUtil;
public class MainPanel extends JPanel implements ViewControler, ClusterController
{
GUIControler guiControler;
JmolPanel jmolPanel;
View view;
private ClusteringImpl clustering;
private boolean spinEnabled = false;
private boolean hideHydrogens = true;
private Style style = Style.wireframe;
private NominalColoring nominalColoring = NominalColoring.TwoThirdMode;
DisguiseMode disguiseUnHovered = DisguiseMode.solid;
DisguiseMode disguiseUnZoomed = DisguiseMode.translucent;
CompoundProperty compoundDescriptorProperty = null;
List<JComponent> ignoreMouseMovementPanels = new ArrayList<JComponent>();
private String getStyleString()
{
if (ScreenSetup.INSTANCE.isFontSizeLarge())
{
switch (style)
{
case wireframe:
return "spacefill 0; wireframe 0.08";
case ballsAndSticks:
return "wireframe 35; spacefill 21%";
case dots:
return "spacefill 85%";
}
throw new IllegalStateException("WTF");
}
else
{
switch (style)
{
case wireframe:
return "spacefill 0; wireframe 0.02";
case ballsAndSticks:
return "wireframe 25; spacefill 15%";
case dots:
return "spacefill 70%";
}
throw new IllegalStateException("WTF");
}
}
private Color getHighlightColor(Highlighter h, CompoundPropertyOwner m, CompoundProperty p, boolean textColor)
{
if (h == Highlighter.CLUSTER_HIGHLIGHTER)
if (m instanceof Compound)
return CompoundPropertyUtil.getClusterColor(clustering.getClusterIndexForCompound((Compound) m));
else
return null;
else
return getHighlightColor(m, p, textColor);
}
public Color getHighlightColor(CompoundPropertyOwner m, CompoundProperty p, boolean textColor)
{
return getHighlightColor(m, p, textColor, isBlackgroundBlack());
}
public Color getHighlightColor(CompoundPropertyOwner m, CompoundProperty p, boolean textColor,
boolean blackBackground)
{
if (m == null)
throw new IllegalStateException();
if (p == null || p.isUndefined())
return textColor ? ComponentFactory.getForeground(blackBackground) : null;
else if (p instanceof NominalProperty)
{
String val;
if (m instanceof CompoundGroupWithProperties)
val = CompoundPropertyUtil.getNominalHighlightValue((NominalProperty) p,
((CompoundGroupWithProperties) m).getNominalSummary((NominalProperty) p), nominalColoring);
else if (m instanceof SingleCompoundPropertyOwner)
val = ((SingleCompoundPropertyOwner) m).getStringValue((NominalProperty) p);
else
throw new IllegalStateException();
if (val == null)
return textColor ? ComponentFactory.getForeground(blackBackground) : CompoundPropertyUtil
.getNullValueColor();
else
return CompoundPropertyUtil.getNominalColor((NominalProperty) p, val);
}
else if (p instanceof NumericProperty)
{
try
{
if (m.getDoubleValue((NumericProperty) p) == null)
return textColor ? ComponentFactory.getForeground(blackBackground) : CompoundPropertyUtil
.getNullValueColor();
double val = clustering.getNormalizedDoubleValue(m, (NumericProperty) p);
if (Double.isNaN(val) || Double.isInfinite(val))
throw new NullPointerException("not null, but nan or infinite");
ColorGradient grad;
if (((NumericProperty) p).getHighlightColorGradient() != null)
grad = ((NumericProperty) p).getHighlightColorGradient();
else
grad = DEFAULT_COLOR_GRADIENT;
if (!blackBackground && grad.getMed().equals(Color.WHITE))
{
if (textColor)
grad = new ColorGradient(grad.high, Color.GRAY, grad.low);
else if (isAntialiasEnabled())
grad = new ColorGradient(grad.high, Color.WHITE, grad.low);
else
grad = new ColorGradient(grad.high, Color.LIGHT_GRAY, grad.low);
}
return grad.getColor(val);
}
catch (NullPointerException e)
{
System.err.println("Nullpointer exception in getHighlightColor: " + e.getMessage() + "\nproperty: " + p
+ " prop-owner: " + m + " get-double-value:" + m.getDoubleValue((NumericProperty) p)
+ " get-normalized-double-value: "
+ clustering.getNormalizedDoubleValue(m, (NumericProperty) p)
+ " get-log-normalized-double-value: "
+ clustering.getNormalizedLogDoubleValue(m, (NumericProperty) p));
e.printStackTrace();
return null;
}
}
else
throw new IllegalStateException();
}
@Override
public void setNominalColoring(NominalColoring nominalColoring)
{
if (this.nominalColoring != nominalColoring)
{
this.nominalColoring = nominalColoring;
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_COLORS_CHANGED);
String msg = "Color groups of nominal feature values according to ";
if (nominalColoring == NominalColoring.TwoThirdMode)
msg += "most frequent value >= 66%";
else if (nominalColoring == NominalColoring.Mode)
msg += "most frequent value";
else if (nominalColoring == NominalColoring.ActiveValueIncluded)
msg += "active value";
guiControler.showMessage(msg);
}
}
public NominalColoring getNominalColoring()
{
return nominalColoring;
}
public static enum Translucency
{
None, ModerateWeak, ModerateStrong, Strong;
}
private String getColorSuffixTranslucent(Translucency t)
{
if (t == Translucency.None)
return "";
double translucency[] = null;
if (style == Style.wireframe)
translucency = new double[] { -1, 0.4, 0.6, 0.8 };
else if (style == Style.ballsAndSticks)
translucency = new double[] { -1, 0.5, 0.7, 0.9 };
else if (style == Style.dots)
translucency = new double[] { -1, 0.5, 0.7, 0.9 };
else
throw new IllegalStateException("WTF");
double trans = translucency[ArrayUtil.indexOf(Translucency.values(), t)];
// if (Settings.SCREENSHOT_SETUP)
// trans = 0.99;// Math.min(0.95, trans + 0.15);
// Settings.LOGGER.warn(trans);
return "; color translucent " + trans;
}
HashMap<String, Highlighter[]> highlighters;
Highlighter selectedHighlighter = null;
Highlighter lastSelectedHighlighter = null;
CompoundProperty selectedHighlightCompoundProperty = null;
boolean highlighterLabelsVisible = false;
HighlightSorting highlightSorting = HighlightSorting.Median;
HighlightAutomatic highlightAutomatic;
HighlightMode highlightMode = HighlightMode.ColorCompounds;
boolean antialiasEnabled = ScreenSetup.INSTANCE.isAntialiasOn();
boolean highlightLastFeatureEnabled = false;
FeatureFilter featureFilter = FeatureFilter.None;
boolean featureSortingEnabled = true;
public Clustering getClustering()
{
return clustering;
}
public boolean isSpinEnabled()
{
return spinEnabled;
}
private int spinSpeed = 3;
public void setSpinEnabled(boolean spin)
{
setSpinEnabled(spin, false);
}
private void setSpinEnabled(boolean spinEnabled, boolean force)
{
if (this.spinEnabled != spinEnabled || force)
{
this.spinEnabled = spinEnabled;
view.setSpinEnabled(spinEnabled, spinSpeed);
fireViewChange(PROPERTY_SPIN_CHANGED);
guiControler.showMessage((spinEnabled ? "Enable" : "Disable") + " spinning.");
}
}
@Override
public void increaseSpinSpeed(boolean increase)
{
if (spinEnabled && (increase || spinSpeed > 2))
{
if (increase)
spinSpeed++;
else
spinSpeed--;
view.setSpinEnabled(spinEnabled, spinSpeed);
guiControler.showMessage((increase ? "Increase" : "Decrease") + " spin speed to " + spinSpeed + ".");
}
}
boolean singleCompoundSelection = false;
@Override
public void setSingleCompoundSelection(boolean b)
{
if (singleCompoundSelection != b)
{
singleCompoundSelection = b;
if (singleCompoundSelection)
fireViewChange(PROPERTY_SINGLE_COMPOUND_SELECTION_ENABLED);
guiControler.showMessage((singleCompoundSelection ? "Single compound" : "Cluster") + " selection enabled.");
}
}
@Override
public boolean isSingleCompoundSelection()
{
return singleCompoundSelection;
}
public MainPanel(GUIControler guiControler)
{
this.guiControler = guiControler;
jmolPanel = new JmolPanel();
setLayout(new BorderLayout());
add(jmolPanel);
clustering = new ClusteringImpl();
clustering.addListener(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_NEW)
|| evt.getPropertyName().equals(ClusteringImpl.CLUSTER_CLEAR))
{
setSuperimpose(false);
jitteringLevel = 0;
MainPanel.this.guiControler.updateTitle(clustering);
}
}
});
view = View.init(jmolPanel, guiControler, this, clustering);
highlightAutomatic = new HighlightAutomatic(this, clustering);
setBackgroundBlack(ComponentFactory.isBackgroundBlack(), true);
// mouse listener to click atoms or clusters (zoom in)
jmolPanel.addMouseListener(new MouseAdapter()
{
JPopupMenu popup;
public void mouseClicked(final MouseEvent e)
{
// MainPanel.this.guiControler.block("handle click");
clearMouseMoveWatchUpdates(false);
if (SwingUtilities.isLeftMouseButton(e))
{
Thread th = new Thread(new Runnable()
{
public void run()
{
int atomIndex = view.findNearestAtomIndex(e.getX(), e.getY());
if (atomIndex != -1)
{
Compound c = clustering.getCompoundWithJmolIndex(view.getAtomCompoundIndex(atomIndex));
boolean selectCluster = !clustering.isClusterActive() && !e.isControlDown();
boolean selectCompound = clustering.isClusterActive() || e.isShiftDown()
|| singleCompoundSelection;
if (selectCompound)
{
if (e.isControlDown())
toggleCompoundActive(c);
else
setCompoundActive(c, true);
}
else if (selectCluster) // compound is not selected
{
setClusterActive(clustering.getClusterForCompound(c), true, false);
}
}
else if (e.getClickCount() > 1)
{
if (clustering.isClusterActive())
{
if (clustering.isCompoundActive()
&& clustering.getActiveCluster().getNumCompounds() > 1)
clearCompoundActive(true);
else
clearClusterActive(true, true);
}
else
clearCompoundActive(true);
}
}
});
th.start();
}
else if (SwingUtilities.isRightMouseButton(e))
{
if (popup == null)
popup = MainPanel.this.guiControler.getPopup();
else
popup.updateUI();
popup.show(e.getComponent(), e.getX(), e.getY());
}
}
});
jmolPanel.addMouseMotionListener(new MouseAdapter()
{
public void mouseMoved(final MouseEvent ev)
{
updateMouse(ev.getPoint(), ev.isShiftDown());
}
});
}
Thread mouseMoveUpdateThread;
boolean mouseMoveUpdate = false;
Runnable mouseMoveRunnable = null;
@Override
public void clearMouseMoveWatchUpdates(boolean clearWatched)
{
if (mouseMoveUpdateThread != null)
synchronized (mouseMoveUpdateThread)
{
this.mouseMoveRunnable = null;
mouseMoveUpdate = false;
}
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
clearCompoundWatched();
clearClusterWatched();
}
});
}
@Override
public void doMouseMoveWatchUpdates(Runnable run)
{
if (mouseMoveUpdateThread == null)
{
mouseMoveUpdateThread = new Thread(new Runnable()
{
@Override
public void run()
{
while (true)
{
Runnable r = null;
synchronized (mouseMoveUpdateThread)
{
if (mouseMoveUpdate)
r = mouseMoveRunnable;
}
ThreadUtil.sleep(35);
synchronized (mouseMoveUpdateThread)
{
if (mouseMoveUpdate && r == mouseMoveRunnable)//check for override by new event
{
final Runnable fr = r;
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
fr.run();
}
});
mouseMoveUpdate = false;
}
}
ThreadUtil.sleep(10);
}
}
});
mouseMoveUpdateThread.start();
}
synchronized (mouseMoveUpdateThread)
{
mouseMoveRunnable = run;
mouseMoveUpdate = true;
}
}
Point mousePos;
@Override
public void updateMouseSelection(boolean shiftDown)
{
updateMouse(mousePos, shiftDown);
}
private void updateMouse(Point mousePos, final boolean shiftDown)
{
if (guiControler.isBlocked())
return;
this.mousePos = mousePos;
for (JComponent c : ignoreMouseMovementPanels)
{
if (c.isVisible())
{
Point p = SwingUtilities.convertPoint(MainPanel.this, mousePos, c);
if (p.x >= 0 && p.y >= 0 && p.x <= c.getWidth() && p.y <= c.getHeight())
return;
}
}
final int atomIndex = view.findNearestAtomIndex(mousePos.x, mousePos.y);
if (atomIndex == -1)
clearMouseMoveWatchUpdates(true);
else
doMouseMoveWatchUpdates(new Runnable()
{
@Override
public void run()
{
if (!clustering.isClusterActive() && !shiftDown && !singleCompoundSelection)
{
clustering.getCompoundWatched().clearSelection();
//clustering.getCompoundActive().clearSelection();
clustering.getClusterWatched().setSelected(
(clustering.getClusterIndexForJmolIndex(view.getAtomCompoundIndex(atomIndex))));
}
else
{
clustering.getClusterWatched().clearSelection();
clustering.getCompoundWatched().setSelected(view.getAtomCompoundIndex(atomIndex));
}
}
});
}
@Override
public void addIgnoreMouseMovementComponents(JComponent c)
{
this.ignoreMouseMovementPanels.add(c);
}
public Style getStyle()
{
return style;
}
private boolean putSpheresBackOn = false;
public void setStyle(Style style)
{
if (this.style != style)
{
if (style == Style.dots && highlightMode == HighlightMode.Spheres)
{
setHighlightMode(HighlightMode.ColorCompounds);
putSpheresBackOn = true;
}
else if ((style == Style.ballsAndSticks || style == Style.wireframe) && putSpheresBackOn)
{
setHighlightMode(HighlightMode.Spheres);
putSpheresBackOn = false;
}
guiControler.block("changing style");
this.style = style;
updateAllClustersAndCompounds(true);//force because of bounding boxes
fireViewChange(PROPERTY_STYLE_CHANGED);
if (style == Style.ballsAndSticks)
guiControler.showMessage("Draw compounds with balls (atoms) and sticks (bonds).");
else if (style == Style.wireframe)
guiControler.showMessage("Draw compounds with wireframes (shows only bonds).");
else if (style == Style.dots)
guiControler.showMessage("Draw compounds as dots.");
guiControler.unblock("changing style");
}
}
@Override
public HashMap<String, Highlighter[]> getHighlighters()
{
return highlighters;
}
@Override
public Highlighter getHighlighter()
{
return selectedHighlighter;
}
@Override
public void setHighlighter(Highlighter highlighter)
{
setHighlighter(highlighter, true);
}
@Override
public void setHighlighter(Highlighter highlighter, boolean showMessage)
{
if (this.selectedHighlighter != highlighter)
{
guiControler.block("set highlighter");
lastSelectedHighlighter = selectedHighlighter;
selectedHighlighter = highlighter;
if (selectedHighlighter instanceof CompoundPropertyHighlighter)
selectedHighlightCompoundProperty = ((CompoundPropertyHighlighter) highlighter).getProperty();
else
selectedHighlightCompoundProperty = null;
updateAllClustersAndCompounds(true);//force to make sure that sphere positions is right (otherwise - dots - change size - b&s - feature failes)
fireViewChange(PROPERTY_HIGHLIGHT_CHANGED);
if (showMessage)
{
String lastMsg = ".";
if (highlightLastFeatureEnabled && lastSelectedHighlighter != null
&& lastSelectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
{
if (lastSelectedHighlighter == Highlighter.CLUSTER_HIGHLIGHTER)
lastMsg = " (flattened spheroid highlights cluster assignement).";
else if (lastSelectedHighlighter instanceof CompoundPropertyHighlighter)
lastMsg = " (flattened spheroid highlights '" + lastSelectedHighlighter + "').";
}
if (highlighter == Highlighter.DEFAULT_HIGHLIGHTER)
guiControler.showMessage("Disable highlighting" + lastMsg);
else if (highlighter == Highlighter.CLUSTER_HIGHLIGHTER)
guiControler.showMessage("Highlight cluster assignement" + lastMsg);
else if (highlighter instanceof CompoundPropertyHighlighter)
guiControler.showMessage("Highlight feature values of '" + highlighter + "'" + lastMsg);
}
guiControler.unblock("set highlighter");
}
}
@Override
public void setHighlighter(CompoundProperty prop)
{
Highlighter high = getHighlighter(prop);
if (high != null)
setHighlighter(high);
}
@Override
public Highlighter getHighlighter(SubstructureSmartsType type)
{
for (Highlighter hs[] : highlighters.values())
for (Highlighter h : hs)
if (h instanceof SubstructureHighlighter && ((SubstructureHighlighter) h).getType() == type)
return h;
return null;
}
@Override
public Highlighter getHighlighter(CompoundProperty p)
{
for (Highlighter hs[] : highlighters.values())
for (Highlighter h : hs)
if (h instanceof CompoundPropertyHighlighter && ((CompoundPropertyHighlighter) h).getProperty() == p)
return h;
return null;
}
@Override
public CompoundProperty getHighlightedProperty()
{
return selectedHighlightCompoundProperty;
}
@Override
public void setHighlighter(SubstructureSmartsType type)
{
Highlighter high = getHighlighter(type);
if (high != null)
setHighlighter(high);
}
@Override
public void setSelectLastSelectedHighlighter()
{
setHighlighter(lastSelectedHighlighter);
}
@Override
public boolean isHighlighterLabelsVisible()
{
return highlighterLabelsVisible;
}
@Override
public void setHighlighterLabelsVisible(boolean selected)
{
if (this.highlighterLabelsVisible != selected)
{
highlighterLabelsVisible = selected;
updateAllClustersAndCompounds(false);
fireViewChange(PROPERTY_HIGHLIGHT_CHANGED);
if (selected)
guiControler.showMessage("Show label for each compound feature value.");
else
guiControler.showMessage("Do not show label for each compound feature value.");
}
}
@Override
public void setHighlightSorting(HighlightSorting sorting)
{
if (this.highlightSorting != sorting)
{
highlightSorting = sorting;
updateAllClustersAndCompounds(false);
fireViewChange(PROPERTY_HIGHLIGHT_CHANGED);
if (sorting == HighlightSorting.Max)
guiControler
.showMessage("Highlight superimposed cluster using the compound with the maximum feature value.");
else if (sorting == HighlightSorting.Median)
guiControler
.showMessage("Highlight superimposed cluster using the compound with the median feature value.");
else if (sorting == HighlightSorting.Min)
guiControler
.showMessage("Highlight superimposed cluster using the compound with the minimum feature value.");
}
}
@Override
public HighlightSorting getHighlightSorting()
{
return highlightSorting;
}
private static double boxTranslucency = 0.05;
/**
* udpates all clusters
*/
private void updateAllClustersAndCompounds(boolean forceUpdate)
{
if (forceUpdate || selectedHighlightCompoundProperty != clustering.getHighlightProperty())
clustering.setHighlighProperty(selectedHighlightCompoundProperty,
getHighlightColor(clustering, selectedHighlightCompoundProperty, true));
// int a = getClustering().getClusterActive().getSelected();
for (int j = 0; j < clustering.numClusters(); j++)
updateCluster(j, forceUpdate);
updateCompoundVisibilty(forceUpdate);
for (Compound m : clustering.getCompounds(true))
if (m.isVisible())
updateCompound(m.getJmolIndex(), forceUpdate);
}
private void updateCluster(int clusterIndex, boolean forceUpdate)
{
Cluster c = clustering.getCluster(clusterIndex);
boolean watch = clustering.getClusterActive().getSelected() == -1
&& clusterIndex == clustering.getClusterWatched().getSelected() && c.getNumCompounds() > 0;
if (forceUpdate || watch != c.isWatched())
{
c.setWatched(watch);
if (watch)
{
view.select(style == Style.dots ? c.getDotModeDisplayBitSet() : c.getBitSet());
view.scriptWait("boundbox { selected }");
view.scriptWait("boundbox off");
// view.scriptWait("font bb" + clusterIndex + " " + View.FONT_SIZE);
view.scriptWait("draw ID bb" + clusterIndex + " BOUNDBOX color "
+ ColorUtil.toJMolString(ComponentFactory.LIST_WATCH_BACKGROUND) + " translucent "
+ boxTranslucency + " MESH NOFILL \"" + c.toStringWithValue() + "\"");
// jmolPanel.repaint(); // HACK to avoid label display errors
}
else
view.scriptWait("draw bb" + clusterIndex + " off");
}
if (forceUpdate || selectedHighlightCompoundProperty != c.getHighlightProperty())
c.setHighlighProperty(selectedHighlightCompoundProperty,
getHighlightColor(c, selectedHighlightCompoundProperty, true));
c.setHighlightSorting(highlightSorting);
}
private void updateCompoundVisibilty(boolean forceUpdate)
{
BitSet toDisplay = new BitSet();
BitSet toHide = new BitSet();
int activeCluster = clustering.getClusterActive().getSelected();
for (Compound m : clustering.getCompounds(true))
{
boolean visible;
if (activeCluster != -1)
{
// there is a active cluster
if (clustering.getClusterIndexForJmolIndex(m.getJmolIndex()) != activeCluster)
// the compound is not in the active cluster
visible = false;
else
{ // compound is in the active cluster
if (clustering.getCompoundActive().isSelected(m.getJmolIndex())
|| clustering.getCompoundWatched().isSelected(m.getJmolIndex()))
visible = true;
else if (clustering.getCompoundActive().getNumSelected() == 1 && zoomToSingleSelectedCompound)
{
// a single different compound is selected
visible = disguiseUnZoomed != DisguiseMode.invisible;
}
else
visible = true;
}
}
else
// there is no active cluster, therefore compound cannot be hidden
visible = true;
if (compoundFilter != null && !compoundFilter.accept(m))
visible = false;
if (!visible)
{
// this is required for invisible compounds as well, e.g. for the compound-list and sorting stuff
m.setHighlightCompoundProperty(selectedHighlightCompoundProperty);
}
if (!visible && (m.isVisible() || forceUpdate))
{
if (m.isSphereVisible() || forceUpdate)
{
m.setSphereVisible(false);
view.hideSphere(m);
}
if (m.isShowActiveBox() || forceUpdate)
{
m.setShowActiveBox(false);
view.scriptWait("draw bb" + m.getJmolIndex() + "a OFF");
}
if (m.isShowHoverBox() || forceUpdate)
{
m.setShowHoverBox(false);
view.scriptWait("draw bb" + m.getJmolIndex() + "h OFF");
}
m.setVisible(visible);
toHide.or(style == Style.dots ? m.getDotModeDisplayBitSet() : m.getBitSet());
}
else if (visible && (!m.isVisible() || forceUpdate))
{
m.setVisible(visible);
toDisplay.or(style == Style.dots ? m.getDotModeDisplayBitSet() : m.getBitSet());
}
}
view.hide(toHide);
view.display(toDisplay);
}
public static boolean vectorEquals(Vector3f o1, Vector3f o2)
{
if (o1 == null)
return o2 == null;
else if (o2 == null)
return false;
else
return o1.x == o2.x && o1.y == o2.y && o1.z == o2.z;
}
/**
* updates single compound
* forceUpdate = true -> everything is reset (independent of compound is part of active cluster or if single props have changed)
*
* shows/hides box around compound
* show/hides compound label
* set compound translucent/opaque
* highlight substructure in compound
*/
private void updateCompound(int compoundJmolIndex, boolean forceUpdate)
{
int clus = clustering.getClusterIndexForJmolIndex(compoundJmolIndex);
Cluster c = clustering.getCluster(clus);
Compound m = clustering.getCompoundWithJmolIndex(compoundJmolIndex);
if (m == null)
{
Settings.LOGGER.warn("compound is null!");
return;
}
int activeCluster = clustering.getClusterActive().getSelected();
int watchedCluster = clustering.getClusterWatched().getSelected();
boolean showHoverBox = false;
boolean showHoverBoxLabels = false;
boolean showActiveBox = false;
boolean showLabel = false;
boolean translucent = false;
// inside the active cluster
if (clus == activeCluster)
{
if (!clustering.getCompoundWatched().isSelected(compoundJmolIndex)
&& !clustering.getCompoundActive().isSelected(compoundJmolIndex))
{
int numWatched = clustering.getCompoundWatched().getNumSelected();
int numActive = clustering.getCompoundActive().getNumSelected();
if (numWatched > 0 || numActive > 1 || (numActive == 1 && !(view.getZoomTarget() instanceof Compound)))
translucent |= (disguiseUnHovered == DisguiseMode.translucent || c.isSuperimposed());
if (numActive == 1 && view.getZoomTarget() instanceof Compound)
translucent |= disguiseUnZoomed == DisguiseMode.translucent;
}
if (selectedHighlighter instanceof CompoundPropertyHighlighter)
showLabel = true;
if (clustering.getCompoundWatched().isSelected(compoundJmolIndex) && !c.isSuperimposed())
{
showHoverBox = true;
showHoverBoxLabels = clustering.getCompoundWatched().getNumSelected() == 1;
}
if (clustering.getCompoundActive().isSelected(compoundJmolIndex) && !c.isSuperimposed())
showActiveBox = true;
}
else
{
List<Compound> compounds;
if (selectedHighlighter instanceof CompoundPropertyHighlighter)
compounds = c.getCompoundsInOrder(((CompoundPropertyHighlighter) selectedHighlighter).getProperty(),
highlightSorting);
else
compounds = c.getCompounds();
if (selectedHighlightCompoundProperty != null
&& (compounds.indexOf(m) == 0 || !clustering.isSuperimposed()))
showLabel = true;
if (clustering.isSuperimposed())
translucent = (compounds.indexOf(m) > 0);
else if (disguiseUnHovered == DisguiseMode.translucent)
{
if (clustering.isCompoundWatched() || clustering.isCompoundActive())
{
translucent = !(clustering.getCompoundWatched().isSelected(compoundJmolIndex) || clustering
.getCompoundActive().isSelected(compoundJmolIndex));
}
else if (watchedCluster == -1 || selectedHighlighter == Highlighter.CLUSTER_HIGHLIGHTER)
translucent = false;
else
translucent = (clus != watchedCluster);
}
if (clustering.getCompoundWatched().isSelected(compoundJmolIndex) && !c.isSuperimposed())
{
showHoverBox = true;
showHoverBoxLabels = clustering.getCompoundWatched().getNumSelected() == 1;
}
if (clustering.getCompoundActive().isSelected(compoundJmolIndex) && !c.isSuperimposed())
showActiveBox = true;
}
String smarts = null;
if (style != Style.dots)
{
if (selectedHighlighter instanceof SubstructureHighlighter)
smarts = c.getSubstructureSmarts(((SubstructureHighlighter) selectedHighlighter).getType());
else if (selectedHighlighter instanceof CompoundPropertyHighlighter
&& ((CompoundPropertyHighlighter) selectedHighlighter).getProperty() instanceof FragmentProperty)
smarts = ((FragmentProperty) ((CompoundPropertyHighlighter) selectedHighlighter).getProperty())
.getSmarts();
}
if (!highlighterLabelsVisible)
showLabel = false;
Color highlightColorCompound = getHighlightColor(selectedHighlighter, m, selectedHighlightCompoundProperty,
false);
Color highlightColorText = getHighlightColor(selectedHighlighter, m, selectedHighlightCompoundProperty, true);
String highlightColorString = highlightColorCompound == null ? null : "color "
+ ColorUtil.toJMolString(highlightColorCompound);
String compoundColor;
if (highlightMode == HighlightMode.Spheres)
compoundColor = "color cpk";
else
compoundColor = highlightColorCompound == null ? "color cpk" : "color "
+ ColorUtil.toJMolString(highlightColorCompound);
if (compoundColor.equals("color cpk") && style == Style.dots)
compoundColor = "color "
+ ColorUtil.toJMolString(isBlackgroundBlack() ? Color.LIGHT_GRAY.brighter() : Color.GRAY);
boolean sphereVisible = (highlightMode == HighlightMode.Spheres && highlightColorString != null);
boolean lastFeatureSphereVisible = sphereVisible && highlightLastFeatureEnabled;
Translucency translucency;
if (translucent)
{
if (clustering.isSuperimposed())
{
translucency = Translucency.Strong;
if (highlightMode == HighlightMode.Spheres)
sphereVisible = false;
}
else
{
if (clus == activeCluster)
{
if (c.getNumCompounds() <= 5)
translucency = Translucency.ModerateWeak;
else if (c.getNumCompounds() <= 15)
translucency = Translucency.ModerateStrong;
else
translucency = Translucency.Strong;
}
else
translucency = Translucency.ModerateStrong;
}
}
else
translucency = Translucency.None;
boolean styleUpdate = style != m.getStyle();
boolean styleDotUpdate = (style == Style.dots && m.getStyle() != Style.dots)
|| (style != Style.dots && m.getStyle() == Style.dots);
boolean compoundUpdate = styleUpdate || translucency != m.getTranslucency()
|| !ObjectUtil.equals(compoundColor, m.getCompoundColor());
//showLabel is enough because superimpose<->not-superimpose is not tracked in compound
boolean checkLabelUpdate = showLabel || (!showLabel && m.getLabel() != null)
|| !ObjectUtil.equals(compoundColor, m.getCompoundColor())
|| selectedHighlightCompoundProperty != m.getHighlightCompoundProperty();
boolean sphereUpdate = sphereVisible != m.isSphereVisible()
|| (lastFeatureSphereVisible != m.isLastFeatureSphereVisible())
|| (sphereVisible && (translucency != m.getTranslucency() || !ObjectUtil.equals(highlightColorString,
m.getHighlightColorString())));
boolean spherePositionUpdate = sphereVisible && !vectorEquals(m.getPosition(), m.getSpherePosition());
boolean hoverBoxUpdate = showHoverBox != m.isShowHoverBox();
boolean activeBoxUpdate = showActiveBox != m.isShowActiveBox();
boolean smartsUpdate = compoundUpdate || smarts != m.getHighlightedSmarts();
m.setCompoundColor(compoundColor);
m.setStyle(style);
m.setTranslucency(translucency);
m.setHighlightCompoundProperty(selectedHighlightCompoundProperty);
m.setHighlightColor(highlightColorString, highlightColorText);
m.setSpherePosition(m.getPosition());
m.setSphereVisible(sphereVisible);
m.setLastFeatureSphereVisible(lastFeatureSphereVisible);
m.setShowHoverBox(showHoverBox);
m.setShowActiveBox(showActiveBox);
if (styleDotUpdate)// || forceUpdate)
{
if (style == Style.dots)
{
view.hide(m.getDotModeHideBitSet());
clustering.moveForDotMode(m, true);
}
else
{
clustering.moveForDotMode(m, false);
view.display(m.getDotModeHideBitSet());
}
}
// SET SELECTION
if (style == Style.dots)
view.select(m.getDotModeDisplayBitSet());
else
view.select(m.getBitSet());
if (forceUpdate || styleUpdate)
view.scriptWait(getStyleString());
if (forceUpdate || compoundUpdate)
view.scriptWait(compoundColor + getColorSuffixTranslucent(translucency));
if (forceUpdate || sphereUpdate || spherePositionUpdate)
{
if (sphereVisible)
view.showSphere(m, lastFeatureSphereVisible, forceUpdate || spherePositionUpdate);
else
view.hideSphere(m);
}
if (forceUpdate || hoverBoxUpdate)
{
if (showHoverBox)
{
if (style == Style.dots)
view.scriptWait("boundbox { selected } { 2 2 2 }");
else
view.scriptWait("boundbox { selected }");
view.scriptWait("boundbox off");
// view.scriptWait("font bb" + m.getJmolIndex() + "h " + View.FONT_SIZE);
String label = "";
if (showHoverBoxLabels)
label = " \"" + m.toStringWithValue() + "\"";
view.scriptWait("draw ID bb" + m.getJmolIndex() + "h BOUNDBOX color "
+ ColorUtil.toJMolString(ComponentFactory.LIST_WATCH_BACKGROUND) + " translucent "
+ boxTranslucency + " MESH NOFILL" + label);
// jmolPanel.repaint(); // HACK to avoid label display errors
}
else
view.scriptWait("draw bb" + m.getJmolIndex() + "h OFF");
}
if (forceUpdate || activeBoxUpdate)
{
if (showActiveBox)
{
if (style == Style.dots)
view.scriptWait("boundbox { selected } { 2 2 2 }");
else
view.scriptWait("boundbox { selected }");
view.scriptWait("boundbox off");
// view.scriptWait("font bb" + m.getJmolIndex() + "a " + View.FONT_SIZE);
view.scriptWait("draw ID bb" + m.getJmolIndex() + "a BOUNDBOX color "
+ ColorUtil.toJMolString(ComponentFactory.LIST_ACTIVE_BACKGROUND) + " translucent "
+ boxTranslucency + " MESH NOFILL");
// jmolPanel.repaint(); // HACK to avoid label display errors
}
else
{
view.scriptWait("draw bb" + m.getJmolIndex() + "a OFF");
}
}
// CHANGES JMOL SELECTION !!!
if (forceUpdate || smartsUpdate)
{
boolean match = false;
if (smarts != null && smarts.length() > 0)
{
BitSet matchBitSet = m.getSmartsMatch(smarts);
if (matchBitSet.cardinality() > 0)
{
match = true;
if (m.getHighlightedSmarts() != null)
{
// first draw whole compound red again, to remove old match
if (highlightMode == HighlightMode.Spheres)
view.scriptWait("color cpk" + getColorSuffixTranslucent(translucency));
else
view.scriptWait(compoundColor + getColorSuffixTranslucent(translucency));
}
m.setHighlightedSmarts(smarts);
view.select(matchBitSet);
view.scriptWait("color " + View.convertColor(CompoundPropertyUtil.HIGHILIGHT_MATCH_COLORS[2])
+ getColorSuffixTranslucent(translucency));
}
}
if (!match)// || forceUpdate || translucentUpdate)
{
m.setHighlightedSmarts(null);
if (highlightMode == HighlightMode.Spheres)
view.scriptWait("color cpk" + getColorSuffixTranslucent(translucency));
else
view.scriptWait(compoundColor + getColorSuffixTranslucent(translucency));
}
}
// MAY CHANGE JMOL SELECTION !!!
if (forceUpdate || checkLabelUpdate)
{
String labelString = null;
if (showLabel)
{
if (activeCluster == -1 && clustering.isSuperimposed())
{
labelString = c.getName() + " - "
+ ((CompoundPropertyHighlighter) selectedHighlighter).getProperty() + ": "
+ c.getSummaryStringValue(selectedHighlightCompoundProperty, false);
}
else
{
CompoundProperty p = ((CompoundPropertyHighlighter) selectedHighlighter).getProperty();
Object val = clustering.getCompoundWithJmolIndex(compoundJmolIndex).getFormattedValue(p);
// Settings.LOGGER.warn("label : " + i + " : " + c + " : " + val);
labelString = ((CompoundPropertyHighlighter) selectedHighlighter).getProperty() + ": " + val;
}
}
// CHANGES JMOL SELECTION!!
if (forceUpdate || !ObjectUtil.equals(labelString, m.getLabel()))
{
m.setLabel(labelString);
view.selectFirstCarbonAtom(m.getBitSet());
// BitSet empty = new BitSet(bs.length());
// empty.set(bs.nextSetBit(0));
// view.select(empty);
if (showLabel)
{
view.scriptWait("set fontSize " + ScreenSetup.INSTANCE.getFontSize());
view.scriptWait("label \"" + labelString + "\"");
}
else
{
// Settings.LOGGER.warn("label : " + i + " : " + c + " : off");
view.scriptWait("label OFF");
}
}
}
}
public void init(ClusteringData clusteredDataset)
{
if (clustering.numClusters() > 0)
throw new IllegalStateException("only done once at the start");
clustering.newClustering(clusteredDataset);
if (clustering.isBigDataMode())
featureSortingEnabled = false;
if (featureSortingEnabled)
clustering.initFeatureNormalization();
clustering.getClusterActive().addListenerFirst(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent e)
{
int cIndexOldArray[] = ((int[]) e.getOldValue());
int cIndexOld = cIndexOldArray.length == 0 ? -1 : cIndexOldArray[0];
singleCompoundSelection = false;
updateClusterSelection(clustering.getClusterActive().getSelected(), cIndexOld, true);
}
});
clustering.getClusterWatched().addListenerFirst(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent e)
{
int cIndexOldArray[] = ((int[]) e.getOldValue());
int cIndexOld = cIndexOldArray.length == 0 ? -1 : cIndexOldArray[0];
updateClusterSelection(clustering.getClusterWatched().getSelected(), cIndexOld, false);
}
});
clustering.getCompoundActive().addListenerFirst(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent e)
{
updateCompoundActiveSelection(((int[]) e.getNewValue()), ((int[]) e.getOldValue()));
}
});
clustering.getCompoundWatched().addListenerFirst(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent e)
{
updateAllClustersAndCompounds(false);
}
});
clustering.addListenerFirst(new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_ADDED))
updateClusteringNew();
else if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_REMOVED))
updateClusterRemoved();
else if (evt.getPropertyName().equals(ClusteringImpl.CLUSTER_MODIFIED))
updateAllClustersAndCompounds(true);
else if (evt.getPropertyName().equals(ClusteringImpl.PROPERTY_ADDED))
newHighlighters(false);
}
});
updateClusteringNew();
}
private void newHighlighters(boolean init)
{
Highlighter[] h = new Highlighter[] { Highlighter.DEFAULT_HIGHLIGHTER };
if (clustering.getNumClusters() > 1)
h = ArrayUtil.concat(Highlighter.class, h, new Highlighter[] { Highlighter.CLUSTER_HIGHLIGHTER });
if (clustering.getAdditionalProperties() != null)
for (CompoundProperty p : clustering.getAdditionalProperties())
h = ArrayUtil.concat(Highlighter.class, h, new Highlighter[] { CompoundPropertyHighlighter.create(p) });
if (clustering.getSubstructureSmartsType() != null)
h = ArrayUtil.concat(Highlighter.class, h,
new Highlighter[] { SubstructureHighlighter.create(clustering.getSubstructureSmartsType()) });
highlighters = new LinkedHashMap<String, Highlighter[]>();
highlighters.put("", h);
List<CompoundProperty> props = clustering.getProperties();
CompoundPropertyHighlighter[] featureHighlighters = new CompoundPropertyHighlighter[props.size()];
int fCount = 0;
for (CompoundProperty p : props)
featureHighlighters[fCount++] = CompoundPropertyHighlighter.create(p);
highlighters.put("Features NOT selected for mapping", featureHighlighters);
props = clustering.getFeatures();
featureHighlighters = new CompoundPropertyHighlighter[props.size()];
fCount = 0;
for (CompoundProperty p : props)
featureHighlighters[fCount++] = CompoundPropertyHighlighter.create(p);
highlighters.put("Features selected for mapping", featureHighlighters);
if (init)
{
if (clustering.getNumClusters() > 1)
selectedHighlighter = Highlighter.CLUSTER_HIGHLIGHTER;
else
selectedHighlighter = Highlighter.DEFAULT_HIGHLIGHTER;
selectedHighlightCompoundProperty = null;
lastSelectedHighlighter = selectedHighlighter;
highlightAutomatic.init();
}
fireViewChange(PROPERTY_NEW_HIGHLIGHTERS);
}
private void updateClusteringNew()
{
newHighlighters(true);
updateAllClustersAndCompounds(true);
view.scriptWait("frame " + view.getCompoundNumberDotted(0) + " "
+ view.getCompoundNumberDotted(clustering.numCompounds() - 1));
setSpinEnabled(isSpinEnabled(), true);
if (clustering.isBigDataMode() && style != Style.dots)
setStyle(Style.dots);
initCompoundDescriptor();
view.suspendAnimation("new clustering");
if (!isAllClustersSpreadable())
setSuperimpose(true);
if (clustering.getNumClusters() == 1)
clustering.getClusterActive().setSelected(0);
else
view.zoomTo(clustering, null);
view.proceedAnimation("new clustering");
if (clustering.isRandomEmbedding())
guiControler.showMessage(clustering.getName());
else
guiControler.showMessage("Chemical space mapping of " + clustering.getName());
}
private void updateClusterRemoved()
{
updateAllClustersAndCompounds(true);
if (clustering.getNumClusters() <= 1)
newHighlighters(selectedHighlighter == Highlighter.CLUSTER_HIGHLIGHTER);
if (clustering.getNumClusters() == 1)
clustering.getClusterActive().setSelected(0);
}
int ctrlHintCount = 3;
private void updateCompoundActiveSelection(int mIndex[], int mIndexOld[])
{
int activeCluster = clustering.getClusterActive().getSelected();
Zoomable currentZoom = view.getZoomTarget();
final Zoomable z;
if (!clustering.isSuperimposed())
{
if (mIndex.length == 1)
{ // one compound selected
if (zoomToSingleSelectedCompound)
z = clustering.getCompoundWithJmolIndex(mIndex[0]);
else
z = null;
}
else if (mIndex.length > 1)
{ // more than one compound selected -> zoom out if necessary
if (currentZoom instanceof Compound)
z = clustering.getUniqueClusterForJmolIndices(mIndex);
else
z = null;
}
else
{ // no compound selected -> zoom back to cluster of previously selected compound
if (currentZoom instanceof Compound && mIndexOld.length > 0 && activeCluster != -1)
z = clustering.getClusterForJmolIndex(mIndexOld[0]);
else
z = null;
}
}
else
z = null;
if (z == null || z instanceof Cluster)
updateAllClustersAndCompounds(false);
if (z != null)
{
runInBackground(new Runnable()
{
public void run()
{
if (view.getZoomTarget() != z && view.isAnimated())
{
if (z instanceof Compound)
{
String msg = "Zoom to compound '" + z + "'.";
if (ctrlHintCount > 0)
{
msg = "<html><p align=\"center\">"
+ msg
+ "<br><span style=\"font-size:"
+ (int) (ScreenSetup.INSTANCE.getFontSize() * 0.8)
+ "px\">Hold down 'Ctrl'-key to select (multiple) compounds without zooming in.</span></p></html>";
ctrlHintCount--;
}
guiControler.showMessage(msg);
}
else if (clustering.getNumClusters() == 1)
guiControler.showMessage("Zoom out to show all compounds.");
else
guiControler.showMessage("Zoom to cluster '" + z + "'.");
view.zoomTo(z, AnimationSpeed.SLOW);
}
if (z instanceof Compound) // zooming in, update afterwards
{
updateAllClustersAndCompounds(false);
}
}
});
}
}
/**
* the cluster selection has changed
* active=true -> the selection change is about active/inactive
* active=false -> the selection change is about watched/not-watched
*
* @param cIndex
* @param cIndexOld
* @param activeClusterChanged
*/
private void updateClusterSelection(int cIndex, final int cIndexOld, final boolean activeClusterChanged)
{
// ignore watch updates when a cluster is active
if (!activeClusterChanged && clustering.isClusterActive())
return;
if (clustering.getNumClusters() == 0)
return;
if (!activeClusterChanged)
updateAllClustersAndCompounds(false);
else
{
// reset to cluster before zooming out
boolean updated = highlightAutomatic.resetClusterHighlighter(activeClusterChanged);
if (!updated && cIndexOld != -1)
//zooming away from cluster, update before
updateAllClustersAndCompounds(false);
if (cIndex == -1)
guiControler.showMessage("Zoom out to show all clusters.");
else if (clustering.getNumClusters() == 1)
guiControler.showMessage("Zoom out to show all compounds.");
else
guiControler.showMessage("Zoom to cluster '" + clustering.getCluster(cIndex) + "'.");
if (guiControler.isVisible())
runInBackground(new Runnable()
{
@Override
public void run()
{
zoomAndSuperimpose(true, false);
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
boolean updated = highlightAutomatic.resetDefaultHighlighter(activeClusterChanged);
if (!updated && cIndexOld == -1)
//zooming away from clustering into cluster, update after
updateAllClustersAndCompounds(false);
}
});
}
});
else
{
zoomAndSuperimpose(false, false);
updated = highlightAutomatic.resetDefaultHighlighter(activeClusterChanged);
if (!updated && cIndexOld == -1)
//zooming away from clustering into cluster, update after
updateAllClustersAndCompounds(false);
}
}
}
static class JmolPanel extends JPanel
{
JmolSimpleViewer viewer;
JmolAdapter adapter;
JmolPanel()
{
adapter = new SmarterJmolAdapter();
viewer = JmolSimpleViewer.allocateSimpleViewer(this, adapter);
}
public JmolSimpleViewer getViewer()
{
return viewer;
}
final Dimension currentSize = new Dimension();
final Rectangle rectClip = new Rectangle();
final Dimension dimSize = new Dimension();
public void paint(Graphics g)
{
////code for old version
// getSize(currentSize);
// g.getClipBounds(rectClip);
// if (g != null && currentSize != null && rectClip != null)
// viewer.renderScreenImage(g, currentSize, rectClip);
getSize(dimSize);
if (dimSize.width == 0)
return;
viewer.renderScreenImage(g, dimSize.width, dimSize.height);
}
}
@Override
public boolean isSuperimpose()
{
return clustering.isSuperimposed();
}
@Override
public void setSuperimpose(boolean superimpose)
{
if (clustering.isSuperimposed() != superimpose)
{
clustering.setSuperimposed(superimpose);
if (superimpose)
guiControler.showMessage("Move compounds to cluster center.");
else
guiControler.showMessage("Move compounds to compound positions.");
if (guiControler.isVisible())
runInBackground(new Runnable()
{
@Override
public void run()
{
zoomAndSuperimpose(true, true);
updateAllClustersAndCompounds(false);
}
});
else
{
zoomAndSuperimpose(false, false);
updateAllClustersAndCompounds(false);
}
}
}
@Override
public boolean isAllClustersSpreadable()
{
for (Cluster c : clustering.getClusters())
if (c.isSpreadable())
return true;
return false;
}
@Override
public boolean isSingleClusterSpreadable()
{
if (!clustering.isClusterActive())
throw new IllegalStateException();
return clustering.getCluster(clustering.getClusterActive().getSelected()).isSpreadable();
}
@Override
public boolean isAntialiasEnabled()
{
return antialiasEnabled;
}
@Override
public void setAntialiasEnabled(boolean b)
{
if (this.antialiasEnabled != b)
{
this.antialiasEnabled = b;
view.setAntialiasOn(antialiasEnabled);
fireViewChange(PROPERTY_ANTIALIAS_CHANGED);
guiControler.showMessage((b ? "Enable" : "Disable") + " antialiasing.");
}
}
private void zoomAndSuperimpose(boolean animateZoom, boolean animateSuperimpose)
{
if (animateZoom || animateSuperimpose)
{
SwingUtil.checkNoAWTEventThread();
if (guiControler.isVisible() && !guiControler.isBlocked())
throw new IllegalStateException("gui not blocked");
}
final List<Cluster> c = new ArrayList<Cluster>();
final Cluster activeCluster;
final boolean superimpose = clustering.isSuperimposed();
final boolean setAntialiasBackOn;
if (antialiasEnabled && view.isAntialiasOn())
{
setAntialiasBackOn = true;
view.setAntialiasOn(false);
}
else
setAntialiasBackOn = false;
if (animateSuperimpose && superimpose && clustering.getCompoundActive().getNumSelected() > 0)
{
if (animateSuperimpose)
view.suspendAnimation("deselect for superimpose");
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundActive().clearSelection();
}
});
if (animateSuperimpose)
view.proceedAnimation("deselect for superimpose");
}
if (clustering.isClusterActive())
{
activeCluster = clustering.getCluster(clustering.getClusterActive().getSelected());
if (activeCluster.isSuperimposed() != superimpose)
c.add(activeCluster);
}
else
{
for (Cluster cluster : clustering.getClusters())
if (cluster.isSuperimposed() != superimpose)
c.add(cluster);
activeCluster = null;
}
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundWatched().clearSelection();
}
});
if (!superimpose && animateSuperimpose) // zoom out before spreading (explicitly fetch non-superimpose diameter)
{
if (!animateZoom)
view.suspendAnimation("zoom");
if (activeCluster == null)
view.zoomTo(clustering, AnimationSpeed.SLOW, false);
else
view.zoomTo(activeCluster, AnimationSpeed.SLOW, false);
if (!animateZoom)
view.proceedAnimation("zoom");
}
if (animateSuperimpose) // for superimposition or un-superimposition, hide shperes manually before moving compounds
for (Compound compound : clustering.getCompounds(true))
if (compound.isSphereVisible())
{
compound.setSphereVisible(false);
view.hideSphere(compound);
}
if (c.size() > 0)
{
if (!animateSuperimpose)
view.suspendAnimation("superimpose");
clustering.setClusterOverlap(c, superimpose, View.AnimationSpeed.SLOW);
if (!animateSuperimpose)
view.proceedAnimation("superimpose");
}
if (superimpose || !animateSuperimpose) // zoom in after superimposing
{
final Zoomable zoom = activeCluster == null ? clustering : activeCluster;
if (!animateZoom)
view.suspendAnimation("zoom");
view.zoomTo(zoom, AnimationSpeed.SLOW);
if (!animateZoom)
view.proceedAnimation("zoom");
}
if (setAntialiasBackOn && !view.isAntialiasOn())
{
ThreadUtil.sleep(200);
view.setAntialiasOn(true);
}
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
fireViewChange(PROPERTY_SUPERIMPOSE_CHANGED);
fireViewChange(PROPERTY_DENSITY_CHANGED);
}
});
}
List<PropertyChangeListener> viewListeners = new ArrayList<PropertyChangeListener>();
@Override
public void addViewListener(PropertyChangeListener l)
{
viewListeners.add(l);
}
private void fireViewChange(String prop)
{
SwingUtil.checkIsAWTEventThread();
for (PropertyChangeListener l : viewListeners)
l.propertyChange(new PropertyChangeEvent(this, prop, "old", "new"));
}
@Override
public boolean canJitter()
{
if (isSuperimpose())
return false;
if (clustering.getActiveCompounds().length == 1 && zoomToSingleSelectedCompound)
return false;
if (clustering.isClusterActive() && clustering.getActiveCluster().getNumCompounds() < 2)
return false;
else if (!clustering.isClusterActive() && clustering.getNumCompounds() < 2)
return false;
return true;
}
@Override
public boolean canChangeCompoundSize(boolean larger)
{
if (larger && ClusteringUtil.COMPOUND_SIZE == ClusteringUtil.COMPOUND_SIZE_MAX)
return false;
if (!larger && ClusteringUtil.COMPOUND_SIZE == 0)
return false;
Cluster activeCluster = clustering.getCluster(clustering.getClusterActive().getSelected());
if (activeCluster == null)
return true;
else
return !clustering.isSuperimposed();
}
@Override
public void changeCompoundSize(boolean larger)
{
if (larger && ClusteringUtil.COMPOUND_SIZE < ClusteringUtil.COMPOUND_SIZE_MAX)
{
ClusteringUtil.COMPOUND_SIZE++;
updateDensity(true);
}
else if (!larger && ClusteringUtil.COMPOUND_SIZE > 0)
{
ClusteringUtil.COMPOUND_SIZE--;
updateDensity(false);
}
}
@Override
public void setCompoundSize(int compoundSize)
{
if (ClusteringUtil.COMPOUND_SIZE != compoundSize)
{
boolean increased = ClusteringUtil.COMPOUND_SIZE < compoundSize;
ClusteringUtil.COMPOUND_SIZE = compoundSize;
updateDensity(increased);
}
}
@Override
public HighlightMode getHighlightMode()
{
return highlightMode;
}
@Override
public void setHighlightMode(HighlightMode mode)
{
if (highlightMode != mode)
{
highlightMode = mode;
if (selectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_MODE_CHANGED);
if (mode == HighlightMode.Spheres)
guiControler.showMessage("Highlight compound feature values with spheres.");
else if (mode == HighlightMode.ColorCompounds)
guiControler.showMessage("Highlight compound feature values by changing atom and bond colors.");
}
}
@Override
public boolean isHighlightLastFeatureEnabled()
{
return highlightLastFeatureEnabled;
}
@Override
public void setHighlightLastFeatureEnabled(boolean b)
{
if (highlightLastFeatureEnabled != b)
{
highlightLastFeatureEnabled = b;
if (highlightMode == HighlightMode.Spheres && selectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_LAST_FEATURE);
String lastMsg = ".";
if (highlightLastFeatureEnabled && lastSelectedHighlighter != null
&& lastSelectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
{
if (lastSelectedHighlighter == Highlighter.CLUSTER_HIGHLIGHTER)
lastMsg = " (flattened spheroid highlights cluster assignement).";
else if (lastSelectedHighlighter instanceof CompoundPropertyHighlighter)
lastMsg = " (flattened spheroid highlights '" + lastSelectedHighlighter + "').";
}
guiControler.showMessage((b ? "Enable" : "Disable") + " highlighting of last selected feature" + lastMsg);
}
}
@Override
public void setSphereSize(double size)
{
if (view.sphereSize != size)
{
boolean increase = view.sphereSize < size;
view.sphereSize = size;
if (highlightMode == HighlightMode.Spheres && selectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
updateAllClustersAndCompounds(true);
guiControler.showMessage((increase ? "Increase" : "Descrease") + " sphere size to "
+ StringUtil.formatDouble(size) + ".");
}
}
@Override
public void setSphereTranslucency(double translucency)
{
if (view.sphereTranslucency != translucency)
{
boolean increase = view.sphereTranslucency < translucency;
view.sphereTranslucency = translucency;
if (highlightMode == HighlightMode.Spheres && selectedHighlighter != Highlighter.DEFAULT_HIGHLIGHTER)
updateAllClustersAndCompounds(true);
guiControler.showMessage((increase ? "Increase" : "Descrease") + " sphere translucency to "
+ StringUtil.formatDouble(translucency) + ".");
}
}
@Override
public int getCompoundSizeMax()
{
return ClusteringUtil.COMPOUND_SIZE_MAX;
}
@Override
public int getCompoundSize()
{
return ClusteringUtil.COMPOUND_SIZE;
}
int jitteringLevel = 0;
@Override
public int getJitteringLevel()
{
return jitteringLevel;
}
private void resetJittering(HashSet<Compound> compounds, boolean skipPositionUpdate)
{
int idx = clustering.getJitteringResetLevel(compounds);
if (idx != -1)
setJitteringLevel(idx, compounds, true, skipPositionUpdate);
}
@Override
public void setJitteringLevel(int level)
{
JitteringProvider.showJitterWarning();
List<Compound> compounds;
if (clustering.isClusterActive())
compounds = clustering.getActiveCluster().getCompounds();
else
compounds = clustering.getCompounds(false);
setJitteringLevel(level, new HashSet<Compound>(compounds), false, false);
}
private void setJitteringLevel(final int level, final HashSet<Compound> compounds, final boolean force,
final boolean skipPositionUpdate)
{
if (jitteringLevel != level || force)
{
if (force)
SwingUtil.checkNoAWTEventThread();
else
SwingUtil.checkIsAWTEventThread();
if (!force)
guiControler.block("jitter");
Thread th = new Thread(new Runnable()
{
@Override
public void run()
{
try
{
final boolean inc = level > jitteringLevel;
if (jitteringLevel == level && force)
clustering.updateJittering(level, compounds);
else
{
if (inc)
{
while (jitteringLevel != level)
{
jitteringLevel++;
clustering.updateJittering(jitteringLevel, compounds);
}
}
else
{
jitteringLevel = level;
clustering.updateJittering(jitteringLevel, compounds);
}
}
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
Compound active[] = new Compound[0];
if (clustering.isClusterActive())
{
active = clustering.getActiveCompounds();
if (active.length > 0)
clustering.getCompoundActive().clearSelection();
}
if (!skipPositionUpdate)
clustering.updatePositions();
if (!force)
{
if (level == 0)
guiControler
.showMessage("Disable spreading (move compounds back to calculated positions).");
else
guiControler.showMessage((inc ? "Increase" : "Descrease") + " spread level to "
+ level + " (move close compounds apart).");
}
fireViewChange(PROPERTY_JITTERING_CHANGED);
if (!force)
{
view.suspendAnimation("jitter");
if (clustering.isClusterActive())
view.centerAt(clustering.getActiveCluster());
else
view.centerAt(clustering);
view.proceedAnimation("jitter");
}
for (Compound compound : clustering.getCompounds(true))
if (compound.isSphereVisible())
view.showSphere(compound, compound.isLastFeatureSphereVisible(), true);
if (active.length > 0)
{
if (active.length == 1) // do not zoom in
toggleCompoundActive(active[0], false);
else
setCompoundActive(active, false);
}
}
});
}
catch (Exception e)
{
Settings.LOGGER.error("jittering failed");
Settings.LOGGER.error(e);
}
finally
{
if (!force)
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
guiControler.unblock("jitter");
}
});
}
}
});
if (!force)
th.start();
else
th.run();
}
}
private void updateDensity(boolean increased)
{
Cluster activeCluster = clustering.getCluster(clustering.getClusterActive().getSelected());
if (activeCluster != null && clustering.isSuperimposed())
throw new IllegalStateException("does not make sense, because superimposed!");
Compound active[] = new Compound[0];
if (activeCluster != null)
{
active = clustering.getActiveCompounds();
if (active.length > 0)
clustering.getCompoundActive().clearSelection();
}
view.suspendAnimation("change density");
clustering.updatePositions();
if (activeCluster != null)
{
Settings.LOGGER.info("zooming out - cluster");
view.centerAt(activeCluster);
}
else
{
Settings.LOGGER.info("zooming out - home");
view.centerAt(clustering);
}
for (Compound compound : clustering.getCompounds(true))
if (compound.isSphereVisible())
view.showSphere(compound, compound.isLastFeatureSphereVisible(), true);
if (active.length > 0)
{
if (active.length == 1) // do not zoom in
toggleCompoundActive(active[0]);
else
setCompoundActive(active, false);
}
view.proceedAnimation("change density");
fireViewChange(PROPERTY_DENSITY_CHANGED);
guiControler.showMessage((increased ? "Increase" : "Descrease") + " compound size to "
+ ClusteringUtil.COMPOUND_SIZE + ".");
}
@Override
public DisguiseMode getDisguiseUnHovered()
{
return disguiseUnHovered;
}
@Override
public DisguiseMode getDisguiseUnZoomed()
{
return disguiseUnZoomed;
}
@Override
public void setDisguiseUnHovered(DisguiseMode disguiseUnHovered)
{
if (this.disguiseUnHovered != disguiseUnHovered)
{
if (disguiseUnHovered == DisguiseMode.invisible)
throw new IllegalArgumentException();
this.disguiseUnHovered = disguiseUnHovered;
updateAllClustersAndCompounds(false);
fireViewChange(PROPERTY_DISGUISE_CHANGED);
if (disguiseUnHovered == DisguiseMode.translucent)
guiControler
.showMessage("When a compound (or cluster) is selected via mouse-over, draw other compounds translucent.");
else
guiControler.showMessage("Ignore mouse-over selection, draw unselected compounds solid.");
}
}
@Override
public void setDisguiseUnZoomed(DisguiseMode disguiseUnZoomed)
{
if (this.disguiseUnZoomed != disguiseUnZoomed)
{
this.disguiseUnZoomed = disguiseUnZoomed;
updateAllClustersAndCompounds(false);
fireViewChange(PROPERTY_DISGUISE_CHANGED);
String msg = "When the view has zoomed in to a single compound, ";
if (disguiseUnZoomed == DisguiseMode.solid)
msg += "draw other compounds solid.";
else if (disguiseUnZoomed == DisguiseMode.translucent)
msg += "draw other compounds translucent.";
else if (disguiseUnZoomed == DisguiseMode.invisible)
msg += "do not draw other compounds.";
guiControler.showMessage(msg);
}
}
@Override
public boolean isHideHydrogens()
{
return hideHydrogens;
}
@Override
public void setHideHydrogens(boolean b)
{
this.hideHydrogens = b;
view.hideHydrogens(b);
if (b)
guiControler.showMessage("Hide hydrogens.");
else
guiControler.showMessage("Draw hydrogens (if available in the dataset file).");
}
@Override
public boolean isBlackgroundBlack()
{
return ComponentFactory.isBackgroundBlack();
}
@Override
public void setBackgroundBlack(boolean backgroundBlack)
{
setBackgroundBlack(backgroundBlack, false);
}
public void setBackgroundBlack(boolean backgroundBlack, boolean forceUpdate)
{
if (backgroundBlack != ComponentFactory.isBackgroundBlack() || forceUpdate)
{
guiControler.block("changing background");
try
{
ComponentFactory.setBackgroundBlack(backgroundBlack);
view.setBackground(ComponentFactory.BACKGROUND);
updateAllClustersAndCompounds(true); // force to assign new colors
fireViewChange(PROPERTY_BACKGROUND_CHANGED);
guiControler.showMessage("Background color set to " + (backgroundBlack ? "black." : "white."));
}
finally
{
guiControler.unblock("changing background");
}
}
}
@Override
public void setFontSize(int font)
{
if (font != ScreenSetup.INSTANCE.getFontSize())
{
boolean wasLarge = ScreenSetup.INSTANCE.isFontSizeLarge();
boolean increase = ScreenSetup.INSTANCE.getFontSize() < font;
ScreenSetup.INSTANCE.setFontSize(font);
if (wasLarge != ScreenSetup.INSTANCE.isFontSizeLarge())
updateAllClustersAndCompounds(true);
ComponentFactory.updateComponents();
fireViewChange(PROPERTY_FONT_SIZE_CHANGED);
guiControler.showMessage((increase ? "Increase" : "Descrease") + " font size to "
+ ScreenSetup.INSTANCE.getFontSize() + ".");
}
}
@Override
public int getFontSize()
{
return ScreenSetup.INSTANCE.getFontSize();
}
@Override
public void increaseFontSize(boolean increase)
{
setFontSize(ScreenSetup.INSTANCE.getFontSize() + (increase ? 1 : -1));
}
@SuppressWarnings("unchecked")
private void initCompoundDescriptor()
{
// if (compoundDescriptorProperty == COMPOUND_INDEX_PROPERTY)
//compoundDescriptorProperty = null;
// else if (!clustering.getFeatures().contains(compoundDescriptorProperty)
// && !clustering.getProperties().contains(compoundDescriptorProperty))
/**
* !!!!until compound-property rewrite the old integrated property cannot be retained!!!
*/
compoundDescriptorProperty = null;
if (compoundDescriptorProperty == null)
{
for (String names : new String[] { "(?i)^name$", "(?i).*name.*", "(?i)^id$", "(?i).*id.*", "(?i)^cas$",
"(?i).*cas.*", "(?i).*title.*" })
{
for (List<CompoundProperty> props : new List[] { clustering.getProperties(), clustering.getFeatures() })
{
for (CompoundProperty p : props)
{
// cond 1 : prop-name has to match
// cond 2 : distinct values are > 75% of the dataset size
// cond 3 : if its the id column, it has to either non-numeric or numeric & integer
if (p.toString().matches(names)
&& clustering.numDistinctValues(p) > (clustering.numCompounds() * 3 / 4.0)
&& (!names.equals("(?i)^id$") || !names.equals("(?i).*id.*")
|| !(p instanceof NumericProperty) || ((NumericProperty) p).isInteger()))
{
compoundDescriptorProperty = p;
break;
}
}
if (compoundDescriptorProperty != null)
break;
}
if (compoundDescriptorProperty != null)
break;
}
}
if (compoundDescriptorProperty == null)
compoundDescriptorProperty = COMPOUND_INDEX_PROPERTY;
for (Compound m : clustering.getCompounds(true))
m.setDescriptor(compoundDescriptorProperty);
}
@Override
public void setCompoundDescriptor(CompoundProperty prop)
{
if (compoundDescriptorProperty != prop)
{
compoundDescriptorProperty = prop;
for (Compound m : clustering.getCompounds(true))
m.setDescriptor(compoundDescriptorProperty);
fireViewChange(PROPERTY_COMPOUND_DESCRIPTOR_CHANGED);
guiControler.showMessage("Set compound identifier to feature value of '" + prop + "'.");
}
}
@Override
public CompoundProperty getCompoundDescriptor()
{
return compoundDescriptorProperty;
}
@Override
public void setHighlightColors(ColorGradient g, NumericProperty props[])
{
boolean fire = false;
for (NumericProperty p : props)
{
if (p == selectedHighlightCompoundProperty && (!g.equals(p.getHighlightColorGradient())))
fire = true;
p.setHighlightColorGradient(g);
}
if (fire)
{
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_COLORS_CHANGED);
guiControler.showMessage("Change color gradient for highlighting.");
}
}
@Override
public void setHighlightColors(Color[] g, NominalProperty[] props)
{
boolean fire = false;
for (NominalProperty p : props)
{
if (p == selectedHighlightCompoundProperty
&& (p.getHighlightColorSequence() == null || !ArrayUtil.equals(g, p.getHighlightColorSequence())))
fire = true;
p.setHighlightColorSequence(g);
}
if (fire)
{
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_COLORS_CHANGED);
guiControler.showMessage("Change color sequence for highlighting.");
}
}
@Override
public void setClusterColors(Color[] sequence)
{
if (!ArrayUtil.equals(CompoundPropertyUtil.CLUSTER_COLORS, sequence))
{
CompoundPropertyUtil.CLUSTER_COLORS = sequence;
if (getHighlighter() == Highlighter.CLUSTER_HIGHLIGHTER)
{
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_COLORS_CHANGED);
}
guiControler.showMessage("Change color sequence for cluster highlighting.");
}
}
@Override
public void setHighlightMatchColors(Color[] colors)
{
if (!ArrayUtil.equals(CompoundPropertyUtil.HIGHILIGHT_MATCH_COLORS, colors))
{
CompoundPropertyUtil.HIGHILIGHT_MATCH_COLORS = colors;
if (selectedHighlightCompoundProperty instanceof FragmentProperty)
{
updateAllClustersAndCompounds(true);
fireViewChange(PROPERTY_HIGHLIGHT_COLORS_CHANGED);
}
guiControler.showMessage("Change colors for SMARTS match highlighting.");
}
}
@Override
public FeatureFilter getFeatureFilter()
{
return featureFilter;
}
@Override
public void setFeatureFilter(FeatureFilter filter)
{
if (featureFilter != filter)
{
this.featureFilter = filter;
fireViewChange(PROPERTY_FEATURE_FILTER_CHANGED);
if (featureFilter == FeatureFilter.None)
guiControler.showMessage("Show all features in feature list.");
else if (featureFilter == FeatureFilter.NotSelectedForMapping)
guiControler.showMessage("Show only features that are NOT used by mapping in feature list.");
else if (featureFilter == FeatureFilter.SelectedForMapping)
guiControler.showMessage("Show only features that are used by mapping in feature list.");
else if (featureFilter == FeatureFilter.Filled)
guiControler.showMessage("Show only filled endpoint features (ends with '_filled').");
else if (featureFilter == FeatureFilter.Real)
guiControler.showMessage("Show only real endpoint features (ends with '_real').");
else if (featureFilter == FeatureFilter.Endpoints)
guiControler.showMessage("Show only endpoint features (does not end with '_real', but exists).");
}
}
@Override
public boolean isFeatureSortingEnabled()
{
return featureSortingEnabled;
}
@Override
public void setFeatureSortingEnabled(boolean b)
{
if (featureSortingEnabled != b)
{
featureSortingEnabled = b;
fireViewChange(PROPERTY_FEATURE_SORTING_CHANGED);
guiControler.showMessage((b ? "Enable" : "Disable") + " feature sorting according to specificity.");
}
}
public void showSortFilterDialog()
{
SortFilterDialog.showDialog(this, clustering);
}
List<Runnable> backgroundJobs = new ArrayList<Runnable>();
public void runInBackground(final Runnable r)
{
if (!guiControler.isVisible())
throw new IllegalStateException("do not animate if gui not visible!");
backgroundJobs.add(r);
guiControler.block("run " + r.hashCode());
Thread th = new Thread(new Runnable()
{
@Override
public void run()
{
r.run();
backgroundJobs.remove(r);
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
guiControler.unblock("run " + r.hashCode());
}
});
}
});
th.start();
}
public void waitForBackground()
{
SwingUtil.checkNoAWTEventThread();
while (backgroundJobs.size() > 0)
ThreadUtil.sleep(10);
}
// cluster controler
@Override
public void setClusterActive(final Cluster c, final boolean animate, boolean clearCompoundActive)
{
if (c.getNumCompounds() == 0)
throw new IllegalStateException("cluster size is null, remove filter before");
if (clearCompoundActive)
{
boolean anim = animate;
if (clustering.getActiveCluster() != null && clustering.getActiveCluster().getNumCompounds() == 1)
anim = false; // do not animate if there is only one compound in the cluster
clearCompoundActive(anim);
}
if (clustering.getNumClusters() == 1)
return;
// clearClusterWatched();
resetJittering(new HashSet<Compound>(c.getCompounds()), false);
if (!animate)
view.suspendAnimation("set cluster active");
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getClusterActive().setSelected(clustering.indexOf(c));
if (!animate)
view.proceedAnimation("set cluster active");
}
});
if (animate)
waitForBackground();
// view.waitForAnimation();
}
@Override
public void setClusterWatched(final Cluster c)
{
if (clustering.getNumClusters() == 1)
return;
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getClusterWatched().setSelected(clustering.indexOf(c));
}
});
}
@Override
public void setCompoundActive(Compound c, boolean animate)
{
setCompoundActive(new Compound[] { c }, animate);
}
@Override
public void setCompoundActive(Compound[] c, boolean animate)
{
if (getCompoundFilter() != null)
for (Compound comp : c)
if (!getCompoundFilter().accept(comp))
throw new IllegalStateException("compound is filtered out, remove filter before");
boolean zoomedToCluster = false;
Cluster clust = clustering.getUniqueClusterForCompounds(c);
if (clust == null && clustering.isClusterActive())
clearClusterActive(true, false);
else if (c.length == 1 && clust != null && clustering.getActiveCluster() != clust)
{
setClusterActive(clust, animate, true);
zoomedToCluster = true;
}
final boolean anim;
if (!animate)
anim = false;
else
{
if (zoomedToCluster && clustering.getActiveCluster().getNumCompounds() == 1)
anim = false; // do not animate if there is only one compound in the cluster
else
anim = true;
}
if (!anim)
view.suspendAnimation("set compound active");
final int idx[] = new int[c.length];
for (int i = 0; i < idx.length; i++)
idx[i] = c[i].getJmolIndex();
zoomToSingleSelectedCompound = true;
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundActive().setSelectedIndices(idx);
if (!anim)
view.proceedAnimation("set compound active");
}
});
if (anim)
waitForBackground(); //view.waitForAnimation();
}
@Override
public void toggleCompoundActive(final Compound c)
{
toggleCompoundActive(c, true);
}
private void toggleCompoundActive(final Compound c, boolean viaCtrlDown)
{
if (getCompoundFilter() != null)
if (!getCompoundFilter().accept(c))
throw new IllegalStateException("compound is filtered out, remove filter before");
if (viaCtrlDown)
ctrlHintCount = 0;
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
// clearCompoundWatched();
zoomToSingleSelectedCompound = false;
clustering.getCompoundActive().setSelectedInverted(c.getJmolIndex());
}
});
}
@Override
public void setCompoundWatched(Compound... c)
{
final int idx[] = new int[c.length];
for (int i = 0; i < idx.length; i++)
idx[i] = c[i].getJmolIndex();
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundWatched().setSelectedIndices(idx);
}
});
}
@Override
public void clearClusterActive(final boolean animate, boolean clearCompoundActive)
{
if (clearCompoundActive)
{
boolean anim = animate;
if (clustering.getActiveCluster() != null && clustering.getActiveCluster().getNumCompounds() == 1)
anim = false; // do not animate if there is only one compound in the cluster
clearCompoundActive(anim);
}
if (clustering.getNumClusters() == 1)
return;
clearClusterWatched();
resetJittering(new HashSet<Compound>(clustering.getCompounds(false)), false);
if (!animate)
view.suspendAnimation("clear cluster active");
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getClusterActive().clearSelection();
if (!animate)
view.proceedAnimation("clear cluster active");
}
});
if (animate)
waitForBackground();// view.waitForAnimation();
}
@Override
public void clearClusterWatched()
{
if (clustering.getNumClusters() == 1)
return;
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getClusterWatched().clearSelection();
}
});
}
@Override
public void clearCompoundActive(final boolean animate)
{
clearCompoundWatched();
if (!animate)
view.suspendAnimation("clear compound active");
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundActive().clearSelection();
if (!animate)
view.proceedAnimation("clear compound active");
}
});
if (animate)
waitForBackground();// view.waitForAnimation();
}
@Override
public void clearCompoundWatched()
{
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.getCompoundWatched().clearSelection();
}
});
}
@Override
public CompoundFilter getCompoundFilter()
{
return compoundFilter;
}
@Override
public void applyCompoundFilter(final List<Compound> compounds, final boolean accept)
{
if (compounds.size() == clustering.numCompounds())
{
JOptionPane.showMessageDialog(Settings.TOP_LEVEL_FRAME, "Cannot hide all compounds of the dataset.");
return;
}
Thread th = new Thread(new Runnable()
{
public void run()
{
List<Compound> c;
if (accept)
c = compounds;
else
{
c = new ArrayList<Compound>();
for (Compound compound : clustering.getCompounds(true))
if (!compounds.contains(compound))
c.add(compound);
}
CompoundFilter compoundFilter = new CompoundFilterImpl(clustering, c, null);
setCompoundFilter(compoundFilter, true);
}
});
th.start();
}
@Override
public void setCompoundFilter(final CompoundFilter filter, final boolean animate)
{
if (filter == null && compoundFilter == null || filter == compoundFilter)
return;
clearClusterActive(animate, true);
if (!animate)
view.suspendAnimation("change compound filter");
final CompoundProperty p = selectedHighlightCompoundProperty;
if (selectedHighlightCompoundProperty != null) // otherwise spheres will screw up
selectedHighlightCompoundProperty = null;
final CompoundFilter f;
if (compoundFilter != null && filter != null)
f = CompoundFilterImpl.combine(clustering, filter, compoundFilter);
else
f = filter;
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
guiControler.block("update filter");
compoundFilter = f;
clustering.setCompoundFilter(null);
updateAllClustersAndCompounds(f == null); // force to get cluster-colors right
}
});
if (filter == null) // has to be done outside of awt thread
resetJittering(new HashSet<Compound>(clustering.getCompounds(false)), true);
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.updatePositions(); // update cluster position stuff after compounds are visible again
if (f != null)
{
clustering.setCompoundFilter(f);
updateAllClustersAndCompounds(true);
clustering.updatePositions(); // update cluster position stuff after compounds are visible again
}
guiControler.showMessage((f != null ? "Enable" : "Disable") + " compound filter.");
Runnable r = new Runnable()
{
@Override
public void run()
{
if (clustering.isClusterActive())
view.zoomTo(clustering.getCluster(clustering.getClusterActive().getSelected()),
AnimationSpeed.SLOW);
else
view.zoomTo(clustering, AnimationSpeed.SLOW);
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
if (p != null)
{
selectedHighlightCompoundProperty = p;
updateAllClustersAndCompounds(false);
}
fireViewChange(PROPERTY_COMPOUND_FILTER_CHANGED);
}
});
}
};
if (animate)
runInBackground(r);
else
{
SwingUtil.invokeAndWait(r);
view.proceedAnimation("change compound filter");
}
guiControler.unblock("update filter");
}
});
if (animate)
waitForBackground();
}
@Override
public void useSelectedCompoundsAsFilter(boolean animate)
{
List<Compound> c = new ArrayList<Compound>();
for (int i : clustering.getCompoundActive().getSelectedIndices())
c.add(clustering.getCompoundWithJmolIndex(i));
CompoundFilter compoundFilter = new CompoundFilterImpl(clustering, c, null);
setCompoundFilter(compoundFilter, animate);
}
private CompoundFilter compoundFilter = null;
private boolean zoomToSingleSelectedCompound = true;
// ------------------------------------------
@Override
public void newClustering()
{
guiControler.block("new clustering");
Thread noAWTThread = new Thread(new Runnable()
{
public void run()
{
try
{
JFrame top = Settings.TOP_LEVEL_FRAME;
CheSMapperWizard wwd = null;
while (wwd == null || wwd.getReturnValue() == CheSMapperWizard.RETURN_VALUE_IMPORT)
{
wwd = new CheSMapperWizard(top, 0);
wwd.setCloseButtonText("Cancel");
Settings.TOP_LEVEL_FRAME = top;
SwingUtil.waitWhileVisible(wwd);
}
if (wwd.getReturnValue() == CheSMapperWizard.RETURN_VALUE_FINISH)
{
guiControler.blockMessages();
clearClusterActive(true, true);
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
clustering.clear();
compoundFilter = null;
}
});
final Task task = TaskProvider.initTask("Chemical space mapping of "
+ wwd.getChesMapping().getDatasetFile().getName());
final TaskDialog taskDialog = new TaskDialog(task, Settings.TOP_LEVEL_FRAME);
final ClusteringData d = wwd.getChesMapping().doMapping();
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
if (d != null)
{
d.setCheSMappingWarningOwner(taskDialog);
guiControler.unblockMessages();
clustering.newClustering(d);
if (featureSortingEnabled)
clustering.initFeatureNormalization();
task.finish();
}
TaskProvider.removeTask();
}
});
}
}
finally
{
SwingUtil.invokeAndWait(new Runnable()
{
@Override
public void run()
{
guiControler.unblockMessages();
guiControler.unblock("new clustering");
}
});
}
}
});
noAWTThread.start();
}
@Override
public void chooseClustersToFilter()
{
int[] indices = clustering.clusterChooser("Hide Cluster/s",
"Select the clusters you want to temporarily remove (the original dataset is not modified).");
if (indices == null)
return;
List<Compound> l = new ArrayList<Compound>();
for (int i = 0; i < indices.length; i++)
for (Compound compound : clustering.getCluster(indices[i]).getCompounds())
l.add(compound);
applyCompoundFilter(l, false);
}
@Override
public void chooseCompoundsToFilter()
{
int[] indices = clustering.selectJmolIndicesWithCompoundChooser("Hide Compounds/s",
"Select the compounds you want to temporarily remove (the original dataset is not modified).");
if (indices == null)
return;
List<Compound> l = new ArrayList<Compound>();
for (int i = 0; i < indices.length; i++)
l.add(clustering.getCompoundWithJmolIndex(indices[i]));
applyCompoundFilter(l, false);
}
@Override
public void chooseClustersToRemove()
{
int[] indices = clustering.clusterChooser("Remove Cluster/s",
"Select the clusters you want to remove (the original dataset is not modified).");
if (indices != null)
{
if (indices.length == clustering.numClusters())
{
JOptionPane.showMessageDialog(Settings.TOP_LEVEL_FRAME, "Cannot remove all clusters from the dataset.");
return;
}
Cluster c2[] = new Cluster[indices.length];
for (int i = 0; i < indices.length; i++)
c2[i] = clustering.getCluster(indices[i]);
clearClusterActive(false, false);
clustering.removeCluster(c2);
}
}
@Override
public void chooseCompoundsToRemove()
{
int[] indices = clustering.selectJmolIndicesWithCompoundChooser("Remove Compounds/s",
"Select the compounds you want to remove (the original dataset is not modified).");
if (indices == null)
return;
if (indices.length == clustering.numCompounds())
{
JOptionPane.showMessageDialog(Settings.TOP_LEVEL_FRAME, "Cannot remove all compounds from the dataset.");
return;
}
clearClusterActive(false, false);
clustering.removeCompoundsWithJmolIndices(indices);
}
@Override
public void removeCluster(final Cluster... c)
{
if (c.length == clustering.numClusters())
{
JOptionPane.showMessageDialog(Settings.TOP_LEVEL_FRAME, "Cannot remove all clusters from the dataset.");
return;
}
clearClusterActive(true, true);
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
clustering.removeCluster(c);
}
});
}
@Override
public void removeCompounds(final Compound[] c)
{
if (c.length == clustering.numCompounds())
{
JOptionPane.showMessageDialog(Settings.TOP_LEVEL_FRAME, "Cannot remove all compounds from the dataset.");
return;
}
clearClusterActive(true, true);
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
clustering.removeCompoundsWithJmolIndices(clustering.getJmolIndicesWithCompounds(c));
}
});
}
@Override
public boolean isShowClusteringPropsEnabled()
{
return true;
}
@Override
public void resetView()
{
guiControler.block("zooming home");
Thread th = new Thread(new Runnable()
{
public void run()
{
if (clustering.getNumClusters() > 1 && clustering.getActiveCluster() != null)
clearClusterActive(true, true);
else if (clustering.getActiveCompound() != null)
clearCompoundActive(true);
else
view.zoomTo(clustering, AnimationSpeed.SLOW);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
if (clustering.getNumClusters() > 1)
{
setHighlighter(Highlighter.CLUSTER_HIGHLIGHTER);
highlightAutomatic.init();
}
else
setHighlighter(Highlighter.DEFAULT_HIGHLIGHTER);
// else
// guiControler.setFullScreen(!guiControler.isFullScreen());
guiControler.unblock("zooming home");
}
});
}
});
th.start();
}
}