package org.seqcode.viz.compositeplot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Stroke; import java.util.ArrayList; import java.util.List; import org.seqcode.viz.colors.Coloring; import org.seqcode.viz.paintable.AbstractPaintable; public class TagProfilePaintable extends AbstractPaintable{ private TagProfile profile; private Color watsonCol=Color.blue; private Color crickCol=Color.blue; private boolean isStranded; private boolean filledColumns=false; private double ymax; private boolean drawAxis=true; private boolean drawPointMarkers=false; private boolean transparent=false; private int hmargin= 50, wmargin=30; private int leftLimit, rightLimit; private List<Integer> pointsOfInterest = new ArrayList<Integer>(); //x-coordinates of points of interest public TagProfilePaintable(TagProfile profile){ this.profile = profile; leftLimit = profile.getLeftRelCoord(); rightLimit = profile.getRightRelCoord(); isStranded = profile.isStranded(); } public void setWatsonColor(Color c){watsonCol= transparent ? Coloring.clearer(c) : c;} public void setCrickColor(Color c){crickCol= transparent ? Coloring.clearer(c) : c;} public void setFilledColumns(boolean b){filledColumns=b;} public void setPointOfInterest(int coord){pointsOfInterest.add(coord);} public void setHmargin(int hm){hmargin = hm;} public void setWmargin(int wm){wmargin = wm;} public void setYmax(double y){ymax = y;} public double getYmax(){return ymax;} public void autoYmax(boolean auto){ if(auto){ double[] w = profile.getWatsonTags(); double[] c = profile.getCrickTags(); ymax = -Double.MAX_VALUE; for(int i=0; i<w.length; i++){ if(w[i]>ymax) ymax=w[i]; if(c[i]>ymax) ymax=c[i]; } } } public void setProfileLeftLimit(int relCoord){ if(relCoord>profile.getLeftRelCoord()) leftLimit=relCoord; } public void setProfileRightLimit(int relCoord){ if(relCoord<profile.getRightRelCoord()) rightLimit=relCoord; } public void setTransparent(boolean t){ if(!transparent && t){ watsonCol = Coloring.clearer(watsonCol); crickCol = Coloring.clearer(crickCol); }transparent = t; } public void paintItem(Graphics g, int x1, int y1, int x2, int y2) { Graphics2D g2 = (Graphics2D)g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Stroke oldStroke = g2.getStroke(); g2.setStroke(new BasicStroke((float)2.5)); int w = x2-x1, h = y2-y1; int tY1 = hmargin, tY2 = h-hmargin; int tX1 = wmargin, tX2 = w-wmargin; int tH = h-(hmargin*2); int tW = w-(wmargin*2); g2.setColor(Color.white); if(!transparent) g2.fillRect(x1, y1, w, y2-y1); int[] xs = new int[profileWidth()]; int[] yws = new int[profileWidth()]; int[] ycs = new int[profileWidth()]; //Split the area into two sections: watson & crick int wPlotH = profile.isStranded() ? (int)(tH/2) : tH; int wPlotY2 =tY1+wPlotH; int cPlotH = tH-wPlotH; int cPlotY1 = wPlotY2; int binPix = (int)Math.round((double)tW / ((double)profileWidth())); int centerX = tX1 + -leftLimit*binPix; // Make sure that the profile doesn't change out from underneath us... synchronized(profile) { int i=0; for(int coord = leftLimit; coord <= rightLimit; coord++) { int x = tX1 + i*binPix; if(coord==0) centerX = x; //watson double ywf= fractionalOffsetY(profile.getWatsonTagsAtRelCoord(coord)); int yw = wPlotY2 - (int)Math.round(ywf * (double)wPlotH); //crick if(isStranded){ double ycf= fractionalOffsetY(profile.getCrickTagsAtRelCoord(coord)); int yc = tY2 - (int)Math.round((1-ycf) * (double)cPlotH); ycs[i] = yc; } xs[i] = x; yws[i] = yw; i++; } //Draw the points if(filledColumns){ //Histogram style for(i = 0; i < xs.length; i++) { g2.setColor(watsonCol); g2.fillRect(xs[i],yws[i], binPix, wPlotY2-yws[i]+1); if(isStranded){ g2.setColor(crickCol); g2.fillRect(xs[i],cPlotY1, binPix, ycs[i]-cPlotY1+1); } } }else{ //Line Graph style for(i = 1; i < xs.length; i++) { g2.setColor(watsonCol); g2.drawLine(xs[i-1], yws[i-1], xs[i], yws[i]); if(isStranded){ g2.setColor(crickCol); g2.drawLine(xs[i-1], ycs[i-1], xs[i], ycs[i]); } } if(drawPointMarkers){ int rad = 2; int diam = rad*2; for(i = 0; i < xs.length; i++) { g2.setColor(Color.white); g2.fillOval(xs[i]-rad, yws[i]-rad, diam, diam); g2.setColor(watsonCol); g2.drawOval(xs[i]-rad, yws[i]-rad, diam, diam); if(isStranded){ g2.setColor(Color.white); g2.fillOval(xs[i]-rad, ycs[i]-rad, diam, diam); g2.setColor(crickCol); g2.drawOval(xs[i]-rad, ycs[i]-rad, diam, diam); } } } } } //Text if(drawAxis){ g2.setColor(Color.black); g2.setStroke(new BasicStroke(2.0f)); g2.drawLine(centerX, tY1, centerX, tY2); //Y-axis g2.drawLine(tX1, wPlotY2, tX2, wPlotY2); //X-axis g2.setFont(new Font("Ariel", Font.PLAIN, 14)); FontMetrics metrics = g2.getFontMetrics(); //X-axis labels g2.drawString(String.format("%d",leftLimit), tX1-(metrics.stringWidth(String.format("%d",leftLimit))/2), wPlotY2+(metrics.getHeight())); g2.drawString(String.format("%d",(rightLimit+1)), tX2-(metrics.stringWidth(String.format("%d",(rightLimit+1)))/2), wPlotY2+(metrics.getHeight())); //X-axis ticks g2.setColor(Coloring.clearer(Color.DARK_GRAY)); g2.setStroke(new BasicStroke(2)); int tickSpacing=100, bigTickSpacing=1000; if(profileWidth()/10000 >=1){ tickSpacing = 100; bigTickSpacing=1000; }else if(profileWidth()/1000 >=1){ tickSpacing = 10; bigTickSpacing=100; }else if(profileWidth()/100 >=1){ tickSpacing = 1; bigTickSpacing = 10; } int tickPixels = binPix * tickSpacing; int coordX = leftLimit; for(int x=tX1; x<=tX2; x+=tickPixels){ if(coordX%bigTickSpacing==0) g2.drawLine(x, wPlotY2-4, x, wPlotY2+4); //big tick marks else g2.drawLine(x, wPlotY2-2, x, wPlotY2+2); //little tick marks coordX+=tickSpacing; } } //Points of Interest for(Integer pt : pointsOfInterest){ if(pt>=leftLimit && pt<=rightLimit){ int coordX = tX1+(((pt-leftLimit)*tW)/profileWidth())+(binPix/2); g2.setColor(Color.white); g2.fillOval(coordX-3, wPlotY2-3, 6, 6); g2.setColor(Color.black); g2.drawOval(coordX-3, wPlotY2-3, 6, 6); } } g2.setStroke(oldStroke); } private double fractionalOffsetY(double val){ return (val)/(ymax); } private int profileWidth(){ return rightLimit-leftLimit+1; } }