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.view.tracks; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import savant.api.adapter.GraphPaneAdapter; import savant.api.data.Record; import savant.api.data.VariantRecord; import savant.api.data.VariantType; import savant.api.util.Resolution; import savant.exception.RenderingException; import savant.settings.ColourSettings; import savant.util.*; /** * Renderer for variant tracks. * * @author tarkvara */ public class VariantTrackRenderer extends TrackRenderer { private static final Log LOG = LogFactory.getLog(VariantTrackRenderer.class); VariantTrackRenderer() { } @Override public void render(Graphics2D g2, GraphPaneAdapter gp) throws RenderingException { // Put up an error message if we don't want to do any rendering. renderPreCheck(); DrawingMode mode = (DrawingMode) instructions.get(DrawingInstruction.MODE); AxisRange axisRange = (AxisRange) instructions.get(DrawingInstruction.AXIS_RANGE); gp.setXRange(axisRange.getXRange()); gp.setYRange(axisRange.getYRange()); if (gp.needsToResize()) return; switch (mode) { case MATRIX: renderMatrixMode(g2, gp); break; case FREQUENCY: renderFrequencyMode(g2, gp); } } /** * Render the data with horizontal blocks for each participant. * * @param g2 the AWT graphics object to be rendered onto * @param gp the GraphPane which we're drawing into * @throws RenderingException */ private void renderMatrixMode(Graphics2D g2, GraphPaneAdapter gp) throws RenderingException { double unitHeight = gp.getUnitHeight(); double unitWidth = gp.getUnitWidth(); ColourScheme cs = (ColourScheme) instructions.get(DrawingInstruction.COLOUR_SCHEME); ColourAccumulator accumulator = new ColourAccumulator(cs); int participantCount = ((AxisRange) instructions.get(DrawingInstruction.AXIS_RANGE)).getYMax(); for (Record rec : data) { VariantRecord varRec = (VariantRecord) rec; double x = gp.transformXPos(varRec.getPosition()); double y = (participantCount - 1) * unitHeight; double w = Math.max(1.0, unitWidth); for (int j = 0; j < participantCount; j++) { accumulateZygoteShapes(varRec.getVariantsForParticipant(j), accumulator, new Rectangle2D.Double(x, y, w, unitHeight)); y -= unitHeight; } recordToShapeMap.put(varRec, new Rectangle2D.Double(x, 0.0, w, unitHeight * participantCount)); } accumulator.fill(g2); if (unitHeight > 16.0) { String[] participants = (String[]) instructions.get(DrawingInstruction.PARTICIPANTS); double y = (participants.length - 0.5) * unitHeight + 4.0; g2.setColor(ColourSettings.getColor(ColourKey.INTERVAL_TEXT)); for (int i = 0; i < participants.length; i++) { drawFeatureLabel(g2, participants[i], 0.0, y); y -= unitHeight; } } } /** * If we're homozygotic, accumulate a rectangle for this variant. If we're heterozygotic, accumulate a triangle for each parent. * @param vars array of one or two variant types * @param accumulator a colour accumulator * @param rect bounding box used for rendering both zygotes */ public static void accumulateZygoteShapes(VariantType[] vars, ColourAccumulator accumulator, Rectangle2D rect) { ColourScheme scheme = accumulator.getScheme(); if (vars != null) { if (vars.length == 1) { accumulator.addShape(scheme.getVariantColor(vars[0]), rect); } else { Color color0 = scheme.getVariantColor(vars[0]); Color color1 = scheme.getVariantColor(vars[1]); Color blend; if (color0 == null) { blend = new Color(color1.getRed(), color1.getGreen(), color1.getBlue(), 128); } else if (color1 == null) { blend = new Color(color0.getRed(), color0.getGreen(), color0.getBlue(), 128); } else { blend = new Color((color0.getRed() + color1.getRed()) / 2, (color0.getGreen() + color1.getGreen()) / 2, (color0.getBlue() + color1.getBlue()) / 2); } accumulator.addShape(blend, rect); } } } /** * Render the data as an allele frequency plot. * * @param g2 the AWT graphics object to be rendered onto * @param gp the GraphPane which we're drawing into * @throws RenderingException */ private void renderFrequencyMode(Graphics2D g2, GraphPaneAdapter gp) throws RenderingException { ColourScheme cs = (ColourScheme) instructions.get(DrawingInstruction.COLOUR_SCHEME); ColourAccumulator accumulator = new ColourAccumulator(cs); double unitHeight = gp.getUnitHeight() / (((VariantRecord) data.get(0)).getParticipantCount() * 2.0); double unitWidth = gp.getUnitWidth(); for (int i = 0; i < data.size(); i++) { VariantRecord varRec = (VariantRecord) data.get(i); Pileup pile = calculatePileup(varRec); double bottom = gp.transformYPos(0); double x = gp.transformXPos(varRec.getPosition()); recordToShapeMap.put(varRec, new Rectangle2D.Double(x, 0.0, unitWidth, gp.getHeight())); VariantType snpNuc; while ((snpNuc = pile.getLargestVariantType(VariantType.NONE)) != null) { double h = pile.getCoverage(snpNuc, null) * unitHeight; Rectangle2D rect = new Rectangle2D.Double(x, bottom - h, unitWidth, h); accumulator.addShape(cs.getVariantColor(snpNuc), rect); bottom -= h; pile.clearVariantType(snpNuc); } } accumulator.fill(g2); } /** * Simplified version calculation from AlleleFrequencyPlot, without the case/control distinction. */ private Pileup calculatePileup(VariantRecord varRec) { Pileup pile = new Pileup(varRec.getPosition()); for (int j = 0; j < varRec.getParticipantCount(); j++) { VariantType[] jVariants = varRec.getVariantsForParticipant(j); if (jVariants != null) { if (jVariants.length == 1) { pile.pileOn(jVariants[0], 1.0, null); pile.pileOn(jVariants[0], 1.0, null); } else { pile.pileOn(jVariants[0], 1.0, null); pile.pileOn(jVariants[1], 1.0, null); } } } return pile; } @Override public Dimension getLegendSize(DrawingMode ignored) { Resolution res = (Resolution) getInstruction(DrawingInstruction.RESOLUTION); return res == Resolution.HIGH ? new Dimension(168, LEGEND_LINE_HEIGHT * 3 + 6) : null; } @Override public void drawLegend(Graphics2D g2, DrawingMode ignored) { int x = 6, y = 17; drawBaseLegend(g2, x, y, ColourKey.A, ColourKey.C, ColourKey.G, ColourKey.T); y += LEGEND_LINE_HEIGHT; g2.setColor(Color.BLACK); g2.fillRect(x, y - SWATCH_SIZE.height + 2, SWATCH_SIZE.width, SWATCH_SIZE.height); g2.setColor(Color.BLACK); g2.drawString("Deletion", x + SWATCH_SIZE.width + 3, y); x += 66; g2.setColor(Color.MAGENTA); g2.fillRect(x, y - SWATCH_SIZE.height + 2, SWATCH_SIZE.width, SWATCH_SIZE.height); g2.setColor(Color.BLACK); g2.drawString("Insertion", x + 12, y); y += LEGEND_LINE_HEIGHT; g2.drawString("Translucent = Heterozygous", 6, y); } }