Merge branch 'Jalview-JS/develop' into merge/Ben_trying_out_JSdevelop_with_develop
[jalview.git] / srcjar / fr / orsay / lri / varna / models / annotations / HighlightRegionAnnotation.java
diff --git a/srcjar/fr/orsay/lri/varna/models/annotations/HighlightRegionAnnotation.java b/srcjar/fr/orsay/lri/varna/models/annotations/HighlightRegionAnnotation.java
new file mode 100644 (file)
index 0000000..1e671bd
--- /dev/null
@@ -0,0 +1,376 @@
+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 javax.xml.transform.sax.TransformerHandler;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+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.models.rna.VARNAPoint;
+import fr.orsay.lri.varna.utils.XMLUtils;
+import fr.orsay.lri.varna.views.VueHighlightRegionEdit;
+import fr.orsay.lri.varna.views.VueUI;
+
+public class HighlightRegionAnnotation implements Serializable {
+       
+       public static final String HEADER_TEXT = "HighlightRegionAnnotation";
+
+       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 Color _outlineColor = DEFAULT_OUTLINE_COLOR;
+       private Color _fillColor = DEFAULT_FILL_COLOR;
+       private double _radius = DEFAULT_RADIUS;
+       private ArrayList<ModeleBase> _bases;
+       
+       public static String XML_ELEMENT_NAME = "region";
+       public static String XML_VAR_OUTLINE_NAME = "outline";
+       public static String XML_VAR_FILL_NAME = "fill";
+       public static String XML_VAR_RADIUS_NAME = "radius";
+
+       public void toXML(TransformerHandler hd) throws SAXException
+       {
+               AttributesImpl atts = new AttributesImpl();
+               atts.addAttribute("","",XML_VAR_OUTLINE_NAME,"CDATA",""+XMLUtils.toHTMLNotation(_outlineColor));
+               atts.addAttribute("","",XML_VAR_FILL_NAME,"CDATA",""+XMLUtils.toHTMLNotation(_fillColor));
+               atts.addAttribute("","",XML_VAR_RADIUS_NAME,"CDATA",""+_radius);
+               hd.startElement("","",XML_ELEMENT_NAME,atts);
+               XMLUtils.toXML(hd, _bases);
+               hd.endElement("","",XML_ELEMENT_NAME);
+       }
+
+  public HighlightRegionAnnotation(RNA r, int startIndex, int stopIndex)
+  {
+         this(r.getBasesBetween(startIndex, stopIndex));
+  }
+
+  public HighlightRegionAnnotation()
+  {
+         this(new ArrayList<ModeleBase>());
+  }
+
+  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 = 16;
+  
+  private Point2D.Double symImage(Point2D.Double p, Point2D.Double center)
+  {
+         return new Point2D.Double(2.*center.x-p.x, 2.*center.y-p.y);
+  }
+  
+  private LinkedList<Point2D.Double> buildRoundedCorner(Point2D.Double p1, Point2D.Double p2, Point2D.Double anotherPoint)
+  {
+               LinkedList<Point2D.Double> result = new LinkedList<Point2D.Double>();
+               Point2D.Double m = new Point2D.Double((p1.x+p2.x)/2.0,(p1.y+p2.y)/2.0);
+               double rad = p1.distance(p2)/2.;
+               double angle = Math.atan2(p1.y-m.y, p1.x-m.x);
+               
+               double incr = Math.PI/((double)NUM_STEPS_ROUNDED_CORNERS+1);
+               
+               Point2D.Double pdir = new Point2D.Double(m.x+rad*Math.cos(angle+Math.PI/2.),m.y+rad*Math.sin(angle+Math.PI/2.));
+               if (pdir.distance(anotherPoint)<p1.distance(anotherPoint))
+               { incr = -incr; }
+               
+               for(int k=1;k<=NUM_STEPS_ROUNDED_CORNERS;k++)
+               {
+                       double angle2 = angle+k*incr;
+                       Point2D.Double interForward = new Point2D.Double(m.x+rad*Math.cos(angle2),m.y+rad*Math.sin(angle2));
+                       result.addLast(interForward);
+               }
+               return result;
+  }
+  
+  
+       public GeneralPath getShape(Point2D.Double[] realCoords,Point2D.Double[] realCenters, double scaleFactor)
+       {
+               GeneralPath p = new GeneralPath();
+               LinkedList<Point2D.Double> pointList = new LinkedList<Point2D.Double>();
+               for (int i = 0;i<getBases().size();i++)
+               { 
+                       int j1 = getBases().get(i).getIndex();
+                       {
+                               int j0 = j1-1;
+                               int j2 = j1+1;
+                               Point2D.Double p0 = new Point2D.Double(0., 0.);
+                               Point2D.Double p1 = new Point2D.Double(0., 0.);                 
+                               Point2D.Double p2 = new Point2D.Double(0., 0.);
+                               if (i==0)
+                               {
+                                       // Single point
+                                       if (i==getBases().size()-1)
+                                       {
+                                               p1 = realCoords[j1];                    
+                                               p0 = new Point2D.Double(p1.x+scaleFactor *getRadius(), p1.y);
+                                               p2 = new Point2D.Double(p1.x-scaleFactor *getRadius(), p1.y);
+                                       }
+                                       else
+                                       {
+                                               p1 = realCoords[j1];                    
+                                               p2 = realCoords[j2];
+                                               p0 = symImage(p2, p1);
+                                       }
+                               }
+                               else if (i==getBases().size()-1)
+                               {
+                                       p0 = realCoords[j0];
+                                       p1 = realCoords[j1];                            
+                                       p2 = symImage(p0, p1);;                                 
+                               }
+                               else
+                               {
+                                       p0 = realCoords[j0];
+                                       p1 = realCoords[j1];                    
+                                       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()==1)
+               {
+                       int midl = pointList.size()/2;
+                       Point2D.Double mid = pointList.get(midl);
+                       Point2D.Double apoint = new Point2D.Double(mid.x+1.,mid.y);
+                       LinkedList<Point2D.Double> pointListStart = buildRoundedCorner(pointList.get(midl-1), pointList.get(midl), apoint);
+                       pointList.addAll(midl, pointListStart);
+                       mid = pointList.get(midl);
+                       apoint = new Point2D.Double(mid.x+1.,mid.y);
+                       LinkedList<Point2D.Double> pointListEnd = buildRoundedCorner(pointList.get(pointList.size()-1),pointList.get(0), apoint);
+                       pointList.addAll(0,pointListEnd);                       
+               }
+               else if (getBases().size()>1)
+               {
+                       int midl = pointList.size()/2;
+                       Point2D.Double apoint = symImage(pointList.get(midl),pointList.get(midl-1));
+                       LinkedList<Point2D.Double> pointListStart = buildRoundedCorner(pointList.get(midl-1), pointList.get(midl), apoint);
+                       pointList.addAll(midl, pointListStart);
+                       apoint = symImage(realCoords[getBases().get(getBases().size()-1).getIndex()],
+                                       realCoords[getBases().get(getBases().size()-2).getIndex()]);
+                       LinkedList<Point2D.Double> pointListEnd = buildRoundedCorner(pointList.get(pointList.size()-1),pointList.get(0), apoint);
+                       pointList.addAll(0,pointListEnd);
+               }
+               
+
+               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);
+               if (parts.length>1)
+               {
+               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)
+               {
+                       e.printStackTrace();
+               }
+               }
+               return new HighlightRegionAnnotation(bases,fill,outline,radius);
+               }
+       catch(Exception e)
+       {
+               e.printStackTrace();
+       }
+       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;
+       }
+       
+}