package ca.bcgsc.abyssexplorer.gui; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.event.ItemListener; import java.awt.geom.Ellipse2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import javax.swing.event.ChangeListener; import org.apache.commons.collections15.Transformer; import ca.bcgsc.abyssexplorer.graph.AbyssGraph; import ca.bcgsc.abyssexplorer.graph.ContigLabel; import ca.bcgsc.abyssexplorer.graph.DistanceEstimate; import ca.bcgsc.abyssexplorer.graph.Edge; import ca.bcgsc.abyssexplorer.graph.Vertex; import ca.bcgsc.abyssexplorer.visualization.control.AbyssGraphMouse; import ca.bcgsc.abyssexplorer.visualization.decorators.AbyssEdgeShape; import ca.bcgsc.abyssexplorer.visualization.picking.AbyssShapePickSupport; import ca.bcgsc.abyssexplorer.visualization.picking.PairedEndPathState; import ca.bcgsc.abyssexplorer.visualization.picking.PartnerEdgeState; import ca.bcgsc.abyssexplorer.visualization.renderers.AbyssEdgeRenderer; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.decorators.EdgeShape; import edu.uci.ics.jung.visualization.layout.PersistentLayoutImpl; import edu.uci.ics.jung.visualization.renderers.DefaultEdgeLabelRenderer; import edu.uci.ics.jung.visualization.renderers.Renderer; /** * Extends edu.uci.ics.jung.visualization.VisualizationViewer * (authors: Joshua O'Madadhain, Tom Nelson, Danyel Fisher). * * Controls the ABySS-Explorer graph display panel (maintains * the graph layout and graph selection states). * * @author Cydney Nielsen * */ public class AbyssVisualizationViewer extends VisualizationViewer { AbyssGraph g; // parsed input data /** * holds the paired end partner edges for the currently * selected (picked) edge */ protected PartnerEdgeState partnerEdgeState; /** * holds the paired end contigs for which the currently * selected (picked) edge is a member */ protected PairedEndPathState pairedEndPathState; public AbyssVisualizationViewer(PersistentLayoutImpl layout) { super(layout); setGraphMouse(new AbyssGraphMouse()); partnerEdgeState = new PartnerEdgeState(); pairedEndPathState = new PairedEndPathState(); setBackground(Color.WHITE); setTransformers(); setRenderers(); setPickSupport(); } public void activatePartnerDisplay() { partnerEdgeState.setDisplayActive(true); } public void activatePePathDisplay() { pairedEndPathState.setDisplayActive(true); } public void deactivatePartnerDisplay() { partnerEdgeState.setDisplayActive(false); } public void deactivatePePathDisplay() { pairedEndPathState.setDisplayActive(false); } public void setGraph(AbyssGraph g) { this.g = g; } public AbyssGraph getGraph() { return g; } public Graph getDisplayedGraph() { return getGraphLayout().getGraph(); } public PairedEndPathState getPairedEndPathState() { return pairedEndPathState; } public PartnerEdgeState getPartnerEdgePickState() { return partnerEdgeState; } public List getPossiblePathLabels() { return pairedEndPathState.getPossibleLabels(); } public List getInboundPartnerLabels() { List iLabels = null; List iDist = partnerEdgeState.getInbound(); if (iDist != null) { iLabels = new ArrayList(iDist.size()); for (DistanceEstimate d: iDist) { iLabels.add(d.toString()); } } return iLabels; } public List getOutboundPartnerLabels() { List oLabels = null; List oDist = partnerEdgeState.getOutbound(); if (oDist != null) { oLabels = new ArrayList(oDist.size()); for (DistanceEstimate d: oDist) { oLabels.add(d.toString()); } } return oLabels; } public Edge getInHighlightedPartner() { return partnerEdgeState.getInHighlighted(); } public Edge getOutHighlightedPartner() { return partnerEdgeState.getOutHighlighted(); } public Edge getSelectedEdge() { Edge sEdge = null; Set s = pickedEdgeState.getPicked(); if (s != null) { if (s.size() > 1) { throw new IllegalArgumentException("Only one edge can be selected at a time (found " + s.size() + ")"); } else if (s.size() == 1) { sEdge = (Edge) s.toArray()[0]; } } return sEdge; } public String getSelectedEdgeString() { Edge sEdge = getSelectedEdge(); if (sEdge == null) { return null; } String s = "Single-end contig id: ,"; s += sEdge.getLabel() + " ,"; Integer sLen = sEdge.getLen(); Integer sCov = sEdge.getCoverage(); if (sLen != null && sCov != null) { s += "(" + sLen + " bp; " + sCov + " kmer cov)"; } else if (sLen != null) { s += "(" + sLen + " bp)"; } else if (sCov != null) { s += "(" + sCov + " kmer cov)"; } s += "\n"; return s; } public String getSelectedEdgeFormats() { String formats = "bold,selected,regular"; return formats; } public String getSelectedPathLabel() { String sLabel = null; ContigLabel s = pairedEndPathState.getSelectedLabel(); if (s != null) { sLabel = s.getLabel(); } return sLabel; } public String getSelectedPathString() { String path = "Paired-end contig id: ,"; path += pairedEndPathState.getSelectedLabel().getLabel() + "\n,"; path += "Single-end contig members: ,"; for (Edge edge: pairedEndPathState.getSelectedMembers()) { path += edge.getLabel() + " ,"; } return path; } public String getSelectedPathStringFormats() { String formats = "bold,regular,bold,"; for (Edge e: pairedEndPathState.getSelectedMembers()) { if (pickedEdgeState.isPicked(e)) { formats += "selected,"; } else if (!getDisplayedGraph().containsEdge(e)){ formats += "missing,"; } else { formats += "regular,"; } } return formats; } public void clear() { clearEdgeState(); pickedVertexState.clear(); } public void clearEdgeState() { pickedEdgeState.clear(); partnerEdgeState.clear(); pairedEndPathState.clear(); setToolTipText(null); } /** * Select the input Edge. Includes selecting * input edge's paired-end partner edges and * possible paired-end contigs (paths). * * @param edge */ public void selectEdge(Edge edge) { clearEdgeState(); setToolTipText(null); pickedEdgeState.pick(edge, true); selectPath(edge); selectPartners(edge); } /** * Select the input paired-end contig (path). Includes * selecting the paired-end partner edges of the path's * terminal edges. * * @param peLabel */ public void selectQueryPath(ContigLabel peLabel) { clear(); // select partners of the terminal edges /*List members = g.getPairedEndContigMembers(peLabel); Edge firstEdge = members.get(0); Edge lastEdge = members.get(members.size()-1); System.out.println("first label: " + firstEdge.getLabel() + " last label: " + lastEdge.getLabel()); System.out.println("selecting partners for first edge: " + firstEdge.getLabel()); selectPartners(firstEdge); System.out.println("selecting partners for last edge: " + lastEdge.getLabel()); addPartnersToSelection(lastEdge);*/ pairedEndPathState.setPossibleLabels(peLabel); selectPath(peLabel); } public void selectVertex(Vertex vertex) { if (pickedVertexState.isPicked(vertex) == false) { pickedVertexState.clear(); pickedVertexState.pick(vertex, true); } } public void selectPartners(Edge edge) { setToolTipText(null); partnerEdgeState.clear(); addPartnersToSelection(edge); } public void addPartnersToSelection(Edge edge) { List in = edge.getInboundPartners(); if (in != null) { // ensure graph has up-to-date edge orientations for (DistanceEstimate iDis: in) { g.getEdge(iDis.getLabelObject()); } partnerEdgeState.pick(in, "in"); } List out = edge.getOutboundPartners(); if (out != null) { // ensure graph has up-to-date edge orientations for (DistanceEstimate oDis: out) { g.getEdge(oDis.getLabelObject()); } partnerEdgeState.pick(out, "out"); } } /** * Selects the first of all paired-end paths * for which the input single-end contig is * a member. Respects the strand of the input * edge. * * @param edge */ public void selectPath(Edge edge) { List peLabels = edge.getPairedEndLabels(); if (peLabels == null) { return; } // determine if this edge is on the currently selected path ContigLabel peSelected = pairedEndPathState.getSelectedLabel(); ContigLabel peToSelect = null; if (peSelected != null) { for (ContigLabel possiblePe: peLabels) { if (peSelected.getId() == possiblePe.getId()) { if (peSelected.getStrand() != possiblePe.getStrand()) { // correct the orientation if needed if (peSelected.getStrand() == 0) { peToSelect = new ContigLabel(peSelected.getId(), (byte) 1); } else { peToSelect = new ContigLabel(peSelected.getId(), (byte) 0); } } else { peToSelect = peSelected; } break; } } } if (peToSelect == null) { if (peToSelect == null) { // select the first possible pe Collections.sort(peLabels); peToSelect = peLabels.get(0); } } pairedEndPathState.setPossibleLabels(peLabels); selectPath(peToSelect); } public void selectPath(String s) { selectPath(new ContigLabel(s)); } /** * Select all single-end contigs that are members * of the input paired-end contig considering the * input strand of the paired-end contig. * * @param peId */ public void selectPath(ContigLabel peLabel) { List members = g.getPairedEndContigMembers(peLabel); pairedEndPathState.setSelection(peLabel, members); } public boolean isEdgeSelected() { if (pickedEdgeState.getSelectedObjects().length == 0) { return false; } else { return true; } } public boolean isPairedEndSelected() { boolean selected = false; if (pairedEndPathState.getNumSelectedMembers() != 0) { selected = true; } return selected; } public void reverseComplementSelection(Edge edge) { edge.reverseComplement(); selectPath(edge); selectPartners(edge); } /** * Overrides some of the default Transformers in JUNG's * edu.uci.ics.jung.visualization.PluggableRenderContex */ protected void setTransformers() { setVertexColorTransformer(); setVertexShapeTransformer(); setEdgeColorTransformer(); setEdgeThicknessTransformer(); turnContigLabelsOn(); setEdgeLabelColor(); setEdgeFontTransformer(); turnContigLenOn(); } protected void setVertexColorTransformer() { Transformer vertexPaint = new Transformer() { public Paint transform(Vertex v) { if (getPickedVertexState().isPicked(v)) { return Color.darkGray; } else if (missingEdges(v)) { return Color.white; } else { return Color.lightGray; } } }; Transformer vertexEdgePaint = new Transformer() { public Paint transform(Vertex v) { if (getPickedVertexState().isPicked(v)) { return Color.darkGray; } else if (missingEdges(v)) { return new Color(150,150,150); } else { return Color.lightGray; } } }; getRenderContext().setVertexFillPaintTransformer(vertexPaint); getRenderContext().setVertexDrawPaintTransformer(vertexEdgePaint); } protected boolean missingEdges(Vertex v) { boolean missing = false; Graph dGraph = getDisplayedGraph(); for (Edge oEdge: v.getOutgoing()) { if (!dGraph.containsEdge(oEdge)) { missing = true; } } for (Edge iEdge: v.getIncoming()) { if (!dGraph.containsEdge(iEdge)) { missing = true; } } return missing; } protected void setVertexShapeTransformer() { Transformer vertexShape = new Transformer() { final int VERTEX_HEIGHT = 25; final int VERTEX_WIDTH = 10; final int VERTEX_POINT = 10; public Shape transform(Vertex v) { if (v.hasConsistentPoles()) { return new Ellipse2D.Float(-VERTEX_POINT / 2, -VERTEX_POINT / 2, VERTEX_POINT, VERTEX_POINT); } else { return new Ellipse2D.Float(-VERTEX_WIDTH / 2, -VERTEX_HEIGHT / 2, VERTEX_WIDTH, VERTEX_HEIGHT); } } }; getRenderContext().setVertexShapeTransformer(vertexShape); } protected void setEdgeColorTransformer() { Transformer edgePaint = new Transformer() { public Paint transform(Edge e) { if (pickedEdgeState.isPicked(e)) { // current selection is medium orange (takes priority) return new Color(236, 112, 20); } else if (partnerEdgeState.isHighlighted(e) && partnerEdgeState.isDisplayActive()) { // use same medium orange as for selection return new Color(236, 112, 20); } else if (pairedEndPathState.isPicked(e) && pairedEndPathState.isDisplayActive()) { // Paired-end path takes priority over partner distance estimates float index = ((Integer) pairedEndPathState.getPosition(e)).floatValue(); float rIndex = index/pairedEndPathState.getNumSelectedMembers(); return getBlue(rIndex); } else if (partnerEdgeState.isInbound(e) && partnerEdgeState.isDisplayActive()) { // inbound are light orange return new Color(254, 196, 79); } else if (partnerEdgeState.isOutbound(e) && partnerEdgeState.isDisplayActive()) { // outbound are dark orange return new Color(153, 52, 4); } else { return Color.gray; } } }; getRenderContext().setEdgeDrawPaintTransformer(edgePaint); getRenderContext().setArrowDrawPaintTransformer(edgePaint); getRenderContext().setArrowFillPaintTransformer(edgePaint); } protected Color getBlue(float percent) { // float rDark = 44; float gDark = 127; float bDark = 184; float rDark = 8; float gDark = 81; float bDark = 156; float rLight = 158; float gLight = 202; float bLight = 225; float rNew = (rLight - (percent * (rLight - rDark)))/255; float gNew = (gLight - (percent * (gLight - gDark)))/255; float bNew = (bLight - (percent * (bLight - bDark)))/255; return new Color(rNew, gNew, bNew); } protected void setEdgeThicknessTransformer() { Transformer edgeThickness = new Transformer() { public Stroke transform(Edge e) { if (pickedEdgeState.isPicked(e)) { return new BasicStroke(4); } else if (pairedEndPathState.isPicked(e) && pairedEndPathState.isDisplayActive()) { return new BasicStroke(4); } else if ((partnerEdgeState.isInbound(e) || partnerEdgeState.isOutbound(e)) && partnerEdgeState.isDisplayActive()) { return new BasicStroke(4); } else { return new BasicStroke(2); } } }; Transformer arrowThickness = new Transformer() { public Stroke transform(Edge e) { return new BasicStroke(2); } }; getRenderContext().setEdgeStrokeTransformer(edgeThickness); // getRenderContext().setEdgeArrowStrokeTransformer(edgeThickness); getRenderContext().setEdgeArrowStrokeTransformer(arrowThickness); } protected void setEdgeLabelColor() { getRenderContext().setEdgeLabelRenderer(new DefaultEdgeLabelRenderer(Color.black)); } protected void setEdgeFontTransformer() { Transformer edgeFont = new Transformer() { public Font transform(Edge e) { return new Font("Helvetica", Font.PLAIN, 14); } }; getRenderContext().setEdgeFontTransformer(edgeFont); } public void setLenScale(int s) { if (getRenderContext().getEdgeShapeTransformer() instanceof AbyssEdgeShape.Squiggle) { ((AbyssEdgeShape.Squiggle) getRenderContext().getEdgeShapeTransformer()).setLenScale(s); } } public void setInHighlightedPartner(String label) { Edge e = null; if (label != null) { e = getGraph().getEdge(label); } partnerEdgeState.setInHighlighted(e); } public void setOutHighlightedPartner(String label) { Edge e = null; if (label != null) { e = getGraph().getEdge(label); } partnerEdgeState.setOutHighlighted(e); } protected void setRenderers() { getRenderer().getVertexLabelRenderer().setPosition( Renderer.VertexLabel.Position.CNTR); getRenderer().setEdgeRenderer(new AbyssEdgeRenderer()); } public void turnContigLenOn() { AbyssEdgeShape.Squiggle edgeShapeTransformer = new AbyssEdgeShape.Squiggle(); edgeShapeTransformer.setControlOffsetIncrement(200.0f); getRenderContext().setEdgeShapeTransformer(edgeShapeTransformer); } public void turnContigLenOff() { EdgeShape.QuadCurve edgeShapeTransformer = new EdgeShape.QuadCurve(); getRenderContext().setEdgeShapeTransformer(edgeShapeTransformer); } public void turnContigLabelsOn() { Transformer edgeLabel = new Transformer() { public String transform(Edge e) { return e.getLabel(); } }; getRenderContext().setEdgeLabelTransformer(edgeLabel); } public void turnContigLabelsOff() { Transformer edgeLabel = new Transformer() { public String transform(Edge e) { return ""; } }; getRenderContext().setEdgeLabelTransformer(edgeLabel); } /** * Ensures correct shapes are returned for selected items */ protected void setPickSupport() { getRenderContext().setPickSupport( new AbyssShapePickSupport(this)); } public void addModelChangeListener(ChangeListener c) { getModel().addChangeListener(c); } public void addGraphMouseChangeListener(ChangeListener c) { ((AbyssGraphMouse) getGraphMouse()).addChangeListener(c); } public void addEdgeItemListener(ItemListener l) { pickedEdgeState.addItemListener(l); } public void addPartnerChangeListener(ChangeListener c) { partnerEdgeState.addChangeListener(c); } public void addPathChangeListener(ChangeListener c) { pairedEndPathState.addChangeListener(c); } public void addPartnerEdgeChangeListener(ChangeListener c) { partnerEdgeState.addChangeListener(c); } /** * */ private static final long serialVersionUID = 484495586168643969L; }