Java tutorial
/** * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package savant.amino; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.util.List; import javax.swing.JPanel; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import savant.api.adapter.TrackAdapter; import savant.api.data.Block; import savant.api.data.Record; import savant.api.data.RichIntervalRecord; import savant.api.data.Strand; import savant.api.util.GenomeUtils; import savant.api.util.NavigationUtils; import savant.api.util.RangeUtils; /** * Semi-transparent panel which is drawn over the layered-panel. * * @author tarkvara */ public class AminoCanvas extends JPanel { private static final Log LOG = LogFactory.getLog(AminoCanvas.class); /** The plugin we belong to. */ AminoPlugin plugin; /** The track for which we are rendering amino acids. */ TrackAdapter track; /** The sequence underlying the record being processed. */ byte[] sequence; /** Start of the thick section of the record being processed. */ int thickStart; public AminoCanvas(AminoPlugin p, TrackAdapter t) { plugin = p; track = t; setOpaque(false); } @Override public void paintComponent(Graphics g) { if (GenomeUtils.getGenome().isSequenceSet()) { double aminoWidth = track.transformXPos(3) - track.transformXPos(0); if (aminoWidth > 0.5) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // We'll be drawing labels if a 'W' (tryptophan) will fit into the space of 3 bases. g2.setFont(g2.getFont().deriveFont(Font.PLAIN, 8)); boolean labelled = g2.getFontMetrics().charWidth('W') < aminoWidth; try { List<Record> records = track.getDataInRange(); if (records != null) { for (Record r : records) { RichIntervalRecord rr = (RichIntervalRecord) r; int recordStart = rr.getInterval().getStart(); thickStart = rr.getThickStart(); int thickEnd = rr.getThickEnd() + 1; LOG.debug(rr.getAlternateName() + ": thickStart=" + thickStart + ", thickEnd=" + thickEnd); if (thickEnd > thickStart) { sequence = GenomeUtils.getGenome().getSequence( NavigationUtils.getCurrentReferenceName(), RangeUtils.createRange(thickStart, thickEnd)); int pos = thickStart; int leftovers = -1; // Left-overs from the previous block. List<Block> blocks = rr.getBlocks(); if (blocks != null) { for (Block b : blocks) { if (pos + 3 <= thickEnd) { // Block positions are relative to the start of the record. int blockStart = b.getPosition() + recordStart; int blockEnd = b.getEnd() + recordStart; LOG.debug(rr.getAlternateName() + ": blockStart=" + blockStart + ", blockEnd=" + blockEnd); AminoAcid a; // If we have leftovers, take care of them first. switch (leftovers) { case -1: // Fresh record with no leftovers. break; case 0: // No leftovers, so we can start immediately on the new block. pos = blockStart; if (pos < thickStart) { pos = thickStart; } break; case 1: // One base from previous block, two bases from current one. LOG.debug(rr.getAlternateName() + ": handling leftover " + getBase(pos) + " at " + pos); if (rr.getStrand() == Strand.FORWARD) { a = AminoAcid.lookup(getBase(pos), getBase(blockStart), getBase(blockStart + 1)); } else { a = AminoAcid.lookup(getComplement(blockStart + 1), getComplement(blockStart), getComplement(pos)); } paintAminoAcid(g2, a, pos, 1, pos, labelled); paintAminoAcid(g2, a, blockStart, 2, blockStart - 1, labelled); pos = blockStart + 2; break; case 2: // Two bases from previous block, one base from current one. LOG.debug(rr.getAlternateName() + ": handling leftover " + getBase(pos) + "," + getBase(pos + 1) + " at " + pos + "," + (pos + 1)); if (rr.getStrand() == Strand.FORWARD) { a = AminoAcid.lookup(getBase(pos), getBase(pos + 1), getBase(blockStart)); } else { a = AminoAcid.lookup(getComplement(blockStart), getComplement(pos + 1), getComplement(pos)); } paintAminoAcid(g2, a, pos, 2, pos, labelled); paintAminoAcid(g2, a, blockStart, 1, blockStart - 2, labelled); pos = blockStart + 1; break; } // Now, handle codons which are entirely contained within the block. while (pos + 3 <= blockEnd && pos + 3 <= thickEnd) { if (rr.getStrand() == Strand.FORWARD) { a = AminoAcid.lookup(getBase(pos), getBase(pos + 1), getBase(pos + 2)); } else { a = AminoAcid.lookup(getComplement(pos + 2), getComplement(pos + 1), getComplement(pos)); } paintAminoAcid(g2, a, pos, 3, pos, labelled); pos += 3; } leftovers = (blockEnd - pos) % 3; LOG.debug(rr.getAlternateName() + ": breaking out of loop: pos=" + pos + ", blockEnd=" + blockEnd + ", leftovers=" + leftovers); } } } } } } } catch (Exception x) { LOG.info("Unable to retrieve sequence.", x); } } } } private void paintAminoAcid(Graphics2D g2, AminoAcid a, int pos, int bases, int labelPos, boolean labelled) { if (a != null) { g2.setColor(new Color(a.color.getRed(), a.color.getGreen(), a.color.getBlue(), plugin.getAlpha())); double x0 = track.transformXPos(pos); double x1 = track.transformXPos(pos + bases); g2.fill(new Rectangle2D.Double(x0, 0.0, x1 - x0, getHeight())); if (labelled) { g2.setColor(a == AminoAcid.STOP ? Color.WHITE : Color.BLACK); double charWidth = g2.getFontMetrics().charWidth(a.code); g2.drawString(Character.toString(a.code), (float) (track.transformXPos(labelPos) + track.transformXPos(labelPos + 3) - charWidth) * 0.5F, getHeight() * 0.5F); } } } /** * Get the base at the given position within the sequence (relative to the start of the chromosome). */ private char getBase(int pos) { return (char) sequence[pos - thickStart]; } /** * Get the complement of the base at the given position within the sequence (relative to the start of the chromosome). */ private char getComplement(int pos) { switch (getBase(pos)) { case 'T': return 'A'; case 'A': return 'T'; case 'C': return 'G'; case 'G': return 'C'; default: return 'N'; } } }