1 package fr.orsay.lri.varna.models.annotations;
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;
12 import javax.xml.transform.sax.TransformerHandler;
14 import org.xml.sax.SAXException;
15 import org.xml.sax.helpers.AttributesImpl;
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;
27 public class HighlightRegionAnnotation implements Serializable {
29 public static final String HEADER_TEXT = "HighlightRegionAnnotation";
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;
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;
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";
46 public void toXML(TransformerHandler hd) throws SAXException
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);
57 public HighlightRegionAnnotation(RNA r, int startIndex, int stopIndex)
59 this(r.getBasesBetween(startIndex, stopIndex));
62 public HighlightRegionAnnotation()
64 this(new ArrayList<ModeleBase>());
67 public HighlightRegionAnnotation(ArrayList<ModeleBase> b)
69 this(b,DEFAULT_FILL_COLOR,DEFAULT_OUTLINE_COLOR,DEFAULT_RADIUS);
73 public HighlightRegionAnnotation(ArrayList<ModeleBase> b,Color fill, Color outline, double radius)
77 _outlineColor = outline;
81 public HighlightRegionAnnotation clone()
83 return new HighlightRegionAnnotation(_bases,_fillColor,_outlineColor,_radius);
86 public int getMinIndex()
88 int min = Integer.MAX_VALUE;
89 for (ModeleBase mb : _bases)
91 min = Math.min(min, mb.getIndex());
96 public int getMaxIndex()
98 int max = Integer.MIN_VALUE;
99 for (ModeleBase mb : _bases)
101 max = Math.max(max, mb.getIndex());
107 public void setOutlineColor(Color c)
112 public ArrayList<ModeleBase> getBases()
117 public void setBases(ArrayList<ModeleBase> b)
122 public void setFillColor(Color c)
127 public Color getFillColor()
132 public Color getOutlineColor()
134 return _outlineColor;
137 public double getRadius()
142 public void setRadius(double v)
147 public static final int NUM_STEPS_ROUNDED_CORNERS = 16;
149 private Point2D.Double symImage(Point2D.Double p, Point2D.Double center)
151 return new Point2D.Double(2.*center.x-p.x, 2.*center.y-p.y);
154 private LinkedList<Point2D.Double> buildRoundedCorner(Point2D.Double p1, Point2D.Double p2, Point2D.Double anotherPoint)
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);
161 double incr = Math.PI/((double)NUM_STEPS_ROUNDED_CORNERS+1);
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))
167 for(int k=1;k<=NUM_STEPS_ROUNDED_CORNERS;k++)
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);
177 public GeneralPath getShape(Point2D.Double[] realCoords,Point2D.Double[] realCenters, double scaleFactor)
179 GeneralPath p = new GeneralPath();
180 LinkedList<Point2D.Double> pointList = new LinkedList<Point2D.Double>();
181 for (int i = 0;i<getBases().size();i++)
183 int j1 = getBases().get(i).getIndex();
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.);
193 if (i==getBases().size()-1)
196 p0 = new Point2D.Double(p1.x+scaleFactor *getRadius(), p1.y);
197 p2 = new Point2D.Double(p1.x-scaleFactor *getRadius(), p1.y);
203 p0 = symImage(p2, p1);
206 else if (i==getBases().size()-1)
210 p2 = symImage(p0, p1);;
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));
230 double nnorm = (D+h*h/D);
232 double nnormF = nnorm;
233 double nnormB = nnorm;
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);
242 if (pointList.size()>0)
244 Point2D.Double prev1 = pointList.getLast();
245 Point2D.Double prev2 = pointList.getFirst();
247 if ((interForward.distance(prev1)+interBackward.distance(prev2))<(interForward.distance(prev2)+interBackward.distance(prev1)))
249 pointList.addLast(interForward);
250 pointList.addFirst(interBackward);
254 pointList.addFirst(interForward);
255 pointList.addLast(interBackward);
260 pointList.addLast(interForward);
261 pointList.addFirst(interBackward);
265 if (getBases().size()==1)
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);
277 else if (getBases().size()>1)
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);
290 if (pointList.size()>0)
292 Point2D.Double point = pointList.get(0);
293 p.moveTo((float)point.x, (float)point.y);
295 for (int i=1;i<pointList.size();i++)
297 point = pointList.get(i);
298 p.lineTo((float)point.x, (float)point.y);
306 public static HighlightRegionAnnotation parseHighlightRegionAnnotation(String txt, VARNAPanel vp)
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);
324 String[] options = parts[1].split(",");
325 for (int k = 0; k < options.length; k++)
327 //System.out.println(options[k]);
330 String[] data = options[k].split("=");
331 String lhs = data[0].toLowerCase();
332 String rhs = data[1];
333 if (lhs.equals("fill"))
335 fill = VARNAConfigLoader.getSafeColor(rhs, fill);
337 else if (lhs.equals("outline"))
339 outline = VARNAConfigLoader.getSafeColor(rhs, outline);
341 else if (lhs.equals("radius"))
343 radius = Double.parseDouble(rhs);
356 return new HighlightRegionAnnotation(bases,fill,outline,radius);
365 public String toString()
367 //String result = "HighlightRegionAnnotation[";
368 //result += "fill:"+_fillColor.toString();
369 //result += ",outline:"+_outlineColor.toString();
370 //result += ",radius:"+_radius;
372 String result = "Highlighted region "+getMinIndex()+"-"+getMaxIndex();