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);
}
}