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 */
021
022package org.biojava.bio.gui.sequence;
023
024import java.awt.Graphics2D;
025import java.awt.event.MouseEvent;
026import java.util.Iterator;
027import java.util.List;
028
029import org.biojava.bio.seq.DNATools;
030import org.biojava.bio.seq.StrandedFeature;
031import org.biojava.bio.symbol.RangeLocation;
032import org.biojava.bio.symbol.SymbolList;
033
034// The graphics model in Java
035// drawing space -> applet space -> device space
036// All operations are cumulative, including translates
037// translates will move drawing rightward/downward for any supplied value
038
039/**
040 * Compute sites of stop codons.  It uses a child renderer to
041 * do actual drawing.
042 *
043 * @author David Huen
044 */ 
045
046
047public class StopRenderer implements SequenceRenderer {
048    private double scaleThreshold = 0.005;
049    private SixFrameRenderer pane;
050    private int moduloFrame;
051    private StrandedFeature.Strand strand;
052
053    public StopRenderer(
054             SixFrameRenderer pane,
055             int moduloFrame, 
056             StrandedFeature.Strand strand) {
057    this.pane = pane;
058    this.moduloFrame = moduloFrame;
059    this.strand = strand;
060    }
061
062    public double getDepth(SequenceRenderContext src) {
063      // an arbitrary limit is set here to prevent excessive sequence
064      // download.
065      if (src.getScale() < scaleThreshold) return 0;
066        else return pane.getDepth(src);
067    }
068
069    public double getMinimumLeader(SequenceRenderContext src) {
070      return 0.0;
071    }
072
073    public double getMinimumTrailer(SequenceRenderContext src) {
074      return 0.0;
075    }
076
077    private boolean isStop(SymbolList seq,
078      int base,
079      StrandedFeature.Strand strand) {
080      // tests whether there is a stop at given location.
081      // the triplet is either base, +1, +2 or -1, -2
082      // depending on the strand searched
083      if (strand == StrandedFeature.POSITIVE) {
084        // check that search does not exceed bounds
085        if (base + 2 > seq.length()) return false;
086
087        // search top strand
088        // first base must be t
089        if (seq.symbolAt(base) != DNATools.t()) return false;
090
091        // second base cannot be c or t
092        if (seq.symbolAt(base+1) == DNATools.c()) return false;
093        if (seq.symbolAt(base+1) == DNATools.t()) return false;
094
095        // if second base is g, the third must be a
096        if (seq.symbolAt(base+1) == DNATools.g()) {
097          if (seq.symbolAt(base+2) != DNATools.a()) return false;
098        }
099        else {
100          // second base is a: third must be a or g.
101          if (seq.symbolAt(base+2) == DNATools.c()) return false;
102          if (seq.symbolAt(base+2) == DNATools.t()) return false;
103        }
104
105        // oh well, must be a stop, innit?
106        return true;
107
108      } else {
109        // check bounds
110        if (base - 2 < 1) return false;
111
112        // search bottom strand
113        // first base must be t
114        if (seq.symbolAt(base) != DNATools.a()) return false;
115
116        // second base cannot be c or t on reverse strand
117        if (seq.symbolAt(base-1) == DNATools.a()) return false;
118        if (seq.symbolAt(base-1) == DNATools.g()) return false;
119
120        // if second base is g, the third must be a
121        if (seq.symbolAt(base-1) == DNATools.c()) {
122          if (seq.symbolAt(base-2) != DNATools.t()) return false;
123        }
124        else {
125          // second base is a: third must be a or g.
126          if (seq.symbolAt(base-2) == DNATools.a()) return false;
127          if (seq.symbolAt(base-2) == DNATools.g()) return false;
128        }
129
130        // ach! a stop!
131        return true;
132      }
133    }
134
135    private void renderOneFrame(
136      Graphics2D g,
137      SequenceRenderContext src,
138      RangeLocation range,
139      boolean onceOnly) {
140      // method to draw by checking succeeding triplets for
141      // stop codons.
142      // write it for horizontal rendering first.
143      SymbolList seq = src.getSymbols();
144
145      // get extent of sequence to render
146      // hope it agrees with clip region!
147      int minS = range.getMin();
148      int maxS = range.getMax();
149
150      // we start at the first triplet whose first base is within
151      // the range.
152      if (minS%3 > moduloFrame) {
153        // first triplet of my frame is in next mod-zero triplet
154        minS = (minS/3 + 1) * 3 + moduloFrame;
155      }
156      else if (minS%3 != moduloFrame) {
157        // first triplet is in current mod-zero triplet
158        minS = (minS/3) * 3 + moduloFrame;
159      }
160
161      // now we search every triplet from minS upward seeking stops.
162      for (int base = minS; base <= maxS; base += 3) {
163        // check for stop
164        if (!isStop(seq, base, strand)) continue;
165
166        // we have a stop, render a line
167        pane.drawLine(g, src, base, strand);
168
169        // do I call it quits now?
170        if (onceOnly) return;
171      }
172    }
173
174    public void paint(
175      Graphics2D g,
176      SequenceRenderContext src
177    ) {
178      double scale = src.getScale();
179
180      // this is a completely arbitrary limit to stop my viewer
181      // from attempting to trigger the download of HUGE amounts 
182      // of sequence.
183      if (scale < scaleThreshold) return;
184
185      // could we get more than one stop per pixel at the current
186      // scale?      
187      if (scale < 0.05) {
188        // yes, we can. Iterate thru' graphics space.
189        Iterator extentsI = pane.sequenceExtentOfPixels(src).iterator();
190
191        // check each extent for stops
192        while (extentsI.hasNext()) {
193          RangeLocation range = (RangeLocation) extentsI.next();
194          renderOneFrame(g, src, range, true);
195        }
196      }
197      else {
198        // no we can't. Iterate thru' sequence.
199        renderOneFrame(g, src, src.getRange(), false);
200      }
201    }
202  
203  public SequenceViewerEvent processMouseEvent(
204    SequenceRenderContext src,
205    MouseEvent me,
206    List path
207  ) {
208    path.add(this);
209    int sPos = src.graphicsToSequence(me.getPoint());
210    return new SequenceViewerEvent(this, null, sPos, me, path);
211  }
212}