001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.bio.gui.sequence; 022 023import java.awt.Color; 024import java.awt.Graphics2D; 025import java.awt.event.MouseEvent; 026import java.awt.geom.Line2D; 027import java.awt.geom.Rectangle2D; 028import java.util.List; 029 030import org.biojava.bio.BioError; 031import org.biojava.bio.program.abi.ABITrace; 032import org.biojava.bio.seq.DNATools; 033import org.biojava.bio.symbol.IllegalSymbolException; 034import org.biojava.utils.AbstractChangeable; 035import org.biojava.utils.ChangeEvent; 036import org.biojava.utils.ChangeSupport; 037import org.biojava.utils.ChangeType; 038import org.biojava.utils.ChangeVetoException; 039 040/** 041 * Renders an ABI trace file as a chromatogram graph. 042 * 043 * @author Matthew Pocock 044 * @author Mark Schreiber 045 */ 046public class AbiTraceRenderer 047extends AbstractChangeable 048implements SequenceRenderer { 049 public static final ChangeType TRACE = new ChangeType( 050 "The trace has changed", 051 AbiTraceRenderer.class, 052 "TRACE", 053 SequenceRenderContext.LAYOUT 054 ); 055 056 public static final ChangeType DEPTH = new ChangeType( 057 "The trace render depth has changed", 058 AbiTraceRenderer.class, 059 "DEPTH", 060 SequenceRenderContext.LAYOUT 061 ); 062 063 private ABITrace trace; 064 private double depth; 065 066 public AbiTraceRenderer() { 067 } 068 069 public void paint(Graphics2D g, SequenceRenderContext ctxt) { 070 if(ctxt.getDirection() == SequenceRenderContext.VERTICAL || trace == null) { 071 return; 072 } 073 074 try { 075 Rectangle2D clip = g.getClip().getBounds2D(); 076 077 int min = Math.max(ctxt.getRange().getMin(), ctxt.graphicsToSequence(clip.getMinX())); 078 int max = Math.min(ctxt.getRange().getMax(), ctxt.graphicsToSequence(clip.getMaxX()));; 079 int[] baseCalls = trace.getBasecalls(); 080 int[] traceA = trace.getTrace(DNATools.a()); 081 int[] traceG = trace.getTrace(DNATools.g()); 082 int[] traceC = trace.getTrace(DNATools.c()); 083 int[] traceT = trace.getTrace(DNATools.t()); 084 085 g.setColor(Color.green); 086 renderTrace(baseCalls, traceA, g, ctxt, min, max); 087 g.setColor(Color.black); 088 renderTrace(baseCalls, traceG, g, ctxt, min, max); 089 g.setColor(Color.blue); 090 renderTrace(baseCalls, traceC, g, ctxt, min, max); 091 g.setColor(Color.red); 092 renderTrace(baseCalls, traceT, g, ctxt, min, max); 093 } catch (IllegalSymbolException ise) { 094 throw new BioError("Can't process trace file", ise); 095 } 096 } 097 098 private void renderTrace(int[] baseCalls, int[] trace, Graphics2D g, SequenceRenderContext ctxt, int min, int max) { 099 double depthScale = depth / 2000.0; // assume X gredations 100 Line2D line = new Line2D.Float(); 101 for(int pos = min; pos <= max; pos++) { 102 int minT; 103 int maxT; 104 105 if(pos == 1) { 106 minT = 0; 107 } else { 108 minT = (baseCalls[pos - 2] + baseCalls[pos - 1]) / 2; 109 } 110 111 if(pos == baseCalls.length) { 112 maxT = trace.length - 1; 113 } else { 114 maxT = (baseCalls[pos - 1] + baseCalls[pos - 0]) / 2; 115 } 116 117 double scale = ctxt.getScale() / (double) (maxT - minT); 118 119 double stg = ctxt.sequenceToGraphics(pos); 120 for(int i = minT; i < maxT; i++) { 121 int j = i - minT; 122 line.setLine( 123 stg + scale * (0.5 + j), 124 depth - trace[i] * depthScale, 125 stg + scale * (0.5 + j + 1), 126 depth - trace[i + 1] * depthScale 127 ); 128 129 g.draw(line); 130 } 131 } 132 } 133 134 public void setTrace(ABITrace trace) 135 throws ChangeVetoException { 136 ChangeSupport cs = getChangeSupport(TRACE); 137 synchronized(cs) { 138 ChangeEvent ce = new ChangeEvent(this, TRACE, trace, this.trace); 139 cs.firePreChangeEvent(ce); 140 this.trace = trace; 141 cs.firePostChangeEvent(ce); 142 } 143 } 144 145 public ABITrace getTrace() { 146 return trace; 147 } 148 149 public void setDepth(double depth) 150 throws ChangeVetoException { 151 if(depth < 0.0) { 152 throw new ChangeVetoException("Can't set depth to a negative number: " + depth); 153 } 154 155 ChangeSupport cs = getChangeSupport(DEPTH); 156 synchronized(cs) { 157 ChangeEvent ce = new ChangeEvent(this, DEPTH, new Double(depth), new Double(this.depth)); 158 cs.firePreChangeEvent(ce); 159 this.depth = depth; 160 cs.firePostChangeEvent(ce); 161 } 162 } 163 164 public double getDepth(SequenceRenderContext src) { 165 if(src.getDirection() == SequenceRenderContext.HORIZONTAL && trace != null) { 166 return depth; 167 } else { 168 return 0.0; 169 } 170 } 171 172 public double getMinimumLeader(SequenceRenderContext src) { 173 return 0.0; 174 } 175 176 public double getMinimumTrailer(SequenceRenderContext src) { 177 return 0.0; 178 } 179 180 public SequenceViewerEvent processMouseEvent( 181 SequenceRenderContext src, 182 MouseEvent me, 183 List path 184 ) { 185 // don't do anything 186 return null; 187 } 188} 189