I've just written the first version of a frame stop renderer which will
form the basis of my six-frame stop renderer later.  i thought I'd put it
out for inspection and comments.

There is a private variable scaleTreshold to prevent this class attempting
to trigger the download of the entire chromosome sequence!  Perhaps I
ought to allow it to be set at object creation time?

To use, for now add something like this to SequenceViewer:-
      zml.addRenderer(new FrameStopRenderer(0, true));
      zml.addRenderer(new FrameStopRenderer(1, true));
      zml.addRenderer(new FrameStopRenderer(2, true));
      zml.addRenderer(new FrameStopRenderer(0, false));
      zml.addRenderer(new FrameStopRenderer(1, false));
      zml.addRenderer(new FrameStopRenderer(2, false));

This class will eventually be wrapped in a single six-frame renderer
class when I eventually get started on the six-frame ziggy renderer.
 
The first integer parameter specifies the modulo of the frame, the second,
the strand to be examined (isTopStrand).

There's a optimisation so that at small scales, it only searches far
enough to determine if there's a stop within a pixel and skips on to the
next pixel when that's the case.  Makes a big difference at low
resolution!

Could I hold off learning how to do CVS commits for the time being and
have someone handle that for me?  I don't have much coding time right
now and it's doing one or the other. I promise to learn it later: the last
time I had a look at Cyclic's manual on it, it had me mystified.

Thanks,
David Huen, Dept. of Genetics, Univ. of Cambridge.



/*
 *                    BioJava development code
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  If you do not have a copy,
 * see:
 *
 *      http://www.gnu.org/copyleft/lesser.html
 *
 * Copyright for this code is held jointly by the individual
 * authors.  These should be listed in @author doc comments.
 *
 * For more information on the BioJava project and its aims,
 * or to join the biojava-l mailing list, visit the home page
 * at:
 *
 *      http://www.biojava.org/
 *
 */

package org.biojava.bio.gui.sequence;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

import org.biojava.bio.*;
import org.biojava.bio.seq.*;
import org.biojava.bio.symbol.*;
import org.biojava.bio.gui.*;

import java.util.List;

// The graphics model in Java
// drawing space -> applet space -> device space
// All operations are cumulative, including translates
// translates will move drawing rightward/downward for any supplied value

/**
 * Render the symbols of a sequence.
 *
 * @author David Huen
 */ 


public class FrameStopRenderer implements SequenceRenderer {
    private double depth = 25.0;
    private double margin = 5.0;
    private double scaleThreshold = 0.005;
    private double bandWidth = depth - margin;
    private int moduloFrame;
    private boolean isTopStrand;

    public FrameStopRenderer(int moduloFrame, boolean isTopStrand) {
    this.moduloFrame = moduloFrame;
    this.isTopStrand = isTopStrand;
    }

    public double getDepth(SequenceRenderContext src) {
      // an arbitrary limit is set here to prevent excessive sequence
      // download.
      if (src.getScale() < scaleThreshold) return 0;
        else return depth + 1.0;
    }

    public double getMinimumLeader(SequenceRenderContext src) {
      return 0.0;
    }

    public double getMinimumTrailer(SequenceRenderContext src) {
      return 0.0;
    }

    private boolean isStop(SymbolList seq,
      int base,
      boolean searchTopStrand) {
      // tests whether there is a stop at given location.
      // the triplet is either base, +1, +2 or -1, -2
      // depending on the strand searched
      if (searchTopStrand) {
        // search top strand
        // first base must be t
        if (seq.symbolAt(base) != DNATools.t()) return false;

        // second base cannot be c or t
        if (seq.symbolAt(base+1) == DNATools.c()) return false;
        if (seq.symbolAt(base+1) == DNATools.t()) return false;

        // if second base is g, the third must be a
        if (seq.symbolAt(base+1) == DNATools.g()) {
          if (seq.symbolAt(base+2) != DNATools.a()) return false;
        }
        else {
          // second base is a: third must be a or g.
          if (seq.symbolAt(base+2) == DNATools.c()) return false;
          if (seq.symbolAt(base+2) == DNATools.t()) return false;
        }

        // oh well, must be a stop, innit?
        return true;

      } else {
        // search bottom strand
        // first base must be t
        if (seq.symbolAt(base) != DNATools.a()) return false;

        // second base cannot be c or t on reverse strand
        if (seq.symbolAt(base-1) == DNATools.a()) return false;
        if (seq.symbolAt(base-1) == DNATools.g()) return false;

        // if second base is g, the third must be a
        if (seq.symbolAt(base-1) == DNATools.c()) {
          if (seq.symbolAt(base-2) != DNATools.t()) return false;
        }
        else {
          // second base is a: third must be a or g.
          if (seq.symbolAt(base-2) == DNATools.a()) return false;
          if (seq.symbolAt(base-2) == DNATools.g()) return false;
        }

        // ach! a stop!
        return true;
      }
    }

