JAL-3026 srcjar files for VARNA and log4j
[jalview.git] / srcjar / fr / orsay / lri / varna / models / annotations / HighlightRegionAnnotation.java
1 package fr.orsay.lri.varna.models.annotations;
2
3 import java.awt.Color;
4 import java.awt.Shape;
5 import java.awt.geom.GeneralPath;
6 import java.awt.geom.Point2D;
7 import java.io.Serializable;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.LinkedList;
11
12 import javax.xml.transform.sax.TransformerHandler;
13
14 import org.xml.sax.SAXException;
15 import org.xml.sax.helpers.AttributesImpl;
16
17 import fr.orsay.lri.varna.VARNAPanel;
18 import fr.orsay.lri.varna.controlers.ControleurClicMovement;
19 import fr.orsay.lri.varna.models.VARNAConfigLoader;
20 import fr.orsay.lri.varna.models.rna.ModeleBase;
21 import fr.orsay.lri.varna.models.rna.RNA;
22 import fr.orsay.lri.varna.models.rna.VARNAPoint;
23 import fr.orsay.lri.varna.utils.XMLUtils;
24 import fr.orsay.lri.varna.views.VueHighlightRegionEdit;
25 import fr.orsay.lri.varna.views.VueUI;
26
27 public class HighlightRegionAnnotation implements Serializable {
28         
29         public static final String HEADER_TEXT = "HighlightRegionAnnotation";
30
31         private static final long serialVersionUID = 7087014168028684775L;
32         public static final Color DEFAULT_OUTLINE_COLOR = Color.decode("#6ed86e");
33         public static final Color DEFAULT_FILL_COLOR = Color.decode("#bcffdd");
34         public static final double DEFAULT_RADIUS = 16.0;
35         
36         private Color _outlineColor = DEFAULT_OUTLINE_COLOR;
37         private Color _fillColor = DEFAULT_FILL_COLOR;
38         private double _radius = DEFAULT_RADIUS;
39         private ArrayList<ModeleBase> _bases;
40         
41         public static String XML_ELEMENT_NAME = "region";
42         public static String XML_VAR_OUTLINE_NAME = "outline";
43         public static String XML_VAR_FILL_NAME = "fill";
44         public static String XML_VAR_RADIUS_NAME = "radius";
45
46         public void toXML(TransformerHandler hd) throws SAXException
47         {
48                 AttributesImpl atts = new AttributesImpl();
49                 atts.addAttribute("","",XML_VAR_OUTLINE_NAME,"CDATA",""+XMLUtils.toHTMLNotation(_outlineColor));
50                 atts.addAttribute("","",XML_VAR_FILL_NAME,"CDATA",""+XMLUtils.toHTMLNotation(_fillColor));
51                 atts.addAttribute("","",XML_VAR_RADIUS_NAME,"CDATA",""+_radius);
52                 hd.startElement("","",XML_ELEMENT_NAME,atts);
53                 XMLUtils.toXML(hd, _bases);
54                 hd.endElement("","",XML_ELEMENT_NAME);
55         }
56
57   public HighlightRegionAnnotation(RNA r, int startIndex, int stopIndex)
58   {
59           this(r.getBasesBetween(startIndex, stopIndex));
60   }
61
62   public HighlightRegionAnnotation()
63   {
64           this(new ArrayList<ModeleBase>());
65   }
66
67   public HighlightRegionAnnotation(ArrayList<ModeleBase> b)
68   {
69           this(b,DEFAULT_FILL_COLOR,DEFAULT_OUTLINE_COLOR,DEFAULT_RADIUS);
70   }
71  
72   
73   public HighlightRegionAnnotation(ArrayList<ModeleBase> b,Color fill, Color outline, double radius)
74   {
75           _bases = b;
76           _fillColor = fill;
77           _outlineColor = outline;
78           _radius = radius;
79   }
80   
81         public HighlightRegionAnnotation clone()
82         {
83                 return new HighlightRegionAnnotation(_bases,_fillColor,_outlineColor,_radius);
84         }
85         
86   public int getMinIndex()
87   {
88           int min = Integer.MAX_VALUE;
89           for (ModeleBase mb : _bases)
90           {
91                   min = Math.min(min, mb.getIndex());
92           }
93           return min;
94   }
95
96   public int getMaxIndex()
97   {
98           int max = Integer.MIN_VALUE;
99           for (ModeleBase mb : _bases)
100           {
101                   max = Math.max(max, mb.getIndex());
102           }
103           return max;
104   }
105
106   
107   public void setOutlineColor(Color c)
108   {
109           _outlineColor = c;  
110   }
111
112   public ArrayList<ModeleBase> getBases()
113   {
114           return _bases;
115   }
116
117   public void setBases(ArrayList<ModeleBase> b)
118   {
119           _bases = b;
120   }
121   
122   public void setFillColor(Color c)
123   {
124           _fillColor = c;  
125   }
126
127   public Color getFillColor()
128   {
129           return _fillColor;  
130   }
131  
132   public Color getOutlineColor()
133   {
134           return _outlineColor;  
135   }
136
137   public double getRadius()
138   {
139           return _radius;  
140   }
141
142   public void setRadius(double v)
143   {
144           _radius = v;  
145   }
146   
147   public static final int NUM_STEPS_ROUNDED_CORNERS = 16;
148   
149   private Point2D.Double symImage(Point2D.Double p, Point2D.Double center)
150   {
151           return new Point2D.Double(2.*center.x-p.x, 2.*center.y-p.y);
152   }
153   
154   private LinkedList<Point2D.Double> buildRoundedCorner(Point2D.Double p1, Point2D.Double p2, Point2D.Double anotherPoint)
155   {
156                 LinkedList<Point2D.Double> result = new LinkedList<Point2D.Double>();
157                 Point2D.Double m = new Point2D.Double((p1.x+p2.x)/2.0,(p1.y+p2.y)/2.0);
158                 double rad = p1.distance(p2)/2.;
159                 double angle = Math.atan2(p1.y-m.y, p1.x-m.x);
160                 
161                 double incr = Math.PI/((double)NUM_STEPS_ROUNDED_CORNERS+1);
162                 
163                 Point2D.Double pdir = new Point2D.Double(m.x+rad*Math.cos(angle+Math.PI/2.),m.y+rad*Math.sin(angle+Math.PI/2.));
164                 if (pdir.distance(anotherPoint)<p1.distance(anotherPoint))
165                 { incr = -incr; }
166                 
167                 for(int k=1;k<=NUM_STEPS_ROUNDED_CORNERS;k++)
168                 {
169                         double angle2 = angle+k*incr;
170                         Point2D.Double interForward = new Point2D.Double(m.x+rad*Math.cos(angle2),m.y+rad*Math.sin(angle2));
171                         result.addLast(interForward);
172                 }
173                 return result;
174   }
175   
176   
177         public GeneralPath getShape(Point2D.Double[] realCoords,Point2D.Double[] realCenters, double scaleFactor)
178         {
179                 GeneralPath p = new GeneralPath();
180                 LinkedList<Point2D.Double> pointList = new LinkedList<Point2D.Double>();
181                 for (int i = 0;i<getBases().size();i++)
182                 { 
183                         int j1 = getBases().get(i).getIndex();
184                         {
185                                 int j0 = j1-1;
186                                 int j2 = j1+1;
187                                 Point2D.Double p0 = new Point2D.Double(0., 0.);
188                                 Point2D.Double p1 = new Point2D.Double(0., 0.);                 
189                                 Point2D.Double p2 = new Point2D.Double(0., 0.);
190                                 if (i==0)
191                                 {
192                                         // Single point
193                                         if (i==getBases().size()-1)
194                                         {
195                                                 p1 = realCoords[j1];                    
196                                                 p0 = new Point2D.Double(p1.x+scaleFactor *getRadius(), p1.y);
197                                                 p2 = new Point2D.Double(p1.x-scaleFactor *getRadius(), p1.y);
198                                         }
199                                         else
200                                         {
201                                                 p1 = realCoords[j1];                    
202                                                 p2 = realCoords[j2];
203                                                 p0 = symImage(p2, p1);
204                                         }
205                                 }
206                                 else if (i==getBases().size()-1)
207                                 {
208                                         p0 = realCoords[j0];
209                                         p1 = realCoords[j1];                            
210                                         p2 = symImage(p0, p1);;                                 
211                                 }
212                                 else
213                                 {
214                                         p0 = realCoords[j0];
215                                         p1 = realCoords[j1];                    
216                                         p2 = realCoords[j2];
217                                 }
218
219                                 double dist1 = p2.distance(p1);
220                                 Point2D.Double v1 = new Point2D.Double((p2.x-p1.x)/dist1,(p2.y-p1.y)/dist1);
221                                 Point2D.Double vn1 = new Point2D.Double(v1.y,-v1.x);
222                                 double dist2 = p1.distance(p0);
223                                 Point2D.Double v2 = new Point2D.Double((p1.x-p0.x)/dist2,(p1.y-p0.y)/dist2);
224                                 Point2D.Double vn2 = new Point2D.Double(v2.y,-v2.x);
225                                 double h = (new Point2D.Double(vn2.x-vn1.x,vn2.y-vn1.y).distance(new Point2D.Double(0,0))/2.0);
226                                 Point2D.Double vn = new Point2D.Double((vn1.x+vn2.x)/2.0,(vn1.y+vn2.y)/2.0);
227                                 double D = vn.distance(new Point2D.Double(0.0,0.0));
228                                 vn.x/= D;
229                                 vn.y/= D;
230                                 double nnorm = (D+h*h/D);
231                                 
232                                 double nnormF = nnorm;
233                                 double nnormB = nnorm;
234                                 
235                                 
236                                 Point2D.Double interForward = new Point2D.Double(p1.x + nnormF*scaleFactor *getRadius()*vn.x, 
237                                                 p1.y + nnormF*scaleFactor *getRadius()*vn.y);
238                                 Point2D.Double interBackward = new Point2D.Double(p1.x - nnormB*scaleFactor *getRadius()*vn.x, 
239                                                 p1.y - nnormB*scaleFactor *getRadius()*vn.y);
240
241
242                                 if (pointList.size()>0)
243                                 {
244                                         Point2D.Double prev1 = pointList.getLast();                     
245                                         Point2D.Double prev2 = pointList.getFirst();
246
247                                         if ((interForward.distance(prev1)+interBackward.distance(prev2))<(interForward.distance(prev2)+interBackward.distance(prev1)))
248                                         {
249                                                 pointList.addLast(interForward);
250                                                 pointList.addFirst(interBackward);
251                                         }
252                                         else
253                                         {
254                                                 pointList.addFirst(interForward);
255                                                 pointList.addLast(interBackward);
256                                         }
257                                 }
258                                 else
259                                 {
260                                         pointList.addLast(interForward);
261                                         pointList.addFirst(interBackward);
262                                 }
263                         }
264                 }
265                 if (getBases().size()==1)
266                 {
267                         int midl = pointList.size()/2;
268                         Point2D.Double mid = pointList.get(midl);
269                         Point2D.Double apoint = new Point2D.Double(mid.x+1.,mid.y);
270                         LinkedList<Point2D.Double> pointListStart = buildRoundedCorner(pointList.get(midl-1), pointList.get(midl), apoint);
271                         pointList.addAll(midl, pointListStart);
272                         mid = pointList.get(midl);
273                         apoint = new Point2D.Double(mid.x+1.,mid.y);
274                         LinkedList<Point2D.Double> pointListEnd = buildRoundedCorner(pointList.get(pointList.size()-1),pointList.get(0), apoint);
275                         pointList.addAll(0,pointListEnd);                       
276                 }
277                 else if (getBases().size()>1)
278                 {
279                         int midl = pointList.size()/2;
280                         Point2D.Double apoint = symImage(pointList.get(midl),pointList.get(midl-1));
281                         LinkedList<Point2D.Double> pointListStart = buildRoundedCorner(pointList.get(midl-1), pointList.get(midl), apoint);
282                         pointList.addAll(midl, pointListStart);
283                         apoint = symImage(realCoords[getBases().get(getBases().size()-1).getIndex()],
284                                         realCoords[getBases().get(getBases().size()-2).getIndex()]);
285                         LinkedList<Point2D.Double> pointListEnd = buildRoundedCorner(pointList.get(pointList.size()-1),pointList.get(0), apoint);
286                         pointList.addAll(0,pointListEnd);
287                 }
288                 
289
290                 if (pointList.size()>0)
291                 {
292                         Point2D.Double point = pointList.get(0);
293                         p.moveTo((float)point.x, (float)point.y);
294
295                         for (int i=1;i<pointList.size();i++)
296                         {
297                                 point = pointList.get(i);
298                                 p.lineTo((float)point.x, (float)point.y);                               
299                         }
300                         p.closePath();
301                 }
302                 return p;
303         }
304
305         
306         public static HighlightRegionAnnotation parseHighlightRegionAnnotation(String txt, VARNAPanel vp)
307         {
308                 try
309                 {
310                 String[] parts = txt.split(":");
311                 String[] coords = parts[0].split("-");
312                 int from = Integer.parseInt(coords[0]);
313                 int to = Integer.parseInt(coords[1]);
314                 int  i = vp.getRNA().getIndexFromBaseNumber(from);
315                 int  j = vp.getRNA().getIndexFromBaseNumber(to);
316                 Color fill = HighlightRegionAnnotation.DEFAULT_FILL_COLOR;
317                 Color outline = HighlightRegionAnnotation.DEFAULT_OUTLINE_COLOR;
318                 double radius = HighlightRegionAnnotation.DEFAULT_RADIUS;
319                 ArrayList<ModeleBase> bases = vp.getRNA().getBasesBetween(i, j);
320                 if (parts.length>1)
321                 {
322                 try
323                 {
324                         String[] options = parts[1].split(",");
325                         for (int k = 0; k < options.length; k++) 
326                         {
327                                 //System.out.println(options[k]);
328                                 try
329                                 {
330                                         String[] data = options[k].split("=");
331                                         String lhs = data[0].toLowerCase();
332                                         String rhs = data[1];
333                                         if (lhs.equals("fill"))
334                                         {
335                                                 fill = VARNAConfigLoader.getSafeColor(rhs, fill);
336                                         }
337                                         else if (lhs.equals("outline"))
338                                         {
339                                                 outline = VARNAConfigLoader.getSafeColor(rhs, outline);
340                                         }
341                                         else if (lhs.equals("radius"))
342                                         {
343                                                 radius = Double.parseDouble(rhs);
344                                         }       
345                                 }
346                                 catch(Exception e)
347                                 {
348                                 }                               
349                         }
350                 }
351                 catch(Exception e)
352                 {
353                         e.printStackTrace();
354                 }
355                 }
356                 return new HighlightRegionAnnotation(bases,fill,outline,radius);
357                 }
358         catch(Exception e)
359         {
360                 e.printStackTrace();
361         }
362         return null;
363         }
364         
365         public String toString()
366         {
367                 //String result = "HighlightRegionAnnotation[";
368                 //result += "fill:"+_fillColor.toString();
369                 //result += ",outline:"+_outlineColor.toString();
370                 //result += ",radius:"+_radius;
371                 //return result+"]";
372                 String result = "Highlighted region "+getMinIndex()+"-"+getMaxIndex();
373                 return result;
374         }
375         
376 }