// previously ArcRenderer
package org.broad.igv.feature.basepair;
//~--- non-JDK imports --------------------------------------------------------
import org.apache.log4j.Logger;
import org.broad.igv.track.RenderContext;
import java.awt.*;
import java.awt.geom.GeneralPath;
// TODO: add vertical scaling option so arcs fit on track, or clip arcs outside of track rect?
// TODO: add visualization for very zoomed-out views
public class BasePairRenderer {
private static Logger log = Logger.getLogger(BasePairRenderer.class);
Color ARC_COLOR_A = new Color(50, 50, 150, 140); //transparent dull blue
Color ARC_COLOR_B = new Color(150, 50, 50, 140); //transparent dull red
Color ARC_COLOR_C = new Color(50, 0, 50, 250);
int dir = -1; // 1 for up, -1 for down
// central horizontal line color
Color COLOR_CENTERLINE = new Color(0, 0, 0, 100);
public int getDirection(){
return dir;
}
public void setDirection(int d){
dir = d;
}
public void draw(BasePairData data, RenderContext context, Rectangle trackRectangle){
double nucsPerPixel = context.getScale();
double origin = context.getOrigin();
//The location of the first base that is loaded
int start = Math.max(0, (int) origin - 1);
//The location of the last base that is loaded
int end = (int) (origin + trackRectangle.width * nucsPerPixel) + 1;
if (end <= start) return;
// TODO: make this a function
java.util.List<BasePairFeature> featureList = data.getFeatures(context.getChr());
if (featureList != null) {
for (BasePairFeature feature : featureList) {
if(feature.startLeft > context.getEndLocation()) break;
else if(feature.endRight < context.getOrigin()) continue;
//System.out.println("Color: "+data.colors[i]);
int arcCount = 0;
//System.out.println("In arcRenderer.draw(): ");
//System.out.println(" track.width = " + trackRectangle.width);
//System.out.println(" nucsPerPixel = " + nucsPerPixel);
//System.out.println(" origin = " + origin);
//System.out.println(" start = " + start);
//System.out.println(" end = " + end);
double startLeftPix = (feature.startLeft - origin) / nucsPerPixel;
double startRightPix = (feature.startRight + 1.0 - origin) / nucsPerPixel;
double endLeftPix = (feature.endLeft - origin) / nucsPerPixel;
double endRightPix = (feature.endRight + 1.0 - origin) / nucsPerPixel;
drawArc(startLeftPix, startRightPix, endLeftPix, endRightPix, trackRectangle, context, feature.color);
arcCount++;
//System.out.println(" leftStart = " + leftStartNucPix);
//System.out.println(" leftEnd = " + leftEndNucPix);
//System.out.println(" rightStart = " + rightStartNucPix);
//System.out.println(" rightEnd = " + rightEndNucPix);
//System.out.println(" arcWidth = " + arcWidthPix);
//drawArc(10, 210, 50, trackRectangle, context, ARC_COLOR_B);
//drawArc(300, 500, 50, trackRectangle, context, ARC_COLOR_A);
//System.out.println("Drew "+arcCount+" arcs");
}
}
//System.out.println("");
//draw a central horizontal line
//Graphics2D g2D = context.getGraphic2DForColor(COLOR_CENTERLINE);
//g2D.drawLine((int) trackRectangle.getX(), y,
// (int) trackRectangle.getMaxX(), y);
}
/**
* Draw a filled arc between two regions of equal length
*
* @param startLeft the starting position of the feature, whether on-screen or not (outer left corner of arc)
* @param startRight (inner left corner of arc)
* @param endLeft the ending position of the feature, whether on-screen or not (inner right corner of arc)
* @param endRight (outer right corner of arc)
* @param trackRectangle
* @param context
* @param featureColor the color specified for this feature. May be null.
*/
protected void drawArc(double startLeft, double startRight, double endLeft, double endRight,
Rectangle trackRectangle, RenderContext context, Color featureColor) {
Color color;
if (featureColor != null) {
color = featureColor;
} else {
color = ARC_COLOR_A;
}
Graphics2D g2D = context.getGraphic2DForColor(color);
//Height of top of an arc of maximum depth
int maxPossibleArcHeight = (trackRectangle.height - 1) / 2;
// Equation by G. Adam Stanislav from http://www.whizkidtech.redprince.net/bezier/circle/
double handleLengthFactor = 4f*((double)Math.sqrt(2f)-1f)/3f;
double outerRadius = (double) (endRight-startLeft)/2.0;
double innerRadius = (double) (endLeft-startRight)/2.0;
double arcWidth = Math.max(1.0, startRight-startLeft);
int y = 0;
if (dir>0){
y = (int) trackRectangle.getMaxY();
} else {
y = (int) trackRectangle.getMinY();
}
// Define all control points
// Use a minimum arc width of 1 pixel
int outerLX = (int) (trackRectangle.getX() + startLeft);
int outerLY = y;
int outerLC1X = (int) (trackRectangle.getX() + outerLX);
int outerLC1Y = (int) (outerLY - dir * handleLengthFactor*outerRadius);
int outerLC2X = (int) (trackRectangle.getX() + outerLX+outerRadius - handleLengthFactor*outerRadius);
int outerLC2Y = (int) (outerLY - dir * outerRadius);
int outerCenterX = (int) (trackRectangle.getX() + outerLX+outerRadius);
int outerCenterY = (int) (outerLY - dir * outerRadius);
int outerRC1X = (int) (trackRectangle.getX() + outerLX+outerRadius + handleLengthFactor*outerRadius);
int outerRC1Y = (int) (outerLY - dir * outerRadius);
int outerRC2X = (int) (trackRectangle.getX() + endRight);
int outerRC2Y = (int) (outerLY - dir * handleLengthFactor*outerRadius);
int outerRX = (int) (trackRectangle.getX() + endRight);
int outerRY = outerLY;
int innerRX = (int) (trackRectangle.getX() + endRight - arcWidth);
int innerRY = outerLY;
int innerRC1X = (int) (trackRectangle.getX() + innerRX);
int innerRC1Y = (int) (outerLY - dir * handleLengthFactor*innerRadius);
int innerRC2X = (int) (trackRectangle.getX() + outerLX + outerRadius + handleLengthFactor*innerRadius);
int innerRC2Y = (int) (outerCenterY + dir * arcWidth);
int innerCenterX = (int) (trackRectangle.getX() + outerLX + outerRadius);
int innerCenterY = (int) (outerCenterY + dir * arcWidth);
int innerLC1X = (int) (trackRectangle.getX() + outerLX + outerRadius - handleLengthFactor*innerRadius);
int innerLC1Y = (int) (outerCenterY + dir * arcWidth);
int innerLC2X = (int) (trackRectangle.getX() + startLeft + arcWidth);
int innerLC2Y = (int) (outerLY - dir * handleLengthFactor*innerRadius);
int innerLX = (int) (trackRectangle.getX() + startLeft + arcWidth);
int innerLY = outerLY;
if (false){
GeneralPath arcCage = new GeneralPath();
arcCage.moveTo(outerLX, outerLY); // outer left
arcCage.lineTo(outerLC1X, outerLC1Y);
arcCage.lineTo(outerLC2X, outerLC2Y);
arcCage.lineTo(outerCenterX, outerCenterY); // outer left control 1, outer left control 2, outer center
arcCage.lineTo(outerRC1X, outerRC1Y);
arcCage.lineTo(outerRC2X, outerRC2Y);
arcCage.lineTo(outerRX, outerRY);// outer right control 1, outer right control 2, outer right
arcCage.lineTo(innerRX, innerRY); // inner right
arcCage.lineTo(innerRC1X, innerRC1Y);
arcCage.lineTo(innerRC2X, innerRC2Y);
arcCage.lineTo(innerCenterX, innerCenterY); // inner right control 1, inner right control 2, inner center
arcCage.lineTo(innerLC1X, innerLC1Y);
arcCage.lineTo(innerLC2X, innerLC2Y);
arcCage.lineTo(innerLX, innerLY); // inner left control 1, inner left control 2, inner left
arcCage.lineTo(outerLX, outerLY); // outer left
arcCage.moveTo(outerLX, outerLY);
arcCage.closePath();
// Draw the shape
//g2D.draw(arcCage);
g2D.fill(arcCage);
}
// Create path
if (true){
GeneralPath arcPath = new GeneralPath();
arcPath.moveTo(outerLX, outerLY); // outer left
arcPath.curveTo(outerLC1X, outerLC1Y,
outerLC2X, outerLC2Y,
outerCenterX, outerCenterY); // outer left control 1, outer left control 2, outer center
arcPath.curveTo(outerRC1X, outerRC1Y,
outerRC2X, outerRC2Y,
outerRX, outerRY);// outer right control 1, outer right control 2, outer right
arcPath.lineTo(innerRX, innerRY); // inner right
arcPath.curveTo(innerRC1X, innerRC1Y,
innerRC2X, innerRC2Y,
innerCenterX, innerCenterY); // inner right control 1, inner right control 2, inner center
arcPath.curveTo(innerLC1X, innerLC1Y,
innerLC2X, innerLC2Y,
innerLX, innerLY); // inner left control 1, inner left control 2, inner left
arcPath.lineTo(outerLX, outerLY); // outer left
arcPath.moveTo(outerLX, outerLY);
arcPath.closePath();
// Draw the arc face
g2D.fill(arcPath);
}
g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_DEFAULT);
g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT);
}
}