package uk.ac.rhul.cs.cl1.ui.cytoscape3;
import java.awt.Color;
import java.awt.Paint;
import java.util.List;
import java.util.Set;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.view.presentation.property.BasicVisualLexicon;
import org.cytoscape.view.presentation.property.NodeShapeVisualProperty;
import org.cytoscape.view.presentation.property.values.NodeShape;
import org.cytoscape.view.vizmap.VisualMappingFunctionFactory;
import org.cytoscape.view.vizmap.VisualMappingManager;
import org.cytoscape.view.vizmap.VisualStyle;
import org.cytoscape.view.vizmap.VisualStyleFactory;
import org.cytoscape.view.vizmap.mappings.BoundaryRangeValues;
import org.cytoscape.view.vizmap.mappings.ContinuousMapping;
import org.cytoscape.view.vizmap.mappings.DiscreteMapping;
/**
* Class responsible for registering and maintaining ClusterONE related
* visual styles in Cytoscape.
*
* @author ntamas
*/
public class VisualStyleManager {
/**
* VizMapper visual style name for coloring nodes by their status
*/
public static final String VISUAL_STYLE_BY_STATUS = "ClusterONE - Status";
/**
* VizMapper visual style name for coloring nodes by their status
*/
public static final String VISUAL_STYLE_BY_AFFINITY = "ClusterONE - Affinity";
/**
* The ClusterONE plugin app in which this visual style manager lives.
*/
private ClusterONECytoscapeApp app;
/**
* Mapping from affinity scores to colors in the affinity visual style.
*/
private ContinuousMapping<Double,Paint> affinityColorMapping;
/**
* The visual style for coloring nodes by their affinity.
*/
private VisualStyle colorNodesByAffinityVisualStyle;
/**
* The visual style for coloring nodes by their status.
*/
private VisualStyle colorNodesByStatusVisualStyle;
/**
* Constructor.
*/
public VisualStyleManager(ClusterONECytoscapeApp app) {
this.app = app;
}
/**
* Ensure that the ClusterONE VizMapper styles are registered in the given network
*/
public void ensureVizMapperStylesRegistered() {
VisualMappingManager visualMappingManager =
app.getService(VisualMappingManager.class);
if (visualMappingManager == null)
return; /* no visual mapping manager, we cannot do anything */
ensureVisualStyleRegistered(visualMappingManager, getColorNodesByStatusVisualStyle());
ensureVisualStyleRegistered(visualMappingManager, getColorNodesByAffinityVisualStyle());
}
/**
* Ensure that the given visual style is registered in the given manager.
*
* @param manager the {@link VisualMappingManager} where the visual style
* has to be registered
* @param style the style to register
*/
private void ensureVisualStyleRegistered(VisualMappingManager manager, VisualStyle style) {
if (style == null)
return;
Set<VisualStyle> styles = manager.getAllVisualStyles();
if (styles.contains(style))
return;
manager.addVisualStyle(style);
}
/**
* Returns the visual style that colors nodes by their status in the clustering.
*/
public VisualStyle getColorNodesByStatusVisualStyle() {
if (colorNodesByStatusVisualStyle == null) {
VisualStyleFactory factory = app.getService(VisualStyleFactory.class);
VisualMappingFunctionFactory discreteMappingFactory = app.getService(
VisualMappingFunctionFactory.class, "(mapping.type=discrete)");
if (factory != null && discreteMappingFactory != null) {
colorNodesByStatusVisualStyle = factory.createVisualStyle(VISUAL_STYLE_BY_STATUS);
DiscreteMapping<String,Paint> colorMapping = (DiscreteMapping<String,Paint>)
discreteMappingFactory.createVisualMappingFunction(
ClusterONECytoscapeApp.ATTRIBUTE_STATUS, String.class,
BasicVisualLexicon.NODE_FILL_COLOR);
colorMapping.putMapValue("Outlier", Color.LIGHT_GRAY);
colorMapping.putMapValue("Cluster", Color.RED);
colorMapping.putMapValue("Overlap", Color.ORANGE);
colorNodesByStatusVisualStyle.addVisualMappingFunction(colorMapping);
DiscreteMapping<String,NodeShape> shapeMapping = (DiscreteMapping<String,NodeShape>)
discreteMappingFactory.createVisualMappingFunction(
ClusterONECytoscapeApp.ATTRIBUTE_STATUS, String.class,
BasicVisualLexicon.NODE_SHAPE);
shapeMapping.putMapValue("Outlier", NodeShapeVisualProperty.ELLIPSE);
shapeMapping.putMapValue("Cluster", NodeShapeVisualProperty.RECTANGLE);
shapeMapping.putMapValue("Overlap", NodeShapeVisualProperty.DIAMOND);
colorNodesByStatusVisualStyle.addVisualMappingFunction(shapeMapping);
}
}
return colorNodesByStatusVisualStyle;
}
/**
* Returns the visual style that colors nodes by their affinity scores.
*/
public VisualStyle getColorNodesByAffinityVisualStyle() {
if (colorNodesByAffinityVisualStyle == null) {
VisualStyleFactory factory = app.getService(VisualStyleFactory.class);
VisualMappingFunctionFactory discreteMappingFactory = app.getService(
VisualMappingFunctionFactory.class, "(mapping.type=discrete)");
VisualMappingFunctionFactory continuousMappingFactory = app.getService(
VisualMappingFunctionFactory.class, "(mapping.type=continuous)");
if (factory != null && discreteMappingFactory != null && continuousMappingFactory != null) {
colorNodesByAffinityVisualStyle = factory.createVisualStyle(VISUAL_STYLE_BY_AFFINITY);
affinityColorMapping = (ContinuousMapping<Double,Paint>)
continuousMappingFactory.createVisualMappingFunction(
ClusterONECytoscapeApp.ATTRIBUTE_AFFINITY, Double.class,
BasicVisualLexicon.NODE_FILL_COLOR);
updateAffinityStyleRange(null);
colorNodesByAffinityVisualStyle.addVisualMappingFunction(affinityColorMapping);
DiscreteMapping<String,NodeShape> shapeMapping = (DiscreteMapping<String,NodeShape>)
discreteMappingFactory.createVisualMappingFunction(
ClusterONECytoscapeApp.ATTRIBUTE_STATUS, String.class,
BasicVisualLexicon.NODE_SHAPE);
shapeMapping.putMapValue("Outlier", NodeShapeVisualProperty.ELLIPSE);
shapeMapping.putMapValue("Cluster", NodeShapeVisualProperty.RECTANGLE);
shapeMapping.putMapValue("Overlap", NodeShapeVisualProperty.DIAMOND);
colorNodesByAffinityVisualStyle.addVisualMappingFunction(shapeMapping);
}
}
return colorNodesByAffinityVisualStyle;
}
/**
* Updates the continuous mapping used to colour the vertices by their affinities to
* reflect the new range of affinity values after some of the attribute values changed.
*
* @param network the network to evaluate to find out the desirable range
*/
public void updateAffinityStyleRange(CyNetwork network) {
double range = 0.1;
final Color minColor = Color.blue;
final Color midColor = Color.white;
final Color maxColor = Color.red;
if (network != null) {
List<Double> scores = network.getDefaultNodeTable().getColumn(
ClusterONECytoscapeApp.ATTRIBUTE_AFFINITY).getValues(Double.class);
for (Double score: scores) {
if (Math.abs(score) > range)
range = Math.abs(score);
}
}
int n = affinityColorMapping.getPointCount();
while (n > 0) {
n--;
affinityColorMapping.removePoint(n);
}
affinityColorMapping.addPoint(-range,
new BoundaryRangeValues<Paint>(minColor, minColor, minColor));
affinityColorMapping.addPoint(0.0,
new BoundaryRangeValues<Paint>(midColor, midColor, midColor));
affinityColorMapping.addPoint(range,
new BoundaryRangeValues<Paint>(maxColor, maxColor, maxColor));
}
}