    private void renderOneFrame(
      Graphics2D g,
      SequenceRenderContext src,
      RangeLocation range,
      boolean onceOnly) {
      // method to draw by checking succeeding triplets for
      // stop codons.
      // write it for horizontal rendering first.
      SymbolList seq = src.getSymbols();

      // get extent of sequence to render
      // hope it agrees with clip region!
      int minS = range.getMin();
      int maxS = range.getMax();

      // we start at the first triplet whose first base is within
      // the range.
      if (minS%3 > moduloFrame) {
        // first triplet of my frame is in next mod-zero triplet
        minS = (minS/3 + 1) * 3 + moduloFrame;
      }
      else if (minS%3 != moduloFrame) {
        // first triplet is in current mod-zero triplet
        minS = (minS/3) * 3 + moduloFrame;
      }

      // now we search every triplet from minS upward seeking stops.
      for (int base = minS; base < maxS; base += 3) {
        // check for stop
        if (!isStop(seq, base, isTopStrand)) continue;

        // we have a stop, render a line
        double gPos = src.sequenceToGraphics(base);
        g.drawLine((int) gPos, (int) 0,
                   (int) gPos, (int) bandWidth);

        // do I call it quits now?
        if (onceOnly) return;
      }
    }

    public void paint(
      Graphics2D g,
      SequenceRenderContext src
    ) {
      // moduloFrame indicates the frame that we are to render.
      // the absolute value indicates the mod(3) value that the
      // triplets begin at.  The sign indicates whether the top or
      // bottom strand is to be considered.

      double scale = src.getScale();

      // this is a completely arbitrary limit to stop my viewer
      // from attempting to trigger the download of HUGE amounts 
      // of sequence.
      if (scale < scaleThreshold) return;

      // could we get more than one stop per pixel at the current
      // scale?      
      if (scale < 0.10) {
        // yes, we can. Iterate thru' graphics space.
        // we will check each pixel
        RangeLocation rangeS = src.getRange();
        int minLocS = rangeS.getMin();
        int maxLocS = rangeS.getMax();
        int minP = (int) src.sequenceToGraphics(src.getRange().getMin());
        int maxP = (int) src.sequenceToGraphics(src.getRange().getMax());
        // System.out.println("FrameStopRenderer minP, maxP: " + minP + " " + maxP);
        for (int currP = minP; currP < maxP; currP++) {
          // compute extent of sequence
          // clamp values to sequence range
          int minS = Math.max((int) src.graphicsToSequence(currP), minLocS);
          int maxS = Math.min(((int) src.graphicsToSequence(currP + 1) - 1), maxLocS);
          // System.out.println("FrameStopRenderer range: " + rangeS);
          //System.out.println("FrameStopRenderer minS maxS currP: " + minS + " " + 
maxS + " " + currP);
          RangeLocation rangeForPixel = new RangeLocation(minS, maxS);

          // now draw it if necessary
          renderOneFrame(g, src, rangeForPixel, true);
        }
      }
      else {
        // no we can't. Iterate thru' sequence.
        renderOneFrame(g, src, src.getRange(), false);
      }
    }
/*
    public void paint(
      Graphics2D g,
      SequenceRenderContext src
    ) {
      SymbolList seq = src.getSymbols();
      int direction = src.getDirection();
      
      Rectangle2D oldClip = g.getClipBounds();
      g.setColor(Color.black);
      double scale = src.getScale();
    }
*/
  
  public SequenceViewerEvent processMouseEvent(
    SequenceRenderContext src,
    MouseEvent me,
    List path
  ) {
    path.add(this);
    int sPos = src.graphicsToSequence(me.getPoint());
    return new SequenceViewerEvent(this, null, sPos, me, path);
  }
}

Reply via email to