package org.seqcode.viz.genomicplot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JFrame;
import org.seqcode.gseutils.ArgParser;
import org.seqcode.gseutils.Pair;
import org.seqcode.viz.paintable.AbstractPaintable;
import org.seqcode.viz.paintable.PaintableFrame;
public class ExpressionPainter extends AbstractPaintable{
private MultidataSpatialPaintable painter;
private static PaintableFrame plotter;
private static int deftScreenSizeX=800, deftScreenSizeY=1200;
private int screenSizeX, screenSizeY;
ArrayList<Pair<String, ArrayList<Double>>> expression = new ArrayList<Pair<String, ArrayList<Double>>>();
private final int geneArcWidth=2, geneArcHeight=2;
private final int expBoxHeight=20, expBoxWidth = 40;
private final int clusterSpacing = 120;
private final int colorbarHeight=15, colorbarWidth=120;
private final double maxExp=4.0, midExp=0, minExp=-4;
private Color expMaxColor = Color.yellow;
private Color expMidColor = Color.black;
private Color expMinColor = Color.blue;
//private Color expMaxColor = new Color(18,54,36);
//private Color expMidColor = Color.white;
//private Color expMinColor = Color.white;
private Color geneColor = Color.gray;
private final int topBorder=50, bottomBorder=50;
private final int leftBorder=25, rightBorder=25;
private int topBound, bottomBound, leftBound, rightBound, baseLine, midLine;
private static ArrayList<String> timeLabels= new ArrayList<String>();
private static int numExpr =4;
private final int geneBoxHeight=22, geneBoxWidth=(numExpr*expBoxWidth)+4;
public static void main(String[] args) {
ArgParser ap = new ArgParser(args);
if(!ap.hasKey("data")) {
System.err.println("Usage:\n " +
"Expression Painter " +
"--data <file name> ");
return;
}
String dfile = ap.getKeyValue("data");
ArrayList<Pair<String, ArrayList<Double>>> expr = loadFile(dfile);
//Paint the picture
ExpressionPainter painter = new ExpressionPainter(expr);
plotter = new PaintableFrame("Expression Painter", painter);
plotter.setSize(deftScreenSizeX, deftScreenSizeY);
plotter.setVisible(true);
plotter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public ExpressionPainter(ArrayList<Pair<String, ArrayList<Double>>> x){
expression = x;
}
public void paintItem (Graphics g, int x1, int y1, int x2, int y2){
Graphics2D g2d = (Graphics2D)g;
FontMetrics metrics = g2d.getFontMetrics();
screenSizeX = x2-x1;
screenSizeY = y2-y1;
g2d.setColor(Color.white);
g2d.fillRect(0, 0, screenSizeX, screenSizeY);
topBound = topBorder+140;
bottomBound = screenSizeY-bottomBorder;
leftBound = leftBorder+30;
rightBound = screenSizeX-rightBorder;
baseLine = (topBound+bottomBound)/2;
midLine = (leftBound+rightBound)/2;
int xPos = (leftBound+rightBound)/2;
//Background rounded boxes
int j=0;
for(Pair <String, ArrayList<Double>> p : expression){
String currName = p.car();
ArrayList<Double> evals = p.cdr();
boolean found =false;
int boxX = xPos-(geneBoxWidth/2);
int boxY = topBound+geneBoxHeight*(j-1);
g2d.setColor(geneColor);
g2d.setStroke(new BasicStroke(1.0f));
g2d.fillRoundRect(boxX, boxY, geneBoxWidth, geneBoxHeight, geneArcWidth, geneArcHeight);
g2d.setColor(Color.darkGray);
g2d.drawRoundRect(boxX, boxY, geneBoxWidth, geneBoxHeight, geneArcWidth, geneArcHeight);
//Default expression boxes
int eY = boxY+(geneBoxHeight-expBoxHeight)/2;
for(int e=0; e<numExpr; e++){
int eX = xPos - ((numExpr*expBoxWidth)/2) +(e*expBoxWidth);
g2d.setColor(Color.lightGray);
g2d.fillRect(eX, eY,expBoxWidth, expBoxHeight);
g2d.setColor(Color.white);
g2d.setStroke(new BasicStroke(1.0f));
g2d.drawRect(eX, eY,expBoxWidth, expBoxHeight);
}
//Find appropriate expression values
if(evals != null){
for(int e=0; e<numExpr; e++){
Double v = evals.get(e);
Color currCol = expColor(v);
int eX = xPos - ((numExpr*expBoxWidth)/2) +(e*expBoxWidth);
g2d.setColor(currCol);
g2d.fillRect(eX, eY,expBoxWidth, expBoxHeight);
g2d.setColor(Color.white);
g2d.setStroke(new BasicStroke(1.0f));
g2d.drawRect(eX, eY,expBoxWidth, expBoxHeight);
}
}
//Draw the String
g2d.setColor(Color.black);
g2d.setFont(new Font("Ariel", Font.PLAIN, 16));
g2d.drawString(currName, boxX+geneBoxWidth+10,boxY+(geneBoxHeight/2)+8);
j++;
}
/*
g2d.setColor(Color.black);
g2d.setFont(new Font("Ariel", Font.BOLD, 20));
metrics = g2d.getFontMetrics();
int xPos = (midLine-(5*clusterSpacing/2));
g2d.drawString("Paralog", xPos-(metrics.stringWidth("Paralog")/2), topBound-(geneBoxHeight/2));
for(int j=1; j<=13; j++){
g2d.setFont(new Font("Ariel", Font.PLAIN, 20));
//Vert g2d.drawString(String.format("%d",j), xPos, topBound+(geneBoxHeight*(j-1))+(geneBoxHeight/2));
AffineTransform oldtrans = g2d.getTransform();
AffineTransform newtrans = new AffineTransform();
newtrans.translate(xPos, topBound+(geneBoxHeight*(j-1))+(geneBoxHeight/2));
newtrans.rotate(Math.toRadians(90));
g2d.setTransform(newtrans);
g2d.drawString(String.format("%d",j), 0,0);
g2d.setTransform(oldtrans);
}*/
//Colorbar
drawExpColorBar(g2d, midLine-(colorbarWidth/2), topBound-160);
//Time Labels
/*
xPos = midLine;
int yPos = topBound+geneBoxHeight*(j);
g2d.setColor(Color.black);
AffineTransform oldtrans = g2d.getTransform();
AffineTransform newtrans = new AffineTransform();
newtrans.translate(xPos, yPos);
//Vert
//newtrans.rotate(Math.toRadians(-90));
newtrans.rotate(Math.toRadians(90));
g2d.setTransform(newtrans);
g2d.setFont(new Font("Ariel", Font.PLAIN, 16));
metrics = g2d.getFontMetrics();
g2d.drawString("Day",-1*metrics.stringWidth("Day")/2,-1*(geneBoxWidth/2));
g2d.setFont(new Font("Ariel", Font.PLAIN, 16));
metrics = g2d.getFontMetrics();
for(int e=0; e<numExpr; e++){
//Vert int etY = (e*expBoxWidth)-((numExpr*expBoxWidth)/2)+(expBoxWidth/2)+(metrics.getHeight()/2);
int etY = ((numExpr-e-1)*expBoxWidth)-((numExpr*expBoxWidth)/2)+(expBoxWidth/2)+(metrics.getHeight()/2);
String text = timeLabels.get(e);
g2d.drawString(text,0,etY);
}
g2d.setTransform(oldtrans);
*/
}
private Color expColor(double v){
Color c;
if(v>midExp){
Color maxColor = expMaxColor;
Color minColor = expMidColor;
double sVal = v>maxExp ? 1 : (v-midExp)/(maxExp-midExp);
int red = (int)(maxColor.getRed() * sVal + minColor.getRed() * (1 - sVal));
int green = (int)(maxColor.getGreen() * sVal + minColor.getGreen() * (1 - sVal));
int blue = (int)(maxColor.getBlue() *sVal + minColor.getBlue() * (1 - sVal));
c = new Color(red, green, blue);
}else{
Color maxColor = expMidColor;
Color minColor = expMinColor;
double sVal = v<minExp ? 0 : ((midExp-minExp)-(midExp-v))/(midExp-minExp);
int red = (int)(maxColor.getRed() * sVal + minColor.getRed() * (1 - sVal));
int green = (int)(maxColor.getGreen() * sVal + minColor.getGreen() * (1 - sVal));
int blue = (int)(maxColor.getBlue() *sVal + minColor.getBlue() * (1 - sVal));
c = new Color(red, green, blue);
}
return(c);
}
private void drawExpColorBar(Graphics2D g2d, int x, int y){
//Draw colors
GradientPaint colorbar = new GradientPaint(x, y, expMinColor, x+colorbarWidth/2, y, expMidColor, false);
g2d.setPaint(colorbar);
g2d.fillRect(x, y, colorbarWidth/2, colorbarHeight);
colorbar = new GradientPaint(x+colorbarWidth/2, y, expMidColor, x+colorbarWidth, y, expMaxColor, false);
g2d.setPaint(colorbar);
g2d.fillRect(x+(colorbarWidth/2), y, colorbarWidth/2, colorbarHeight);
//Draw border
g2d.setPaint(Color.black);
g2d.setColor(Color.black);
g2d.setStroke(new BasicStroke(1.0f));
g2d.drawRect(x, y, colorbarWidth, colorbarHeight);
//Legend
g2d.setFont(new Font("Ariel", Font.PLAIN, 12));
FontMetrics metrics = g2d.getFontMetrics();
int textY = y+colorbarHeight+ (metrics.getHeight());
g2d.drawString(String.format("%.0f",midExp), x+(colorbarWidth/2)-(metrics.stringWidth(String.format("%.0f",midExp))/2), textY);
g2d.drawString(String.format("%.1f",minExp), x-(metrics.stringWidth(String.format(".1f",minExp))/2), textY);
g2d.drawString(String.format("%.1f",maxExp), x+colorbarWidth-(metrics.stringWidth(String.format(".1f",maxExp))/2), textY);
//Title
g2d.setFont(new Font("Ariel", Font.ITALIC, 12));
metrics = g2d.getFontMetrics();
g2d.drawString("log-foldchange", x+(colorbarWidth/2)-(metrics.stringWidth("log-foldchange")/2), y- (metrics.getHeight())/2);
}
private static ArrayList<Pair<String, ArrayList<Double>>> loadFile(String inF){
ArrayList<Pair<String, ArrayList<Double>>> ex = new ArrayList<Pair<String, ArrayList<Double>>>();
try{
File aFile = new File(inF);
if(aFile.isFile()){
BufferedReader reader;
reader = new BufferedReader(new FileReader(aFile));
String line;
//Labels
line= reader.readLine();
String [] tokens = line.split("\\s+");
for(int i=2; i<tokens.length; i++){
timeLabels.add(tokens[i]);
}numExpr = timeLabels.size();
//Expression
while((line= reader.readLine())!=null){
tokens = line.split("\\s+");
String name = tokens[0];
ArrayList<Double> vals = new ArrayList<Double>();
for(int i=2; i<tokens.length; i++){
vals.add(new Double(tokens[i]));
}
ex.add(new Pair(name, vals));
}
reader.close();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NumberFormatException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return(ex);
}
public static void drawArrow(Graphics2D g2d, int xCenter, int yCenter, int x, int y, float stroke) {
double aDir=Math.atan2(xCenter-x,yCenter-y);
g2d.drawLine(x,y,xCenter,yCenter);
g2d.setStroke(new BasicStroke(stroke)); // make the arrow head solid even if dash pattern has been specified
Polygon tmpPoly=new Polygon();
int i1=12+(int)(stroke*2);
int i2=6+(int)stroke; // make the arrow head the same size regardless of the length length
tmpPoly.addPoint(x,y); // arrow tip
tmpPoly.addPoint(x+xCor(i1,aDir+.5),y+yCor(i1,aDir+.5));
tmpPoly.addPoint(x+xCor(i2,aDir),y+yCor(i2,aDir));
tmpPoly.addPoint(x+xCor(i1,aDir-.5),y+yCor(i1,aDir-.5));
tmpPoly.addPoint(x,y); // arrow tip
g2d.drawPolygon(tmpPoly);
g2d.fillPolygon(tmpPoly); // remove this line to leave arrow head unpainted
}
private static int yCor(int len, double dir) {return (int)(len * Math.cos(dir));}
private static int xCor(int len, double dir) {return (int)(len * Math.sin(dir));}
}