org.biojava.bio.gui.sequence.ZiggyFeatureRenderer Maven / Gradle / Ivy
/*
* 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.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Iterator;
import org.biojava.bio.seq.Feature;
import org.biojava.bio.seq.FeatureHolder;
import org.biojava.bio.seq.StrandedFeature;
import org.biojava.bio.symbol.Location;
import org.biojava.utils.AbstractChangeable;
import org.biojava.utils.ChangeEvent;
import org.biojava.utils.ChangeSupport;
import org.biojava.utils.ChangeVetoException;
/**
* A feature renderer that draws non-contiguous features as a set of boxes
* joined by zig-zags.
*
* This is applicable to rendering cds's or non-contiguous homologies for
* example.
*
* @author Matthew Pocock
*/
public class ZiggyFeatureRenderer
extends AbstractChangeable
implements FeatureRenderer, java.io.Serializable {
private Paint outline = Color.black;
private Paint fill = Color.yellow;
private double blockDepth = 10.0;
public void setFill(Paint p)
throws ChangeVetoException {
if(hasListeners()) {
ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT);
synchronized(cs) {
ChangeEvent ce = new ChangeEvent(
this, SequenceRenderContext.REPAINT,
p, this.fill
);
cs.firePreChangeEvent(ce);
this.fill = p;
cs.firePostChangeEvent(ce);
}
} else {
this.fill = p;
}
}
public Paint getFill() {
return fill;
}
public void setOutline(Paint p)
throws ChangeVetoException {
if(hasListeners()) {
ChangeSupport cs = getChangeSupport(SequenceRenderContext.REPAINT);
synchronized(cs) {
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.REPAINT);
cs.firePreChangeEvent(ce);
this.outline = p;
cs.firePostChangeEvent(ce);
}
} else {
this.outline = p;
}
}
public Paint getOutline() {
return outline;
}
public void setBlockDepth(double depth)
throws ChangeVetoException {
if(hasListeners()) {
ChangeSupport cs = getChangeSupport(SequenceRenderContext.LAYOUT);
synchronized(cs) {
ChangeEvent ce = new ChangeEvent(this, SequenceRenderContext.LAYOUT);
cs.firePreChangeEvent(ce);
this.blockDepth = depth;
cs.firePostChangeEvent(ce);
}
} else {
this.blockDepth = depth;
}
}
public double getBlockDepth() {
return blockDepth;
}
public double getDepth(SequenceRenderContext src) {
return blockDepth + 1.0;
}
public void renderFeature(
Graphics2D g, Feature f, SequenceRenderContext context
) {
Location loc = f.getLocation();
Iterator i = loc.blockIterator();
Location last = null;
if(i.hasNext()) {
last = (Location) i.next();
renderLocation(g, last, context);
}
while(i.hasNext()) {
Location next = (Location) i.next();
renderLink(g, f, last, next, context);
renderLocation(g, next, context);
last = next;
}
}
private void renderLocation(
Graphics2D g, Location loc, SequenceRenderContext context
) {
Rectangle2D.Double block = new Rectangle2D.Double();
double min = context.sequenceToGraphics(loc.getMin());
double max = context.sequenceToGraphics(loc.getMax()+1);
if(context.getDirection() == SequenceRenderContext.HORIZONTAL) {
block.setFrame(
min, 0.0,
max - min, blockDepth
);
} else {
block.setFrame(
0.0, min,
blockDepth, max - min
);
}
if(fill != null) {
g.setPaint(fill);
g.fill(block);
}
if(outline != null) {
g.setPaint(outline);
g.draw(block);
}
}
private StrandedFeature.Strand getStrand(Feature f) {
if (f instanceof StrandedFeature) {
return ((StrandedFeature) f).getStrand();
} else {
FeatureHolder fh = f.getParent();
if (fh instanceof Feature) {
return getStrand((Feature) fh);
} else {
return StrandedFeature.UNKNOWN;
}
}
}
private void renderLink(
Graphics2D g, Feature f, Location source, Location dest,
SequenceRenderContext context
) {
Line2D line = new Line2D.Double();
Point2D startP;
Point2D midP;
Point2D endP;
double half = blockDepth * 0.5;
if(context.getDirection() == SequenceRenderContext.HORIZONTAL) {
if(getStrand(f) == StrandedFeature.NEGATIVE) {
double start = context.sequenceToGraphics(dest.getMin());
double end = context.sequenceToGraphics(source.getMax()+1);
double mid = (start + end) * 0.5;
startP = new Point2D.Double(start, half);
midP = new Point2D.Double(mid, blockDepth);
endP = new Point2D.Double(end, half);
} else {
double start = context.sequenceToGraphics(source.getMax()+1);
double end = context.sequenceToGraphics(dest.getMin());
double mid = (start + end) * 0.5;
startP = new Point2D.Double(start, half);
midP = new Point2D.Double(mid, 0.0);
endP = new Point2D.Double(end, half);
}
} else {
if (getStrand(f) == StrandedFeature.NEGATIVE) {
double start = context.sequenceToGraphics(dest.getMin()+1);
double end = context.sequenceToGraphics(source.getMax());
double mid = (start + end) * 0.5;
startP = new Point2D.Double(half, start);
midP = new Point2D.Double(blockDepth, mid);
endP = new Point2D.Double(half, end);
} else {
double start = context.sequenceToGraphics(source.getMax());
double end = context.sequenceToGraphics(dest.getMin()+1);
double mid = (start + end) * 0.5;
startP = new Point2D.Double(half, start);
midP = new Point2D.Double(0.0, mid);
endP = new Point2D.Double(half, end);
}
}
g.setPaint(getOutline());
line.setLine(startP, midP);
g.draw(line);
line.setLine(midP, endP);
g.draw(line);
}
public FeatureHolder processMouseEvent(
FeatureHolder hits,
SequenceRenderContext src,
MouseEvent me
) {
return hits;
}
}