package fr.orsay.lri.varna.models.annotations;
import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import fr.orsay.lri.varna.VARNAPanel;
import fr.orsay.lri.varna.controlers.ControleurClicMovement;
import fr.orsay.lri.varna.models.VARNAConfigLoader;
import fr.orsay.lri.varna.models.rna.ModeleBase;
import fr.orsay.lri.varna.models.rna.RNA;
import fr.orsay.lri.varna.views.VueHighlightRegionEdit;
import fr.orsay.lri.varna.views.VueUI;
public class HighlightRegionAnnotation implements Serializable {
private static final long serialVersionUID = 7087014168028684775L;
public static final Color DEFAULT_OUTLINE_COLOR = Color.decode("#6ed86e");
public static final Color DEFAULT_FILL_COLOR = Color.decode("#bcffdd");
public static final double DEFAULT_RADIUS = 16.0;
private ArrayList<ModeleBase> _bases;
private Color _outlineColor = DEFAULT_OUTLINE_COLOR;
private Color _fillColor = DEFAULT_FILL_COLOR;
private double _radius = DEFAULT_RADIUS;
public HighlightRegionAnnotation(RNA r, int startIndex, int stopIndex)
{
this(r.getBasesBetween(startIndex, stopIndex));
}
public HighlightRegionAnnotation(ArrayList<ModeleBase> b)
{
this(b,DEFAULT_FILL_COLOR,DEFAULT_OUTLINE_COLOR,DEFAULT_RADIUS);
}
public HighlightRegionAnnotation(ArrayList<ModeleBase> b,Color fill, Color outline, double radius)
{
_bases = b;
_fillColor = fill;
_outlineColor = outline;
_radius = radius;
}
public HighlightRegionAnnotation clone()
{
return new HighlightRegionAnnotation(_bases,_fillColor,_outlineColor,_radius);
}
public int getMinIndex()
{
int min = Integer.MAX_VALUE;
for (ModeleBase mb : _bases)
{
min = Math.min(min, mb.getIndex());
}
return min;
}
public int getMaxIndex()
{
int max = Integer.MIN_VALUE;
for (ModeleBase mb : _bases)
{
max = Math.max(max, mb.getIndex());
}
return max;
}
public void setOutlineColor(Color c)
{
_outlineColor = c;
}
public ArrayList<ModeleBase> getBases()
{
return _bases;
}
public void setBases(ArrayList<ModeleBase> b)
{
_bases = b;
}
public void setFillColor(Color c)
{
_fillColor = c;
}
public Color getFillColor()
{
return _fillColor;
}
public Color getOutlineColor()
{
return _outlineColor;
}
public double getRadius()
{
return _radius;
}
public void setRadius(double v)
{
_radius = v;
}
public static final int NUM_STEPS_ROUNDED_CORNERS = 10;
public GeneralPath getShape(Point2D.Double[] realCoords,Point2D.Double[] realCenters, double scaleFactor)
{
GeneralPath p = new GeneralPath();
LinkedList<Point2D.Double> pointList = new LinkedList<Point2D.Double>();
boolean isDirect = true;
if (getBases().size()>=2)
{
int j = getBases().get(0).getIndex();
Point2D.Double p1 = realCoords[j];
Point2D.Double p2 = realCoords[j+1];
Point2D.Double p3 = realCenters[j];
isDirect = RNA.testDirectionality(p1, p3, p2);
}
if (getBases().size()>0)
{
int j = getBases().get(0).getIndex();
Point2D.Double point = realCoords[j];
Point2D.Double centerBck = realCenters[j];
double dist = point.distance(centerBck);
Point2D.Double vn = new Point2D.Double((centerBck.x-point.x)/dist,(centerBck.y-point.y)/dist);
for(int k=1;k<=NUM_STEPS_ROUNDED_CORNERS;k++)
{
double angle = k*Math.PI/((double)NUM_STEPS_ROUNDED_CORNERS+1);
if (isDirect)
angle += Math.PI;
Point2D.Double nvn = new Point2D.Double(Math.cos(angle)*vn.x+Math.sin(angle)*vn.y,-Math.sin(angle)*vn.x+Math.cos(angle)*vn.y);
Point2D.Double interForward = new Point2D.Double(point.x + scaleFactor *getRadius()*nvn.x,
point.y + scaleFactor *getRadius()*nvn.y);
pointList.addLast(interForward);
}
}
for (int i = 0;i<getBases().size();i++)
{
int j1 = getBases().get(i).getIndex();
if ((j1>0)&&(j1<realCoords.length-1))
{
int j0 = j1-1;
int j2 = j1+1;
Point2D.Double p0 = realCoords[j0];
Point2D.Double p1 = realCoords[j1];
Point2D.Double p2 = realCoords[j2];
double dist1 = p2.distance(p1);
Point2D.Double v1 = new Point2D.Double((p2.x-p1.x)/dist1,(p2.y-p1.y)/dist1);
Point2D.Double vn1 = new Point2D.Double(v1.y,-v1.x);
double dist2 = p1.distance(p0);
Point2D.Double v2 = new Point2D.Double((p1.x-p0.x)/dist2,(p1.y-p0.y)/dist2);
Point2D.Double vn2 = new Point2D.Double(v2.y,-v2.x);
double h = (new Point2D.Double(vn2.x-vn1.x,vn2.y-vn1.y).distance(new Point2D.Double(0,0))/2.0);
Point2D.Double vn = new Point2D.Double((vn1.x+vn2.x)/2.0,(vn1.y+vn2.y)/2.0);
double D = vn.distance(new Point2D.Double(0.0,0.0));
vn.x/= D;
vn.y/= D;
double nnorm = (D+h*h/D);
double nnormF = nnorm;
double nnormB = nnorm;
Point2D.Double interForward = new Point2D.Double(p1.x + nnormF*scaleFactor *getRadius()*vn.x,
p1.y + nnormF*scaleFactor *getRadius()*vn.y);
Point2D.Double interBackward = new Point2D.Double(p1.x - nnormB*scaleFactor *getRadius()*vn.x,
p1.y - nnormB*scaleFactor *getRadius()*vn.y);
if (pointList.size()>0)
{
Point2D.Double prev1 = pointList.getLast();
Point2D.Double prev2 = pointList.getFirst();
if ((interForward.distance(prev1)+interBackward.distance(prev2))<(interForward.distance(prev2)+interBackward.distance(prev1)))
{
pointList.addLast(interForward);
pointList.addFirst(interBackward);
}
else
{
pointList.addFirst(interForward);
pointList.addLast(interBackward);
}
}
else
{
pointList.addLast(interForward);
pointList.addFirst(interBackward);
}
}
}
if (getBases().size()>=2)
{
int j = getBases().get(getBases().size()-1).getIndex();
Point2D.Double p1 = realCoords[j];
Point2D.Double p2 = realCoords[j-1];
Point2D.Double p3 = realCenters[j];
isDirect = RNA.testDirectionality(p1, p2, p3);
}
if (getBases().size()>0)
{
int j = getBases().get(getBases().size()-1).getIndex();
Point2D.Double point = realCoords[j];
Point2D.Double centerBck = realCenters[j];
double dist = point.distance(centerBck);
Point2D.Double vn = new Point2D.Double((centerBck.x-point.x)/dist,(centerBck.y-point.y)/dist);
for(int k=1;k<=NUM_STEPS_ROUNDED_CORNERS;k++)
{
double angle = k*Math.PI/((double)NUM_STEPS_ROUNDED_CORNERS+1);
if (!isDirect)
angle += Math.PI;
Point2D.Double nvn = new Point2D.Double(Math.cos(angle)*vn.x+Math.sin(angle)*vn.y,-Math.sin(angle)*vn.x+Math.cos(angle)*vn.y);
Point2D.Double interForward = new Point2D.Double(point.x + scaleFactor *getRadius()*nvn.x,
point.y + scaleFactor *getRadius()*nvn.y);
pointList.addLast(interForward);
}
}
if (pointList.size()>0)
{
Point2D.Double point = pointList.get(0);
p.moveTo((float)point.x, (float)point.y);
for (int i=1;i<pointList.size();i++)
{
point = pointList.get(i);
p.lineTo((float)point.x, (float)point.y);
}
p.closePath();
}
return p;
}
public static HighlightRegionAnnotation parseHighlightRegionAnnotation(String txt, VARNAPanel vp)
{
try
{
String[] parts = txt.split(":");
String[] coords = parts[0].split("-");
int from = Integer.parseInt(coords[0]);
int to = Integer.parseInt(coords[1]);
int i = vp.getRNA().getIndexFromBaseNumber(from);
int j = vp.getRNA().getIndexFromBaseNumber(to);
Color fill = HighlightRegionAnnotation.DEFAULT_FILL_COLOR;
Color outline = HighlightRegionAnnotation.DEFAULT_OUTLINE_COLOR;
double radius = HighlightRegionAnnotation.DEFAULT_RADIUS;
ArrayList<ModeleBase> bases = vp.getRNA().getBasesBetween(i, j);
try
{
String[] options = parts[1].split(",");
for (int k = 0; k < options.length; k++)
{
//System.out.println(options[k]);
try
{
String[] data = options[k].split("=");
String lhs = data[0].toLowerCase();
String rhs = data[1];
if (lhs.equals("fill"))
{
fill = VARNAConfigLoader.getSafeColor(rhs, fill);
}
else if (lhs.equals("outline"))
{
outline = VARNAConfigLoader.getSafeColor(rhs, outline);
}
else if (lhs.equals("radius"))
{
radius = Double.parseDouble(rhs);
}
}
catch(Exception e)
{
}
}
}
catch(Exception e)
{
}
return new HighlightRegionAnnotation(bases,fill,outline,radius);
}
catch(Exception e)
{
}
return null;
}
public String toString()
{
//String result = "HighlightRegionAnnotation[";
//result += "fill:"+_fillColor.toString();
//result += ",outline:"+_outlineColor.toString();
//result += ",radius:"+_radius;
//return result+"]";
String result = "Highlighted region "+getMinIndex()+"-"+getMaxIndex();
return result;
}
}