JAL-3032 adds Java 8 functionality (2/2)
[jalview.git] / src2 / fr / orsay / lri / varna / models / rna / RNA.java
1 /*
2  VARNA is a tool for the automated drawing, visualization and annotation of the secondary structure of RNA, designed as a companion software for web servers and databases.
3  Copyright (C) 2008  Kevin Darty, Alain Denise and Yann Ponty.
4  electronic mail : Yann.Ponty@lri.fr
5  paper mail : LRI, bat 490 Université Paris-Sud 91405 Orsay Cedex France
6
7  This file is part of VARNA version 3.1.
8  VARNA version 3.1 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
9  as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10
11  VARNA version 3.1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  See the GNU General Public License for more details.
14
15  You should have received a copy of the GNU General Public License along with VARNA version 3.1.
16  If not, see http://www.gnu.org/licenses.
17  */
18 package fr.orsay.lri.varna.models.rna;
19
20 import java.awt.Color;
21 import java.awt.Point;
22 import java.awt.geom.GeneralPath;
23 import java.awt.geom.Point2D;
24 import java.awt.geom.Rectangle2D;
25 import java.io.ByteArrayInputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.FileOutputStream;
28 import java.io.FileWriter;
29 import java.io.IOException;
30 import java.io.ObjectInputStream;
31 import java.io.ObjectOutputStream;
32 import java.io.OutputStreamWriter;
33 import java.io.Reader;
34 import java.io.Serializable;
35 import java.io.StreamTokenizer;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Comparator;
40 import java.util.HashSet;
41 import java.util.Hashtable;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.Stack;
45 import java.util.Vector;
46
47 import javax.xml.transform.sax.TransformerHandler;
48
49 import org.xml.sax.SAXException;
50 import org.xml.sax.helpers.AttributesImpl;
51
52 import fr.orsay.lri.varna.VARNAPanel;
53 import fr.orsay.lri.varna.applications.templateEditor.Couple;
54 import fr.orsay.lri.varna.exceptions.ExceptionExportFailed;
55 import fr.orsay.lri.varna.exceptions.ExceptionFileFormatOrSyntax;
56 import fr.orsay.lri.varna.exceptions.ExceptionNAViewAlgorithm;
57 import fr.orsay.lri.varna.exceptions.ExceptionPermissionDenied;
58 import fr.orsay.lri.varna.exceptions.ExceptionUnmatchedClosingParentheses;
59 import fr.orsay.lri.varna.exceptions.ExceptionWritingForbidden;
60 import fr.orsay.lri.varna.factories.RNAFactory;
61 import fr.orsay.lri.varna.interfaces.InterfaceVARNAListener;
62 import fr.orsay.lri.varna.interfaces.InterfaceVARNAObservable;
63 import fr.orsay.lri.varna.models.VARNAConfig;
64 import fr.orsay.lri.varna.models.VARNAConfig.BP_STYLE;
65 import fr.orsay.lri.varna.models.annotations.ChemProbAnnotation;
66 import fr.orsay.lri.varna.models.annotations.HighlightRegionAnnotation;
67 import fr.orsay.lri.varna.models.annotations.TextAnnotation;
68 import fr.orsay.lri.varna.models.export.PSExport;
69 import fr.orsay.lri.varna.models.export.SVGExport;
70 import fr.orsay.lri.varna.models.export.SecStrDrawingProducer;
71 import fr.orsay.lri.varna.models.export.TikzExport;
72 import fr.orsay.lri.varna.models.export.XFIGExport;
73 import fr.orsay.lri.varna.models.naView.NAView;
74 import fr.orsay.lri.varna.models.rna.ModeleBackboneElement.BackboneType;
75 import fr.orsay.lri.varna.models.templates.DrawRNATemplateCurveMethod;
76 import fr.orsay.lri.varna.models.templates.DrawRNATemplateMethod;
77 import fr.orsay.lri.varna.models.templates.RNATemplate;
78 import fr.orsay.lri.varna.models.templates.RNATemplateDrawingAlgorithmException;
79 import fr.orsay.lri.varna.models.templates.RNATemplateMapping;
80 import fr.orsay.lri.varna.utils.RNAMLParser;
81 import fr.orsay.lri.varna.utils.XMLUtils;
82 import fr.orsay.lri.varna.views.VueUI;
83
84 /**
85  * The RNA model which contain the base list and the draw algorithm mode
86  * 
87  * @author darty
88  * 
89  */
90 public class RNA extends InterfaceVARNAObservable implements Serializable {
91         /**
92          * 
93          */
94         private static final long serialVersionUID = 7541274455751497303L;
95
96         /**
97          * Selects the "Feynman diagram" drawing algorithm that places the bases on
98          * a circle and draws the base-pairings as chords of the circle graph.
99          */
100
101         public static final int DRAW_MODE_CIRCULAR = 1;
102         /**
103          * Selects the "tree drawing" algorithm. Draws each loop on a circle whose
104          * radius depends on the number of bases involved in the loop. As some
105          * helices can be overlapping in the result, basic interaction is provided
106          * so that the user can "disentangle" the drawing by spinning the helices
107          * around the axis defined by their multiloop (bulge or internal loop)
108          * origin. This is roughly the initial placement strategy of RNAViz.
109          * 
110          * @see <a href="http://rnaviz.sourceforge.net/">RNAViz</a>
111          */
112         public static final int DRAW_MODE_RADIATE = 2;
113
114         /**
115          * Selects the NAView algorithm.
116          */
117         public static final int DRAW_MODE_NAVIEW = 3;
118         /**
119          * Selects the linear algorithm.
120          */
121         public static final int DRAW_MODE_LINEAR = 4;
122
123         public static final int DRAW_MODE_VARNA_VIEW = 5;
124
125         /**
126          * Selects the RNAView algorithm.
127          */
128         public static final int DRAW_MODE_MOTIFVIEW = 6;
129
130         public static final int DRAW_MODE_TEMPLATE = 7;
131
132         public static final int DEFAULT_DRAW_MODE = DRAW_MODE_RADIATE;
133
134         public int BASE_RADIUS = 10;
135         public static final double LOOP_DISTANCE = 40.0; // distance between base
136                                                                                                                 // pairs in an helix
137         public static final double BASE_PAIR_DISTANCE = 65.0; // distance between
138                                                                                                                         // the two bases of
139                                                                                                                         // a pair
140         public static final double MULTILOOP_DISTANCE = 35.0;
141         public static final double VIRTUAL_LOOP_RADIUS = 40.0;
142
143         public double CHEM_PROB_DIST = 14;
144         public double CHEM_PROB_BASE_LENGTH = 30;
145         public double CHEM_PROB_ARROW_HEIGHT = 10;
146         public double CHEM_PROB_ARROW_WIDTH = 5;
147         public double CHEM_PROB_TRIANGLE_WIDTH = 2.5;
148         public double CHEM_PROB_PIN_SEMIDIAG = 6;
149         public double CHEM_PROB_DOT_RADIUS = 6.;
150         public static double CHEM_PROB_ARROW_THICKNESS = 2.0;
151
152         public static ArrayList<String> NormalBases = new ArrayList<String>();
153         {
154                 NormalBases.add("a");
155                 NormalBases.add("c");
156                 NormalBases.add("g");
157                 NormalBases.add("u");
158                 NormalBases.add("t");
159         }
160
161         public GeneralPath _debugShape = null;
162
163         /**
164          * The draw algorithm mode
165          */
166         private int _drawMode = DRAW_MODE_RADIATE;
167         private boolean _drawn = false;
168         private String _name = "";
169         private String _id = "";
170         public double _bpHeightIncrement = VARNAConfig.DEFAULT_BP_INCREMENT;
171         /**
172          * the base list
173          */
174         private ArrayList<ModeleBase> _listeBases;
175         /**
176          * the strand list
177          */
178         StructureTemp _listStrands = new StructureTemp();
179         /**
180          * Additional bonds and info can be specified here.
181          */
182         private ArrayList<ModeleBP> _structureAux = new ArrayList<ModeleBP>();
183         private ArrayList<TextAnnotation> _listeAnnotations = new ArrayList<TextAnnotation>();
184         private ArrayList<HighlightRegionAnnotation> _listeRegionHighlights = new ArrayList<HighlightRegionAnnotation>();
185         private ArrayList<ChemProbAnnotation> _chemProbAnnotations = new ArrayList<ChemProbAnnotation>();
186         private ModeleBackbone _backbone = new ModeleBackbone();
187
188         public static String XML_ELEMENT_NAME = "RNA";
189         public static String XML_VAR_BASE_SPACING_NAME = "spacing";
190         public static String XML_VAR_DRAWN_NAME = "drawn";
191         public static String XML_VAR_NAME_NAME = "name";
192         public static String XML_VAR_DRAWN_MODE_NAME = "mode";
193         public static String XML_VAR_ID_NAME = "id";
194         public static String XML_VAR_BP_HEIGHT_NAME = "delta";
195         public static String XML_VAR_BASES_NAME = "bases";
196         public static String XML_VAR_BASEPAIRS_NAME = "BPs";
197         public static String XML_VAR_ANNOTATIONS_NAME = "annotations";
198         public static String XML_VAR_BACKBONE_NAME = "backbone";
199
200         public void toXML(TransformerHandler hd) throws SAXException {
201                 AttributesImpl atts = new AttributesImpl();
202                 atts.addAttribute("", "", XML_VAR_DRAWN_NAME, "CDATA", "" + _drawn);
203                 atts.addAttribute("", "", XML_VAR_DRAWN_MODE_NAME, "CDATA", ""
204                                 + _drawMode);
205                 atts.addAttribute("", "", XML_VAR_ID_NAME, "CDATA", "" + _id);
206                 atts.addAttribute("", "", XML_VAR_BP_HEIGHT_NAME, "CDATA", ""
207                                 + _bpHeightIncrement);
208                 hd.startElement("", "", XML_ELEMENT_NAME, atts);
209
210                 atts.clear();
211                 hd.startElement("", "", XML_VAR_NAME_NAME, atts);
212                 XMLUtils.exportCDATAString(hd, "" + _name);
213                 hd.endElement("", "", XML_VAR_NAME_NAME);
214
215                 atts.clear();
216                 hd.startElement("", "", XML_VAR_BASES_NAME, atts);
217                 for (ModeleBase mb : _listeBases) {
218                         mb.toXML(hd);
219                 }
220                 hd.endElement("", "", XML_VAR_BASES_NAME);
221                 atts.clear();
222
223                 hd.startElement("", "", XML_VAR_BASEPAIRS_NAME, atts);
224                 for (ModeleBP mbp : getSecStrBPs()) {
225                         mbp.toXML(hd, true);
226                 }
227                 for (ModeleBP mbp : _structureAux) {
228                         mbp.toXML(hd, false);
229                 }
230                 hd.endElement("", "", XML_VAR_BASEPAIRS_NAME);
231                 atts.clear();
232
233                 getBackbone().toXML(hd);
234                 atts.clear();
235
236                 hd.startElement("", "", XML_VAR_ANNOTATIONS_NAME, atts);
237                 for (TextAnnotation ta : _listeAnnotations) {
238                         ta.toXML(hd);
239                 }
240                 for (HighlightRegionAnnotation hra : _listeRegionHighlights) {
241                         hra.toXML(hd);
242                 }
243                 for (ChemProbAnnotation cpa : _chemProbAnnotations) {
244                         cpa.toXML(hd);
245                 }
246                 hd.endElement("", "", XML_VAR_ANNOTATIONS_NAME);
247                 hd.endElement("", "", XML_ELEMENT_NAME);
248         }
249
250         public ModeleBackbone getBackbone() {
251                 return _backbone;
252         }
253
254         public void setBackbone(ModeleBackbone b) {
255                 _backbone = b;
256         }
257
258         transient private ArrayList<InterfaceVARNAListener> _listeVARNAListener = new ArrayList<InterfaceVARNAListener>();
259
260         public RNA() {
261                 this("");
262         }
263
264         public RNA(String name) {
265                 _name = name;
266                 _listeBases = new ArrayList<ModeleBase>();
267                 _drawn = false;
268                 init();
269         }
270
271         public String toString() {
272                 if (_name.equals("")) {
273                         return getStructDBN();
274                 } else {
275                         return _name;
276                 }
277         }
278
279         public RNA(RNA r) {
280                 _drawMode = r._drawMode;
281                 _listeBases.addAll(r._listeBases);
282                 _listeVARNAListener = (ArrayList<InterfaceVARNAListener>) r._listeVARNAListener;
283                 _drawn = r._drawn;
284                 init();
285         }
286
287         public void init() {
288         }
289
290         public void saveRNADBN(String path, String title)
291                         throws ExceptionWritingForbidden {
292                 try {
293                         FileWriter out = new FileWriter(path);
294                         if (!title.equals("")) {
295                                 out.write("> " + title + "\n");
296                         }
297                         out.write(getListeBasesToString());
298                         out.write('\n');
299                         String str = "";
300                         for (int i = 0; i < _listeBases.size(); i++) {
301                                 if (_listeBases.get(i).getElementStructure() == -1) {
302                                         str += '.';
303                                 } else {
304                                         if (_listeBases.get(i).getElementStructure() > i) {
305                                                 str += '(';
306                                         } else {
307                                                 str += ')';
308                                         }
309                                 }
310                         }
311                         out.write(str);
312                         out.write('\n');
313                         out.close();
314                 } catch (IOException e) {
315                         throw new ExceptionWritingForbidden(e.getMessage());
316                 }
317         }
318
319         public Color getBaseInnerColor(int i, VARNAConfig conf) {
320                 Color result = _listeBases.get(i).getStyleBase().getBaseInnerColor();
321                 String res = _listeBases.get(i).getContent();
322                 if (conf._drawColorMap) {
323                         result = conf._cm.getColorForValue(_listeBases.get(i).getValue());
324                 } else if ((conf._colorDashBases && (res.contains("-")))) {
325                         result = conf._dashBasesColor;
326                 } else if ((conf._colorSpecialBases && !NormalBases.contains(res
327                                 .toLowerCase()))) {
328                         result = conf._specialBasesColor;
329                 }
330                 return result;
331         }
332
333         public Color getBaseOuterColor(int i, VARNAConfig conf) {
334                 Color result = _listeBases.get(i).getStyleBase()
335                                 .getBaseOutlineColor();
336                 return result;
337         }
338
339         private static double correctComponent(double c)
340         {
341             c = c / 255.0;
342             if (c <= 0.03928) 
343                 c = c/12.92;
344             else 
345                 c = Math.pow(((c+0.055)/1.055) , 2.4);
346             return c;
347         }
348         public static double getLuminance(Color c)
349         {
350                 return 0.2126 * correctComponent(c.getRed()) + 0.7152 * correctComponent(c.getGreen()) + 0.0722 * correctComponent(c.getBlue());
351         }
352         
353         public static boolean whiteLabelPreferrable(Color c)
354         {
355                 if (getLuminance(c) > 0.32)
356                         return false;
357                 return true;
358         }
359         
360
361         
362         public Color getBaseNameColor(int i, VARNAConfig conf) {
363                 Color result = _listeBases.get(i).getStyleBase().getBaseNameColor();
364                 if ( RNA.whiteLabelPreferrable(getBaseInnerColor(i, conf)))
365                 {
366                         result=Color.white;
367                 }
368
369                 return result;
370         }
371
372         public Color getBasePairColor(ModeleBP bp, VARNAConfig conf) {
373                 Color bondColor = conf._bondColor;
374                 if (conf._useBaseColorsForBPs) {
375                         bondColor = _listeBases.get(bp.getPartner5().getIndex())
376                                         .getStyleBase().getBaseInnerColor();
377                 }
378                 if (bp != null) {
379                         bondColor = bp.getStyle().getColor(bondColor);
380                 }
381                 return bondColor;
382         }
383
384         public double getBasePairThickness(ModeleBP bp, VARNAConfig conf) {
385                 double thickness = bp.getStyle().getThickness(conf._bpThickness);
386                 return thickness;
387         }
388
389         private void drawSymbol(SecStrDrawingProducer out, double posx,
390                         double posy, double normx, double normy, double radius,
391                         boolean isCIS, ModeleBP.Edge e, double thickness) {
392                 Color bck = out.getCurrentColor();
393                 switch (e) {
394                 case WC:
395                         if (isCIS) {
396                                 out.fillCircle(posx, posy, (radius / 2.0), thickness, bck);
397                         } else {
398                                 out.fillCircle(posx, posy, (radius / 2.0), thickness,
399                                                 Color.white);
400                                 out.setColor(bck);
401                                 out.drawCircle(posx, posy, (radius / 2.0), thickness);
402                         }
403                         break;
404                 case HOOGSTEEN: {
405                         double xtab[] = new double[4];
406                         double ytab[] = new double[4];
407                         xtab[0] = posx - radius * normx / 2.0 - radius * normy / 2.0;
408                         ytab[0] = posy - radius * normy / 2.0 + radius * normx / 2.0;
409                         xtab[1] = posx + radius * normx / 2.0 - radius * normy / 2.0;
410                         ytab[1] = posy + radius * normy / 2.0 + radius * normx / 2.0;
411                         xtab[2] = posx + radius * normx / 2.0 + radius * normy / 2.0;
412                         ytab[2] = posy + radius * normy / 2.0 - radius * normx / 2.0;
413                         xtab[3] = posx - radius * normx / 2.0 + radius * normy / 2.0;
414                         ytab[3] = posy - radius * normy / 2.0 - radius * normx / 2.0;
415                         if (isCIS) {
416                                 out.fillPolygon(xtab, ytab, bck);
417                         } else {
418                                 out.fillPolygon(xtab, ytab, Color.white);
419                                 out.setColor(bck);
420                                 out.drawPolygon(xtab, ytab, thickness);
421                         }
422                 }
423                         break;
424                 case SUGAR: {
425                         double ix = radius * normx / 2.0;
426                         double iy = radius * normy / 2.0;
427                         double jx = radius * normy / 2.0;
428                         double jy = -radius * normx / 2.0;
429                         double xtab[] = new double[3];
430                         double ytab[] = new double[3];
431                         xtab[0] = posx - ix + jx;
432                         ytab[0] = posy - iy + jy;
433                         xtab[1] = posx + ix + jx;
434                         ytab[1] = posy + iy + jy;
435                         xtab[2] = posx - jx;
436                         ytab[2] = posy - jy;
437
438                         if (isCIS) {
439                                 out.fillPolygon(xtab, ytab, bck);
440                         } else {
441                                 out.fillPolygon(xtab, ytab, Color.white);
442                                 out.setColor(bck);
443                                 out.drawPolygon(xtab, ytab, thickness);
444                         }
445                 }
446                         break;
447                 }
448                 out.setColor(bck);
449         }
450
451         private void drawBasePairArc(SecStrDrawingProducer out, int i, int j,
452                         Point2D.Double orig, Point2D.Double dest, ModeleBP style,
453                         VARNAConfig conf) {
454                 double coef;
455                 double distance;
456                 Point2D.Double center = new Point2D.Double((orig.x + dest.x)/2., (orig.y + dest.y)/2. + BASE_RADIUS); 
457                 if (j - i == 1)
458                         coef = _bpHeightIncrement * 2;
459                 else
460                         coef = _bpHeightIncrement * 1;
461                 distance = (int) Math.round(dest.x - orig.x);
462                 if (conf._mainBPStyle != BP_STYLE.LW) {
463                         out.drawArc(center, distance, distance * coef, 180, 0);
464                 } else {
465                         double thickness = getBasePairThickness(style, conf);
466                         double radiusCircle = ((BASE_PAIR_DISTANCE - BASE_RADIUS) / 5.0);
467
468                         if (style.isCanonical()) {
469                                 if (style.isCanonicalGC()) {
470                                         if ((orig.x != dest.x) || (orig.y != dest.y)) {
471                                                 out.drawArc(center, distance - BASE_RADIUS / 2.,
472                                                                 distance * coef - BASE_RADIUS / 2, 180, 0);
473                                                 out.drawArc(center, distance + BASE_RADIUS / 2.,
474                                                                 distance * coef + BASE_RADIUS / 2, 180, 0);
475                                         }
476                                 } else if (!style.isWobbleUG()) {
477                                         out.drawArc(center, distance, distance * coef, 180, 0);
478                                         drawSymbol(out, center.x, center.y + distance * coef / 2., 180., 0,
479                                                         radiusCircle, style.isCIS(),
480                                                         style.getEdgePartner5(), thickness);
481                                 } else {
482                                         out.drawArc(orig, distance, distance * coef, 180, 0);
483                                 }
484                         } else {
485                                 ModeleBP.Edge p1 = style.getEdgePartner5();
486                                 ModeleBP.Edge p2 = style.getEdgePartner3();
487                                 out.drawArc(center, distance, distance * coef, 180, 0);
488                                 if (p1 == p2) {
489                                         drawSymbol(out, center.x, center.y + distance * coef / 2., 1., 0,
490                                                         radiusCircle, style.isCIS(),
491                                                         style.getEdgePartner5(), thickness);
492                                 } else {
493                                         drawSymbol(out, center.x - BASE_RADIUS,
494                                                         center.y + distance * coef / 2., 1., 0, radiusCircle,
495                                                         style.isCIS(), p1, thickness);
496                                         drawSymbol(out, center.x + BASE_RADIUS,
497                                                         center.y + distance * coef / 2., 1., 0, radiusCircle,
498                                                         style.isCIS(), p2, thickness);
499                                 }
500                         }
501                 }
502         }
503
504         private void drawBasePair(SecStrDrawingProducer out, Point2D.Double orig,
505                         Point2D.Double dest, ModeleBP style, VARNAConfig conf) {
506                 double dx = dest.x - orig.x;
507                 double dy = dest.y - orig.y;
508                 double dist = Math.sqrt((dest.x - orig.x) * (dest.x - orig.x)
509                                 + (dest.y - orig.y) * (dest.y - orig.y));
510                 dx /= dist;
511                 dy /= dist;
512                 double nx = -dy;
513                 double ny = dx;
514                 orig = new Point2D.Double(orig.x + BASE_RADIUS * dx, orig.y
515                                 + BASE_RADIUS * dy);
516                 dest = new Point2D.Double(dest.x - BASE_RADIUS * dx, dest.y
517                                 - BASE_RADIUS * dy);
518                 if (conf._mainBPStyle == VARNAConfig.BP_STYLE.LW) {
519                         double thickness = getBasePairThickness(style, conf);
520                         double radiusCircle = ((BASE_PAIR_DISTANCE - BASE_RADIUS) / 5.0);
521
522                         if (style.isCanonical()) {
523                                 if (style.isCanonicalGC()) {
524                                         if ((orig.x != dest.x) || (orig.y != dest.y)) {
525                                                 nx *= BASE_RADIUS / 4.0;
526                                                 ny *= BASE_RADIUS / 4.0;
527                                                 out.drawLine((orig.x + nx), (orig.y + ny),
528                                                                 (dest.x + nx), (dest.y + ny), conf._bpThickness);
529                                                 out.drawLine((orig.x - nx), (orig.y - ny),
530                                                                 (dest.x - nx), (dest.y - ny), conf._bpThickness);
531                                         }
532                                 } else if (style.isCanonicalAU()) {
533                                         out.drawLine(orig.x, orig.y, dest.x, dest.y,
534                                                         conf._bpThickness);
535                                 } else if (style.isWobbleUG()) {
536                                         double cx = (dest.x + orig.x) / 2.0;
537                                         double cy = (dest.y + orig.y) / 2.0;
538                                         out.drawLine(orig.x, orig.y, dest.x, dest.y,
539                                                         conf._bpThickness);
540                                         drawSymbol(out, cx, cy, nx, ny, radiusCircle, false,
541                                                         ModeleBP.Edge.WC, thickness);
542                                 }
543
544                                 else {
545                                         double cx = (dest.x + orig.x) / 2.0;
546                                         double cy = (dest.y + orig.y) / 2.0;
547                                         out.drawLine(orig.x, orig.y, dest.x, dest.y,
548                                                         conf._bpThickness);
549                                         drawSymbol(out, cx, cy, nx, ny, radiusCircle,
550                                                         style.isCIS(), style.getEdgePartner5(), thickness);
551                                 }
552                         } else {
553                                 ModeleBP.Edge p1 = style.getEdgePartner5();
554                                 ModeleBP.Edge p2 = style.getEdgePartner3();
555                                 double cx = (dest.x + orig.x) / 2.0;
556                                 double cy = (dest.y + orig.y) / 2.0;
557                                 out.drawLine(orig.x, orig.y, dest.x, dest.y, conf._bpThickness);
558                                 if (p1 == p2) {
559                                         drawSymbol(out, cx, cy, nx, ny, radiusCircle,
560                                                         style.isCIS(), p1, thickness);
561                                 } else {
562                                         double vdx = (dest.x - orig.x);
563                                         double vdy = (dest.y - orig.y);
564                                         vdx /= 6.0;
565                                         vdy /= 6.0;
566                                         drawSymbol(out, cx + vdx, cy + vdy, nx, ny, radiusCircle,
567                                                         style.isCIS(), p2, thickness);
568                                         drawSymbol(out, cx - vdx, cy - vdy, nx, ny, radiusCircle,
569                                                         style.isCIS(), p1, thickness);
570                                 }
571                         }
572                 } else if (conf._mainBPStyle == VARNAConfig.BP_STYLE.RNAVIZ) {
573                         double xcenter = (orig.x + dest.x) / 2.0;
574                         double ycenter = (orig.y + dest.y) / 2.0;
575                         out.fillCircle(xcenter, ycenter, 3.0 * conf._bpThickness,
576                                         conf._bpThickness, out.getCurrentColor());
577                 } else if (conf._mainBPStyle == VARNAConfig.BP_STYLE.SIMPLE) {
578                         out.drawLine(orig.x, orig.y, dest.x, dest.y, conf._bpThickness);
579                 }
580         }
581
582         private void drawColorMap(VARNAConfig _conf, SecStrDrawingProducer out) {
583                 double v1 = _conf._cm.getMinValue();
584                 double v2 = _conf._cm.getMaxValue();
585                 int x, y;
586                 double xSpaceAvail = 0;
587                 double ySpaceAvail = 0;
588                 double thickness = 1.0;
589                 /*
590                  * ySpaceAvail =
591                  * Math.min((getHeight()-rnabbox.height*scaleFactor-getTitleHeight
592                  * ())/2.0,scaleFactor*(_conf._colorMapHeight+VARNAConfig.
593                  * DEFAULT_COLOR_MAP_FONT_SIZE)); if ((int)ySpaceAvail==0) { xSpaceAvail
594                  * =
595                  * Math.min((getWidth()-rnabbox.width*scaleFactor)/2,scaleFactor*(_conf
596                  * ._colorMapWidth)+VARNAConfig.DEFAULT_COLOR_MAP_STRIPE_WIDTH); }
597                  */
598                 Rectangle2D.Double currentBBox = out.getBoundingBox();
599
600                 double xBase = (currentBBox.getMaxX() - _conf._colorMapWidth - _conf._colorMapXOffset);
601                 // double yBase = (minY - _conf._colorMapHeight +
602                 // _conf._colorMapYOffset);
603                 double yBase = (currentBBox.getMinY() - _conf._colorMapHeight - VARNAConfig.DEFAULT_COLOR_MAP_FONT_SIZE);
604
605                 for (int i = 0; i < _conf._colorMapWidth; i++) {
606                         double ratio = (((double) i) / ((double) _conf._colorMapWidth - 1));
607                         double val = v1 + (v2 - v1) * ratio;
608                         Color c = _conf._cm.getColorForValue(val);
609                         x = (int) (xBase + i);
610                         y = (int) yBase;
611                         out.fillRectangle(x, y, VARNAConfig.DEFAULT_COLOR_MAP_STRIPE_WIDTH,
612                                         _conf._colorMapHeight, c);
613                 }
614                 out.setColor(VARNAConfig.DEFAULT_COLOR_MAP_OUTLINE);
615                 out.drawRectangle(xBase, yBase, (double) _conf._colorMapWidth
616                                 + VARNAConfig.DEFAULT_COLOR_MAP_STRIPE_WIDTH - 1,
617                                 _conf._colorMapHeight, thickness);
618
619                 out.setColor(VARNAConfig.DEFAULT_COLOR_MAP_FONT_COLOR);
620                 out.setFont(out.getCurrentFont(),
621                                 VARNAConfig.DEFAULT_COLOR_MAP_FONT_SIZE / 1.5);
622                 out.drawText(xBase, yBase + _conf._colorMapHeight
623                                 + VARNAConfig.DEFAULT_COLOR_MAP_FONT_SIZE / 1.7,
624                                 "" + _conf._cm.getMinValue());
625                 out.drawText(xBase + VARNAConfig.DEFAULT_COLOR_MAP_STRIPE_WIDTH
626                                 + _conf._colorMapWidth, yBase + _conf._colorMapHeight
627                                 + VARNAConfig.DEFAULT_COLOR_MAP_FONT_SIZE / 1.7,
628                                 "" + _conf._cm.getMaxValue());
629                 out.drawText(
630                                 xBase
631                                                 + (VARNAConfig.DEFAULT_COLOR_MAP_STRIPE_WIDTH + _conf._colorMapWidth)
632                                                 / 2.0, yBase
633                                                 - (VARNAConfig.DEFAULT_COLOR_MAP_FONT_SIZE / 1.7),
634                                 _conf._colorMapCaption);
635
636         }
637
638         private void renderRegionHighlights(SecStrDrawingProducer out,
639                         Point2D.Double[] realCoords, Point2D.Double[] realCenters) {
640                 for (HighlightRegionAnnotation r : _listeRegionHighlights) {
641                         GeneralPath s = r.getShape(realCoords, realCenters, 1.0);
642                         out.setColor(r.getFillColor());
643                         out.fillPolygon(s, r.getFillColor());
644                         out.setColor(r.getOutlineColor());
645                         out.drawPolygon(s, 1l);
646                 }
647
648         }
649
650         private void saveRNA(String path, VARNAConfig conf, double scale,
651                         SecStrDrawingProducer out) throws ExceptionWritingForbidden {
652                 out.setScale(scale);
653                 // Computing bounding boxes
654                 double EPSMargin = 40;
655                 double minX = Double.MAX_VALUE;
656                 double maxX = Double.MIN_VALUE;
657                 double minY = Double.MAX_VALUE;
658                 double maxY = Double.MIN_VALUE;
659
660                 double x0, y0, x1, y1, xc, yc, xp, yp, dx, dy, norm;
661
662                 for (int i = 0; i < _listeBases.size(); i++) {
663                         minX = Math.min(minX, (_listeBases.get(i).getCoords().getX()
664                                         - BASE_RADIUS - EPSMargin));
665                         minY = Math.min(minY, -(_listeBases.get(i).getCoords().getY()
666                                         - BASE_RADIUS - EPSMargin));
667                         maxX = Math.max(maxX, (_listeBases.get(i).getCoords().getX()
668                                         + BASE_RADIUS + EPSMargin));
669                         maxY = Math.max(maxY, -(_listeBases.get(i).getCoords().getY()
670                                         + BASE_RADIUS + EPSMargin));
671                 }
672
673                 // Rescaling everything
674                 Point2D.Double[] coords = new Point2D.Double[_listeBases.size()];
675                 Point2D.Double[] centers = new Point2D.Double[_listeBases.size()];
676                 for (int i = 0; i < _listeBases.size(); i++) {
677                         xp = (_listeBases.get(i).getCoords().getX() - minX);
678                         yp = -(_listeBases.get(i).getCoords().getY() - minY);
679                         coords[i] = new Point2D.Double(xp, yp);
680
681                         Point2D.Double centerBck = getCenter(i);
682                         if (get_drawMode() == RNA.DRAW_MODE_NAVIEW
683                                         || get_drawMode() == RNA.DRAW_MODE_RADIATE) {
684                                 if ((_listeBases.get(i).getElementStructure() != -1)
685                                                 && i < _listeBases.size() - 1 && i > 1) {
686                                         ModeleBase b1 = get_listeBases().get(i - 1);
687                                         ModeleBase b2 = get_listeBases().get(i + 1);
688                                         int j1 = b1.getElementStructure();
689                                         int j2 = b2.getElementStructure();
690                                         if ((j1 == -1) ^ (j2 == -1)) {
691                                                 // alors la position du nombre associé doit etre
692                                                 // décalé
693                                                 Point2D.Double a1 = b1.getCoords();
694                                                 Point2D.Double a2 = b2.getCoords();
695                                                 Point2D.Double c1 = b1.getCenter();
696                                                 Point2D.Double c2 = b2.getCenter();
697
698                                                 centerBck.x = _listeBases.get(i).getCoords().x
699                                                                 + (c1.x - a1.x) / c1.distance(a1)
700                                                                 + (c2.x - a2.x) / c2.distance(a2);
701                                                 centerBck.y = _listeBases.get(i).getCoords().y
702                                                                 + (c1.y - a1.y) / c1.distance(a1)
703                                                                 + (c2.y - a2.y) / c2.distance(a2);
704                                         }
705                                 }
706                         }
707                         xc = (centerBck.getX() - minX);
708                         yc = -(centerBck.getY() - minY);
709                         centers[i] = new Point2D.Double(xc, yc);
710                 }
711
712                 // Drawing background
713                 if (conf._drawBackground)
714                         out.setBackgroundColor(conf._backgroundColor);
715
716                 // Drawing region highlights
717                 renderRegionHighlights(out, coords, centers);
718
719                 // Drawing backbone
720                 if (conf._drawBackbone)
721                 {
722                         for (int i = 1; i < _listeBases.size(); i++) {
723                                 Point2D.Double p1 = coords[i - 1];
724                                 Point2D.Double p2 = coords[i];
725                                 x0 = p1.x;
726                                 y0 = p1.y;
727                                 x1 = p2.x;
728                                 y1 = p2.y;
729                                 Point2D.Double vn = new Point2D.Double();
730                                 double dist = p1.distance(p2);
731                                 int a = _listeBases.get(i - 1).getElementStructure();
732                                 int b = _listeBases.get(i).getElementStructure();
733                                 BackboneType bt = _backbone.getTypeBefore(i);
734                                 boolean consecutivePair = (a == i) && (b == i - 1);
735         
736                                 if (dist > 0) {
737                                         if (bt != BackboneType.DISCONTINUOUS_TYPE) {
738                                                 Color c = _backbone.getColorBefore(i, conf._backboneColor);
739                                                 if (bt == BackboneType.MISSING_PART_TYPE) {
740                                                         c.brighter();
741                                                 }
742                                                 out.setColor(c);
743         
744                                                 vn.x = (x1 - x0) / dist;
745                                                 vn.y = (y1 - y0) / dist;
746                                                 if (consecutivePair
747                                                                 &&(getDrawMode() != RNA.DRAW_MODE_LINEAR)
748                                                                 && (getDrawMode() != RNA.DRAW_MODE_CIRCULAR)) {
749                                                         int dir = 0;
750                                                         if (i + 1 < coords.length) {
751                                                                 dir = (testDirectionality(i - 1, i, i + 1) ? 1 : -1);
752                                                         } else if (i - 2 >= 0) {
753                                                                 dir = (testDirectionality(i - 2, i - 1, i) ? 1 : -1);
754                                                         }
755                                                         Point2D.Double centerSeg = new Point2D.Double(
756                                                                         (p1.x + p2.x) / 2.0, (p1.y + p2.y) / 2.0);
757                                                         double centerDist = RNA.VIRTUAL_LOOP_RADIUS * scale;
758                                                         Point2D.Double centerLoop = new Point2D.Double(
759                                                                         centerSeg.x + centerDist * dir * vn.y,
760                                                                         centerSeg.y - centerDist * dir * vn.x);
761                                                         // Debug crosshair
762                                                         //out.drawLine(centerLoop.x - 5, centerLoop.y,
763                                                         //              centerLoop.x + 5, centerLoop.y, 2.0);
764                                                         //out.drawLine(centerLoop.x, centerLoop.y - 5,
765                                                         //              centerLoop.x, centerLoop.y + 5, 2.0);
766                                                         
767                                                         double radius = centerLoop.distance(p1);
768                                                         double a1 = 360.
769                                                                         * (Math.atan2(p1.y - centerLoop.y, p1.x - centerLoop.x))
770                                                                         / (2. * Math.PI);
771                                                         double a2 = 360.
772                                                                         * (Math.atan2(p2.y - centerLoop.y, p2.x - centerLoop.x))
773                                                                         / (2. * Math.PI);
774                                                         if (dir>0)
775                                                         {
776                                                                 double tmp = a1;
777                                                                 a1 = a2;
778                                                                 a2 = tmp;
779                                                         }
780                                                         if (a1 < 0) {
781                                                                 a1 += 360.;
782                                                         }
783                                                         if (a2 < 0) {
784                                                                 a2 += 360.;
785                                                         }
786                                                         out.drawArc(centerLoop, 2. * radius, 2. * radius, a1,
787                                                                         a2);
788                                                 } else {
789                                                         out.drawLine((x0 + BASE_RADIUS * vn.x),
790                                                                         (y0 + BASE_RADIUS * vn.y), (x1 - BASE_RADIUS
791                                                                                         * vn.x), (y1 - BASE_RADIUS * vn.y), 1.0);
792                                                 }
793                                         }
794                                 }
795                         }
796                 }
797
798                 // Drawing bonds
799                 for (int i = 0; i < _listeBases.size(); i++) {
800                         if (_listeBases.get(i).getElementStructure() > i) {
801                                 ModeleBP style = _listeBases.get(i).getStyleBP();
802                                 if (style.isCanonical() || conf._drawnNonCanonicalBP) {
803                                         Color bpcol = getBasePairColor(style, conf);
804                                         out.setColor(bpcol);
805
806                                         int j = _listeBases.get(i).getElementStructure();
807                                         x0 = coords[i].x;
808                                         y0 = coords[i].y;
809                                         x1 = coords[j].x;
810                                         y1 = coords[j].y;
811                                         dx = x1 - x0;
812                                         dy = y1 - y0;
813                                         norm = Math.sqrt(dx * dx + dy * dy);
814                                         dx /= norm;
815                                         dy /= norm;
816
817                                         if (_drawMode == DRAW_MODE_CIRCULAR
818                                                         || _drawMode == DRAW_MODE_RADIATE
819                                                         || _drawMode == DRAW_MODE_NAVIEW) {
820                                                 drawBasePair(out, new Point2D.Double(x0, y0),
821                                                                 new Point2D.Double(x1, y1), style, conf);
822                                         } else if (_drawMode == DRAW_MODE_LINEAR) {
823                                                 drawBasePairArc(out, i, j, new Point2D.Double(x0, y0),
824                                                                 new Point2D.Double(x1, y1), style, conf);
825                                         }
826                                 }
827                         }
828                 }
829
830                 // Drawing additional bonds
831                 if (conf._drawnNonPlanarBP) {
832                         for (int i = 0; i < _structureAux.size(); i++) {
833                                 ModeleBP bp = _structureAux.get(i);
834                                 out.setColor(getBasePairColor(bp, conf));
835
836                                 int a = bp.getPartner5().getIndex();
837                                 int b = bp.getPartner3().getIndex();
838
839                                 if (bp.isCanonical() || conf._drawnNonCanonicalBP) {
840                                         x0 = coords[a].x;
841                                         y0 = coords[a].y;
842                                         x1 = coords[b].x;
843                                         y1 = coords[b].y;
844                                         dx = x1 - x0;
845                                         dy = y1 - y0;
846                                         norm = Math.sqrt(dx * dx + dy * dy);
847                                         dx /= norm;
848                                         dy /= norm;
849                                         if ((_drawMode == DRAW_MODE_CIRCULAR)
850                                                         || (_drawMode == DRAW_MODE_RADIATE)
851                                                         || _drawMode == DRAW_MODE_NAVIEW) {
852                                                 drawBasePair(out, new Point2D.Double(x0, y0),
853                                                                 new Point2D.Double(x1, y1), bp, conf);
854                                         } else if (_drawMode == DRAW_MODE_LINEAR) {
855                                                 drawBasePairArc(out, a, b, new Point2D.Double(x0, y0),
856                                                                 new Point2D.Double(x1, y1), bp, conf);
857                                         }
858                                 }
859                         }
860                 }
861
862                 // Drawing Bases
863                 double baseFontSize = (1.5 * BASE_RADIUS);
864                 out.setFont(PSExport.FONT_HELVETICA_BOLD, baseFontSize);
865                 
866                 for (int i = 0; i < _listeBases.size(); i++) {
867                         x0 = coords[i].x;
868                         y0 = coords[i].y;
869                         
870                         Color baseInnerColor = getBaseInnerColor(i, conf);
871                         Color baseOuterColor = getBaseOuterColor(i, conf);
872                         Color baseNameColor = getBaseNameColor(i, conf);
873                         if ( RNA.whiteLabelPreferrable(baseInnerColor))
874                         {
875                                 baseNameColor=Color.white;
876                         }
877
878
879                         if (_listeBases.get(i) instanceof ModeleBasesComparison) {
880                                 ModeleBasesComparison mb = (ModeleBasesComparison) _listeBases
881                                                 .get(i);
882                                 if (conf._fillBases) {
883                                         out.fillRectangle(x0 - 1.5 * BASE_RADIUS, y0 - BASE_RADIUS,
884                                                         3 * BASE_RADIUS, 2 * BASE_RADIUS,
885                                                         baseInnerColor);
886                                 }
887                                 if (conf._drawOutlineBases) {
888                                         out.setColor(baseOuterColor);
889                                         out.drawRectangle(x0 - 1.5 * BASE_RADIUS, y0 - BASE_RADIUS,
890                                                         3 * BASE_RADIUS, 2 * BASE_RADIUS, 1l);
891                                         out.drawLine(x0, y0 - BASE_RADIUS, x0, y0 + BASE_RADIUS, 1l);
892                                 }
893                                 
894                                 out.setColor(baseNameColor);
895                                 out.drawText(x0 - .75 * BASE_RADIUS, y0, "" + mb.getBase1());
896                                 out.drawText(x0 + .75 * BASE_RADIUS, y0, "" + mb.getBase2());
897                         } else if (_listeBases.get(i) instanceof ModeleBaseNucleotide) {
898                                 if (conf._fillBases) {
899                                         out.fillCircle(x0, y0, BASE_RADIUS, 1l,
900                                                         baseInnerColor);
901                                 }
902                                 if (conf._drawOutlineBases) {
903                                         out.setColor(baseOuterColor);
904                                         out.drawCircle(x0, y0, BASE_RADIUS, 1l);
905                                 }
906                                 out.setColor(baseNameColor);
907                                 out.drawText(x0, y0, _listeBases.get(i).getContent());
908
909                         }
910                 }
911
912                 // Drawing base numbers
913                 double numFontSize = (double) (1.5 * BASE_RADIUS);
914                 out.setFont(PSExport.FONT_HELVETICA_BOLD, numFontSize);
915
916                 for (int i = 0; i < _listeBases.size(); i++) {
917                         int basenum = _listeBases.get(i).getBaseNumber();
918                         if (basenum == -1) {
919                                 basenum = i + 1;
920                         }
921                         ModeleBase mb = _listeBases.get(i);
922                         if (this.isNumberDrawn(mb, conf._numPeriod)) {
923                                 out.setColor(mb.getStyleBase()
924                                                 .getBaseNumberColor());
925                                 x0 = coords[i].x;
926                                 y0 = coords[i].y;
927                                 x1 = centers[i].x;
928                                 y1 = centers[i].y;
929                                 dx = x1 - x0;
930                                 dy = y1 - y0;
931                                 norm = Math.sqrt(dx * dx + dy * dy);
932                                 dx /= norm;
933                                 dy /= norm;
934                                 Point2D.Double vn = VARNAPanel.computeExcentricUnitVector(i,coords,centers);
935                                 
936                                 out.drawLine((x0 + 1.5 * BASE_RADIUS * vn.x), (y0 + 1.5
937                                                 * BASE_RADIUS * vn.y), (x0 + 2.5 * BASE_RADIUS * vn.x),
938                                                 (y0 + 2.5 * BASE_RADIUS * vn.y), 1);
939                                 out.drawText(
940                                                 (x0 + (conf._distNumbers + 1.0) * BASE_RADIUS * vn.x),
941                                                 (y0 + (conf._distNumbers + 1.0) * BASE_RADIUS * vn.y), mb.getLabel());
942                         }
943                 }
944                 renderAnnotations(out, minX, minY, conf);
945
946                 // Draw color map
947                 if (conf._drawColorMap) {
948                         drawColorMap(conf, out);
949                 }
950
951                 // Drawing Title
952                 Rectangle2D.Double currentBBox = out.getBoundingBox();
953                 double titleFontSize = (2.0 * conf._titleFont.getSize());
954                 out.setColor(conf._titleColor);
955                 out.setFont(PSExport.FONT_HELVETICA, titleFontSize);
956                 double yTitle = currentBBox.y - titleFontSize / 2.0;
957                 if (!getName().equals("")) {
958                         out.drawText((maxX - minX) / 2.0, yTitle, getName());
959                 }
960
961                 OutputStreamWriter fout;
962
963                 try {
964                         fout = new OutputStreamWriter(new FileOutputStream(path), "UTF-8");
965
966                         fout.write(out.export());
967                         fout.close();
968                 } catch (IOException e) {
969                         throw new ExceptionWritingForbidden(e.getMessage());
970                 }
971         }
972
973         Point2D.Double buildCaptionPosition(ModeleBase mb, double heightEstimate,
974                         VARNAConfig conf) {
975                 double radius = 2.0;
976                 if (isNumberDrawn(mb, conf._numPeriod)) {
977                         radius += (conf._distNumbers + 1.0);
978                 }
979                 Point2D.Double center = mb.getCenter();
980                 Point2D.Double p = mb.getCoords();
981                 double realDistance = BASE_RADIUS * radius + heightEstimate;
982                 return new Point2D.Double(center.getX() + (p.getX() - center.getX())
983                                 * ((p.distance(center) + realDistance) / p.distance(center)),
984                                 center.getY()
985                                                 + (p.getY() - center.getY())
986                                                 * ((p.distance(center) + realDistance) / p
987                                                                 .distance(center)));
988         }
989
990         public double getBPHeightIncrement() {
991                 return this._bpHeightIncrement;
992         }
993
994         public void setBPHeightIncrement(double d) {
995                 _bpHeightIncrement = d;
996         }
997
998         private void drawChemProbAnnotation(SecStrDrawingProducer out,
999                         ChemProbAnnotation cpa, Point2D.Double anchor, double minX,
1000                         double minY) {
1001                 out.setColor(cpa.getColor());
1002                 Point2D.Double v = cpa.getDirVector();
1003                 Point2D.Double vn = cpa.getNormalVector();
1004                 Point2D.Double base = new Point2D.Double((anchor.x + CHEM_PROB_DIST
1005                                 * v.x), (anchor.y + CHEM_PROB_DIST * v.y));
1006                 Point2D.Double edge = new Point2D.Double(
1007                                 (base.x + CHEM_PROB_BASE_LENGTH * cpa.getIntensity() * v.x),
1008                                 (base.y + CHEM_PROB_BASE_LENGTH * cpa.getIntensity() * v.y));
1009                 double thickness = CHEM_PROB_ARROW_THICKNESS * cpa.getIntensity();
1010                 switch (cpa.getType()) {
1011                 case ARROW: {
1012                         Point2D.Double arrowTip1 = new Point2D.Double(
1013                                         (base.x + cpa.getIntensity()
1014                                                         * (CHEM_PROB_ARROW_WIDTH * vn.x + CHEM_PROB_ARROW_HEIGHT
1015                                                                         * v.x)),
1016                                         (base.y + cpa.getIntensity()
1017                                                         * (CHEM_PROB_ARROW_WIDTH * vn.y + CHEM_PROB_ARROW_HEIGHT
1018                                                                         * v.y)));
1019                         Point2D.Double arrowTip2 = new Point2D.Double(
1020                                         (base.x + cpa.getIntensity()
1021                                                         * (-CHEM_PROB_ARROW_WIDTH * vn.x + CHEM_PROB_ARROW_HEIGHT
1022                                                                         * v.x)),
1023                                         (base.y + cpa.getIntensity()
1024                                                         * (-CHEM_PROB_ARROW_WIDTH * vn.y + CHEM_PROB_ARROW_HEIGHT
1025                                                                         * v.y)));
1026                         out.drawLine(base.x - minX, minY - base.y, edge.x - minX, minY
1027                                         - edge.y, thickness);
1028                         out.drawLine(base.x - minX, minY - base.y, arrowTip1.x - minX, minY
1029                                         - arrowTip1.y, thickness);
1030                         out.drawLine(base.x - minX, minY - base.y, arrowTip2.x - minX, minY
1031                                         - arrowTip2.y, thickness);
1032                 }
1033                         break;
1034                 case PIN: {
1035                         Point2D.Double side1 = new Point2D.Double(
1036                                         (edge.x - cpa.getIntensity()
1037                                                         * (CHEM_PROB_PIN_SEMIDIAG * v.x)),
1038                                         (edge.y - cpa.getIntensity()
1039                                                         * (CHEM_PROB_PIN_SEMIDIAG * v.y)));
1040                         Point2D.Double side2 = new Point2D.Double(
1041                                         (edge.x - cpa.getIntensity()
1042                                                         * (CHEM_PROB_PIN_SEMIDIAG * vn.x)),
1043                                         (edge.y - cpa.getIntensity()
1044                                                         * (CHEM_PROB_PIN_SEMIDIAG * vn.y)));
1045                         Point2D.Double side3 = new Point2D.Double(
1046                                         (edge.x + cpa.getIntensity()
1047                                                         * (CHEM_PROB_PIN_SEMIDIAG * v.x)),
1048                                         (edge.y + cpa.getIntensity()
1049                                                         * (CHEM_PROB_PIN_SEMIDIAG * v.y)));
1050                         Point2D.Double side4 = new Point2D.Double(
1051                                         (edge.x + cpa.getIntensity()
1052                                                         * (CHEM_PROB_PIN_SEMIDIAG * vn.x)),
1053                                         (edge.y + cpa.getIntensity()
1054                                                         * (CHEM_PROB_PIN_SEMIDIAG * vn.y)));
1055                         GeneralPath p2 = new GeneralPath();
1056                         p2.moveTo((float) (side1.x - minX), (float) (minY - side1.y));
1057                         p2.lineTo((float) (side2.x - minX), (float) (minY - side2.y));
1058                         p2.lineTo((float) (side3.x - minX), (float) (minY - side3.y));
1059                         p2.lineTo((float) (side4.x - minX), (float) (minY - side4.y));
1060                         p2.closePath();
1061                         out.fillPolygon(p2, cpa.getColor());
1062                         out.drawLine(base.x - minX, minY - base.y, edge.x - minX, minY
1063                                         - edge.y, thickness);
1064                 }
1065                         break;
1066                 case TRIANGLE: {
1067                         Point2D.Double arrowTip1 = new Point2D.Double(
1068                                         (edge.x + cpa.getIntensity()
1069                                                         * (CHEM_PROB_TRIANGLE_WIDTH * vn.x)),
1070                                         (edge.y + cpa.getIntensity()
1071                                                         * (CHEM_PROB_TRIANGLE_WIDTH * vn.y)));
1072                         Point2D.Double arrowTip2 = new Point2D.Double(
1073                                         (edge.x + cpa.getIntensity()
1074                                                         * (-CHEM_PROB_TRIANGLE_WIDTH * vn.x)),
1075                                         (edge.y + cpa.getIntensity()
1076                                                         * (-CHEM_PROB_TRIANGLE_WIDTH * vn.y)));
1077                         GeneralPath p2 = new GeneralPath();
1078                         p2.moveTo((float) (base.x - minX), (float) (minY - base.y));
1079                         p2.lineTo((float) (arrowTip1.x - minX),
1080                                         (float) (minY - arrowTip1.y));
1081                         p2.lineTo((float) (arrowTip2.x - minX),
1082                                         (float) (minY - arrowTip2.y));
1083                         p2.closePath();
1084                         out.fillPolygon(p2, cpa.getColor());
1085                 }
1086                         break;
1087                 case DOT: {
1088                         Double radius = CHEM_PROB_DOT_RADIUS * cpa.getIntensity();
1089                         Point2D.Double center = new Point2D.Double((base.x + radius * v.x)
1090                                         - minX, minY - (base.y + radius * v.y));
1091                         out.fillCircle(center.x, center.y, radius, thickness,
1092                                         cpa.getColor());
1093                 }
1094                         break;
1095                 }
1096         }
1097
1098         private void renderAnnotations(SecStrDrawingProducer out, double minX,
1099                         double minY, VARNAConfig conf) {
1100                 for (TextAnnotation textAnnotation : getAnnotations()) {
1101                         out.setColor(textAnnotation.getColor());
1102                         out.setFont(PSExport.FONT_HELVETICA_BOLD, 2.0 * textAnnotation
1103                                         .getFont().getSize());
1104                         Point2D.Double position = textAnnotation.getCenterPosition();
1105                         if (textAnnotation.getType() == TextAnnotation.AnchorType.BASE) {
1106                                 ModeleBase mb = (ModeleBase) textAnnotation.getAncrage();
1107                                 double fontHeight = Math.ceil(textAnnotation.getFont()
1108                                                 .getSize());
1109                                 position = buildCaptionPosition(mb, fontHeight, conf);
1110
1111                         }
1112                         out.drawText(position.x - minX, -(position.y - minY),
1113                                         textAnnotation.getTexte());
1114                 }
1115                 for (ChemProbAnnotation cpa : getChemProbAnnotations()) {
1116                         Point2D.Double anchor = cpa.getAnchorPosition();
1117                         drawChemProbAnnotation(out, cpa, anchor, minX, minY);
1118                 }
1119         }
1120
1121         public boolean isNumberDrawn(ModeleBase mb, int numPeriod) {
1122                 if (numPeriod <= 0)
1123                         return false;
1124                 return ((mb.getIndex() == 0) || ((mb.getBaseNumber()) % numPeriod == 0) || (mb
1125                                 .getIndex() == get_listeBases().size() - 1));
1126         }
1127
1128         public void saveRNAEPS(String path, VARNAConfig conf)
1129                         throws ExceptionWritingForbidden {
1130                 PSExport out = new PSExport();
1131                 saveRNA(path, conf, 0.4, out);
1132         }
1133
1134         public void saveRNAXFIG(String path, VARNAConfig conf)
1135                         throws ExceptionWritingForbidden {
1136                 XFIGExport out = new XFIGExport();
1137                 saveRNA(path, conf, 20, out);
1138         }
1139
1140         public void saveRNATIKZ(String path, VARNAConfig conf)
1141                         throws ExceptionWritingForbidden {
1142                 TikzExport out = new TikzExport();
1143                 saveRNA(path, conf, 0.15, out);
1144         }
1145
1146         public void saveRNASVG(String path, VARNAConfig conf)
1147                         throws ExceptionWritingForbidden {
1148                 SVGExport out = new SVGExport();
1149                 saveRNA(path, conf, 0.5, out);
1150         }
1151
1152         public Rectangle2D.Double getBBox() {
1153                 Rectangle2D.Double result = new Rectangle2D.Double(10, 10, 10, 10);
1154                 double minx, maxx, miny, maxy;
1155                 minx = Double.MAX_VALUE;
1156                 miny = Double.MAX_VALUE;
1157                 maxx = -Double.MAX_VALUE;
1158                 maxy = -Double.MAX_VALUE;
1159                 for (int i = 0; i < _listeBases.size(); i++) {
1160                         minx = Math.min(
1161                                         _listeBases.get(i).getCoords().getX() - BASE_RADIUS, minx);
1162                         miny = Math.min(
1163                                         _listeBases.get(i).getCoords().getY() - BASE_RADIUS, miny);
1164                         maxx = Math.max(
1165                                         _listeBases.get(i).getCoords().getX() + BASE_RADIUS, maxx);
1166                         maxy = Math.max(
1167                                         _listeBases.get(i).getCoords().getY() + BASE_RADIUS, maxy);
1168                 }
1169                 result.x = minx;
1170                 result.y = miny;
1171                 result.width = Math.max(maxx - minx, 1);
1172                 result.height = Math.max(maxy - miny, 1);
1173                 if (_drawMode == RNA.DRAW_MODE_LINEAR) {
1174                         double realHeight = _bpHeightIncrement * result.width / 2.0;
1175                         result.height += realHeight;
1176                         result.y -= realHeight;
1177                 }
1178                 return result;
1179         }
1180
1181         public void setCoord(int index, Point2D.Double p) {
1182                 setCoord(index, p.x, p.y);
1183         }
1184
1185         public void setCoord(int index, double x, double y) {
1186                 if (index < _listeBases.size()) {
1187                         _listeBases.get(index).setCoords(new Point2D.Double(x, y));
1188                 }
1189         }
1190
1191         public Point2D.Double getCoords(int i) {
1192                 if (i < _listeBases.size() && i >= 0) {
1193                         return _listeBases.get(i).getCoords();
1194                 }
1195                 return new Point2D.Double();
1196         }
1197
1198         public String getBaseContent(int i) {
1199                 if ((i >= 0) && (i < _listeBases.size())) {
1200                         return _listeBases.get(i).getContent();
1201                 }
1202                 return "";
1203         }
1204
1205         public int getBaseNumber(int i) {
1206                 if ((i >= 0) && (i < _listeBases.size())) {
1207                         return _listeBases.get(i).getBaseNumber();
1208                 }
1209                 return -1;
1210         }
1211
1212         public Point2D.Double getCenter(int i) {
1213                 if (i < _listeBases.size()) {
1214                         return _listeBases.get(i).getCenter();
1215                 }
1216
1217                 return new Point2D.Double();
1218         }
1219
1220         public void setCenter(int i, double x, double y) {
1221                 setCenter(i, new Point2D.Double(x, y));
1222         }
1223
1224         public void setCenter(int i, Point2D.Double p) {
1225                 if (i < _listeBases.size()) {
1226                         _listeBases.get(i).setCenter(p);
1227                 }
1228         }
1229
1230         public void drawRNACircle(VARNAConfig conf) {
1231                 _drawn = true;
1232                 _drawMode = DRAW_MODE_CIRCULAR;
1233                 int radius = (int) ((3 * (_listeBases.size() + 1) * BASE_RADIUS) / (2 * Math.PI));
1234                 double angle;
1235                 for (int i = 0; i < _listeBases.size(); i++) {
1236                         angle = -((((double) -(i + 1)) * 2.0 * Math.PI)
1237                                         / ((double) (_listeBases.size() + 1)) - Math.PI / 2.0);
1238                         _listeBases
1239                                         .get(i)
1240                                         .setCoords(
1241                                                         new Point2D.Double(
1242                                                                         (radius * Math.cos(angle) * conf._spaceBetweenBases),
1243                                                                         (radius * Math.sin(angle) * conf._spaceBetweenBases)));
1244                         _listeBases.get(i).setCenter(new Point2D.Double(0, 0));
1245                 }
1246         }
1247
1248         public void drawRNAVARNAView(VARNAConfig conf) {
1249                 _drawn = true;
1250                 _drawMode = DRAW_MODE_VARNA_VIEW;
1251                 VARNASecDraw vs = new VARNASecDraw();
1252                 vs.drawRNA(1, this);
1253         }
1254
1255         public void drawRNALine(VARNAConfig conf) {
1256                 _drawn = true;
1257                 _drawMode = DRAW_MODE_LINEAR;
1258                 for (int i = 0; i < get_listeBases().size(); i++) {
1259                         get_listeBases().get(i).setCoords(
1260                                         new Point2D.Double(i * conf._spaceBetweenBases * 20, 0));
1261                         get_listeBases().get(i).setCenter(
1262                                         new Point2D.Double(i * conf._spaceBetweenBases * 20, -10));
1263                 }
1264         }
1265
1266         public RNATemplateMapping drawRNATemplate(RNATemplate template, boolean straightBulges,
1267                         VARNAConfig conf) throws RNATemplateDrawingAlgorithmException {
1268                 return drawRNATemplate(template, conf,
1269                                 DrawRNATemplateMethod.getDefault(),
1270                                 DrawRNATemplateCurveMethod.getDefault(), straightBulges);
1271         }
1272
1273         public RNATemplateMapping drawRNATemplate(RNATemplate template,
1274                         VARNAConfig conf, DrawRNATemplateMethod helixLengthAdjustmentMethod, 
1275                         boolean straightBulges)
1276                         throws RNATemplateDrawingAlgorithmException {
1277                 return drawRNATemplate(template, conf, helixLengthAdjustmentMethod,
1278                                 DrawRNATemplateCurveMethod.getDefault(),straightBulges);
1279         }
1280
1281         public RNATemplateMapping drawRNATemplate(RNATemplate template,
1282                         VARNAConfig conf,
1283                         DrawRNATemplateMethod helixLengthAdjustmentMethod,
1284                         DrawRNATemplateCurveMethod curveMethod,
1285                         boolean straightBulges)
1286                         throws RNATemplateDrawingAlgorithmException {
1287                 _drawn = true;
1288                 _drawMode = DRAW_MODE_TEMPLATE;
1289
1290                 DrawRNATemplate drawRNATemplate = new DrawRNATemplate(this);
1291                 drawRNATemplate.drawRNATemplate(template, conf,
1292                                 helixLengthAdjustmentMethod, curveMethod, 
1293                                 straightBulges);
1294                 return drawRNATemplate.getMapping();
1295         }
1296
1297         private static double objFun(int n1, int n2, double r, double bpdist,
1298                         double multidist) {
1299                 return (((double) n1) * 2.0 * Math.asin(((double) bpdist) / (2.0 * r))
1300                                 + ((double) n2) * 2.0
1301                                 * Math.asin(((double) multidist) / (2.0 * r)) - (2.0 * Math.PI));
1302         }
1303
1304         public double determineRadius(int nbHel, int nbUnpaired, double startRadius) {
1305                 return determineRadius(nbHel, nbUnpaired, startRadius,
1306                                 BASE_PAIR_DISTANCE, MULTILOOP_DISTANCE);
1307         }
1308
1309         public static double determineRadius(int nbHel, int nbUnpaired,
1310                         double startRadius, double bpdist, double multidist) {
1311                 double xmin = bpdist / 2.0;
1312                 double xmax = 3.0 * multidist + 1;
1313                 double x = (xmin + xmax) / 2.0;
1314                 double y = 10000.0;
1315                 double ymin = -1000.0;
1316                 double ymax = 1000.0;
1317                 int numIt = 0;
1318                 double precision = 0.00001;
1319                 while ((Math.abs(y) > precision) && (numIt < 10000)) {
1320                         x = (xmin + xmax) / 2.0;
1321                         y = objFun(nbHel, nbUnpaired, x, bpdist, multidist);
1322                         ymin = objFun(nbHel, nbUnpaired, xmax, bpdist, multidist);
1323                         ymax = objFun(nbHel, nbUnpaired, xmin, bpdist, multidist);
1324                         if (ymin > 0.0) {
1325                                 xmax = xmax + (xmax - xmin);
1326                         } else if ((y <= 0.0) && (ymax > 0.0)) {
1327                                 xmax = x;
1328                         } else if ((y >= 0.0) && (ymin < 0.0)) {
1329                                 xmin = x;
1330                         } else if (ymax < 0.0) {
1331                                 xmin = Math.max(xmin - (x - xmin),
1332                                                 Math.max(bpdist / 2.0, multidist / 2.0));
1333                                 xmax = x;
1334                         }
1335                         numIt++;
1336                 }
1337                 return x;
1338         }
1339
1340         public void drawRNA(VARNAConfig conf) throws ExceptionNAViewAlgorithm {
1341                 drawRNA(RNA.DEFAULT_DRAW_MODE, conf);
1342         }
1343
1344         public void drawRNA(int mode, VARNAConfig conf)
1345                         throws ExceptionNAViewAlgorithm {
1346                 _drawMode = mode;
1347                 switch (get_drawMode()) {
1348                 case RNA.DRAW_MODE_RADIATE:
1349                         drawRNARadiate(conf);
1350                         break;
1351                 case RNA.DRAW_MODE_LINEAR:
1352                         drawRNALine(conf);
1353                         break;
1354                 case RNA.DRAW_MODE_CIRCULAR:
1355                         drawRNACircle(conf);
1356                         break;
1357                 case RNA.DRAW_MODE_NAVIEW:
1358                         drawRNANAView(conf);
1359                         break;
1360                 case RNA.DRAW_MODE_VARNA_VIEW:
1361                         drawRNAVARNAView(conf);
1362                         break;
1363                 default:
1364                         break;
1365                 }
1366
1367         }
1368
1369         public int getDrawMode() {
1370                 return _drawMode;
1371         }
1372         
1373         public static double HYSTERESIS_EPSILON = .15;
1374         public static final double[] HYSTERESIS_ATTRACTORS = {0.,Math.PI/4.,Math.PI/2.,3.*Math.PI/4.,Math.PI,5.*(Math.PI)/4.,3.*(Math.PI)/2,7.*(Math.PI)/4.};
1375         
1376         public static double normalizeAngle(double angle)
1377         {
1378                 return normalizeAngle(angle,0.);
1379         }
1380         
1381         public static double normalizeAngle(double angle, double fromVal)
1382         {
1383                 double toVal = fromVal +2.*Math.PI;
1384                 double result = angle;
1385                 while(result<fromVal)
1386                 {
1387                         result += 2.*Math.PI;
1388                 }
1389                 while(result >= toVal)
1390                 {
1391                         result -= 2.*Math.PI;
1392                 }
1393                 return result;          
1394         }
1395         
1396         public static double correctHysteresis(double angle)
1397         {
1398                 double result = normalizeAngle(angle);
1399                 
1400                 for (int i=0;i<HYSTERESIS_ATTRACTORS.length;i++)
1401                 {
1402                         double att = HYSTERESIS_ATTRACTORS[i];
1403                         if (Math.abs(normalizeAngle(att-result,-Math.PI))<HYSTERESIS_EPSILON)
1404                         {
1405                                 result = att;
1406                         }
1407                 }
1408                 return result;
1409         }
1410         
1411
1412         
1413         private void distributeUnpaired(
1414                         double radius,
1415                         double angle, 
1416                         double pHel, 
1417                         double base,
1418                         Point2D.Double center,
1419                         Vector<Integer> bases)
1420         {
1421                         double mydist = Math.abs(radius*(angle / (bases.size() + 1)));
1422                         double addedRadius= 0.;
1423                         Point2D.Double PA = new Point2D.Double(center.x + radius * Math.cos(base + pHel),
1424                                         center.y + radius * Math.sin(base + pHel));
1425                         Point2D.Double PB = new Point2D.Double(center.x + radius * Math.cos(base + pHel+angle),
1426                                                 center.y + radius * Math.sin(base + pHel+angle));
1427                         double dist = PA.distance(PB);
1428                         Point2D.Double VN = new Point2D.Double((PB.y-PA.y)/dist,(-PB.x+PA.x)/dist);
1429                         if (mydist<2*BASE_RADIUS)
1430                         {
1431                                 addedRadius=Math.min(1.0,(2*BASE_RADIUS-mydist)/4)*computeRadius(mydist, 2.29*(bases.size() + 1)*BASE_RADIUS-mydist);
1432                         }
1433                         
1434                         
1435                         ArrayList<Point2D.Double> pos = computeNewAngles(bases.size(),center,VN, angle,base + pHel,radius,addedRadius);
1436                         for (int i = 0; i < bases.size(); i++) 
1437                         {
1438                                 int k = bases.get(i);
1439                                 setCoord(k, pos.get(i));
1440                         }                               
1441                 
1442         }
1443
1444         private double computeRadius(double b, double pobj)
1445         {
1446                 double a=b, aL=a, aU=Double.POSITIVE_INFINITY;
1447                 double h = (a-b)*(a-b)/((a+b)*(a+b));
1448                 double p = Math.PI*(a+b)*(1+h/4.+h*h/64.+h*h*h/256.+25.*h*h*h*h/16384.)/2.0;
1449                 double aold = a+1.;
1450                 while ((Math.abs(p-pobj)>10e-4)&&(aold!=a)){
1451                         aold = a;
1452                         if (p<pobj)
1453                         {
1454                                 aL = a;
1455                                 if (aU==Double.POSITIVE_INFINITY)
1456                                 {a *= 2.;}
1457                                 else
1458                                 { a = (a+aU)/2.; }
1459                         }
1460                         else
1461                         {
1462                                 aU = a;
1463                                 a = (a+aL)/2.0;
1464                         }
1465                         h = (a-b)*(a-b)/((a+b)*(a+b));
1466                         p = (Math.PI*(a+b)*(1+h/4.+h*h/64.+h*h*h/256.+25.*h*h*h*h/16384.))/2.0;
1467                 }
1468                 return a;
1469         }
1470         
1471         public static double computeAngle(Point2D.Double center, Point2D.Double p) {
1472                 double dist = center.distance(p);
1473                 double angle = Math.asin((p.y - center.y) / dist);
1474                 if (p.x - center.x < 0) {
1475                         angle = Math.PI - angle;
1476                 }
1477                 return angle;
1478         }
1479         
1480         private Point2D.Double rotatePoint(Point2D.Double center, Point2D.Double p,
1481                         double angle) {
1482                 double dist = p.distance(center);
1483                 double oldAngle = Math.asin((p.y - center.y) / dist);
1484
1485                 if (p.x - center.x < 0) {
1486                         oldAngle = Math.PI - oldAngle;
1487                 }
1488
1489                 double newX = (center.x + dist * Math.cos(oldAngle + angle));
1490                 double newY = (center.y + dist * Math.sin(oldAngle + angle));
1491
1492                 return new Point2D.Double(newX, newY);
1493         }
1494
1495
1496         
1497         private void rotateHelix(Point2D.Double center, int i, int j, double angle) {
1498                 for (int k = i; k <= j; k++) {
1499                         Point2D.Double oldp = getCoords(k);
1500                         Point2D.Double newp = rotatePoint(center, oldp, angle);
1501                         setCoord(k, newp);
1502                         if ((k != i) && (k != j)) {
1503
1504                                 Point2D.Double oldc = get_listeBases().get(k)
1505                                                 .getCenter();
1506                                 Point2D.Double newc = rotatePoint(center, oldc, angle);
1507                                 setCenter(k,newc);
1508                         }
1509                 }
1510         }
1511         
1512
1513
1514         private void fixUnpairedPositions(boolean isDirect, 
1515                         double angleRightPartner, 
1516                         double angleLimitLeft, double angleLimitRight, double angleLeftPartner, 
1517                         double radius, double base, Point2D.Double center,Vector<Integer> prevBases,Vector<Integer> nextBases)
1518         {
1519                 if (isDirect) {
1520                         double anglePrev = normalizeAngle(angleLimitLeft - angleRightPartner);
1521                         double angleNext = normalizeAngle(angleLeftPartner - angleLimitRight);
1522                         distributeUnpaired(radius,anglePrev, angleRightPartner, base,
1523                                         center,prevBases);
1524                         distributeUnpaired(radius,-angleNext, angleLeftPartner, base,
1525                                         center,nextBases);
1526                 } else {
1527                         double anglePrev = normalizeAngle(angleLeftPartner - angleLimitRight);
1528                         double angleNext = normalizeAngle(angleLimitLeft - angleRightPartner);
1529                         distributeUnpaired(radius,-anglePrev, angleLeftPartner, base,
1530                                         center,prevBases);                      
1531                         distributeUnpaired(radius,angleNext, angleRightPartner, base,
1532                                         center,nextBases);
1533                 }
1534         
1535         }
1536         
1537         private static Point2D.Double getPoint(double angleLine, double angleBulge, Point2D.Double center,  
1538                         Point2D.Double VN,double radius, double addedRadius, double dirBulge)
1539         {
1540                 return new Point2D.Double(
1541                                 center.x + radius * Math.cos(angleLine)+
1542                                 dirBulge*addedRadius*Math.sin(angleBulge)*VN.x,
1543                                 center.y + radius * Math.sin(angleLine)+
1544                                 dirBulge*addedRadius*Math.sin(angleBulge)*VN.y);
1545         }
1546         
1547
1548         private ArrayList<Point2D.Double> computeNewAngles(int numPoints, Point2D.Double center,  
1549                         Point2D.Double VN, double angle, double angleBase, double radius, double addedRadius)
1550         {
1551                 ArrayList<Point2D.Double> result = new ArrayList<Point2D.Double>();
1552                 if (numPoints>0)
1553                 {
1554                 ArrayList<Double> factors = new ArrayList<Double>();
1555                 
1556
1557                 Point2D.Double prevP = new Point2D.Double(
1558                                 center.x + radius * Math.cos(angleBase),
1559                                 center.y + radius * Math.sin(angleBase));
1560                 
1561                 
1562                 double fact = 0.;
1563                 
1564                 double angleBulge = 0.;
1565                 double dirBulge = (angle<0)?-1.:1.;
1566                 double dtarget =2.*BASE_RADIUS; 
1567                 
1568                 for (int i = 0; i < numPoints; i++) 
1569                 {
1570                                 double lbound = fact;
1571                                 double ubound = 1.0;
1572                                 double angleLine = angleBase + angle*fact;
1573                                 angleBulge = Math.PI*fact;
1574                                 Point2D.Double currP = getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge);
1575
1576                                 int numIter = 0;
1577                                 while ((Math.abs(currP.distance(prevP)-dtarget)>0.01)&& (numIter<100))
1578                                 {
1579                                         if (currP.distance(prevP)> dtarget)
1580                                         {
1581                                                 ubound = fact;
1582                                                 fact = (fact+lbound)/2.0;
1583                                         }
1584                                         else
1585                                         {
1586                                                 lbound = fact;
1587                                                 fact = (fact+ubound)/2.0;                                       
1588                                         }                               
1589                                         angleLine = angleBase + angle*fact;
1590                                         angleBulge = Math.PI*fact;
1591                                         currP = getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge);
1592                                         numIter++;
1593                                 }
1594                                 factors.add(fact);
1595                                 prevP = currP;
1596                 }
1597                 
1598                 
1599                 double rescale = 1.0/(factors.get(factors.size()-1)+factors.get(0));
1600
1601                 for(int j=0;j<factors.size();j++)
1602                 {
1603                         factors.set(j,factors.get(j)*rescale);
1604                 }
1605                 
1606                  
1607                 if (addedRadius>0)
1608                 {
1609                         prevP =  getPoint(angleBase, 0, center,VN,radius, addedRadius, dirBulge);
1610                         double totDist = 0.0;
1611                         for(int j=0;j<factors.size();j++)
1612                         {
1613                                 double newfact = factors.get(j);
1614                                 double angleLine = angleBase + angle*newfact;
1615                                 angleBulge = Math.PI*newfact;
1616                                 Point2D.Double currP = getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge); 
1617                                 totDist += currP.distance(prevP);
1618                                 prevP = currP;
1619                         }
1620                         totDist += getPoint(angleBase+angle, Math.PI, center,VN,radius, addedRadius, dirBulge).distance(prevP);
1621                         dtarget = totDist/(numPoints+1);
1622                         fact = 0.0;
1623                         factors=new ArrayList<Double>();
1624                         prevP = new Point2D.Double(
1625                                         center.x + radius * Math.cos(angleBase),
1626                                         center.y + radius * Math.sin(angleBase));
1627                         for (int i = 0; i < numPoints; i++) 
1628                         {
1629                                         double lbound = fact;
1630                                         double ubound = 1.5;
1631                                         double angleLine = angleBase + angle*fact;
1632                                         angleBulge = Math.PI*fact;
1633                                         Point2D.Double currP = getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge);
1634
1635                                         int numIter = 0;
1636                                         while ((Math.abs(currP.distance(prevP)-dtarget)>0.01)&& (numIter<100))
1637                                         {
1638                                                 if (currP.distance(prevP)> dtarget)
1639                                                 {
1640                                                         ubound = fact;
1641                                                         fact = (fact+lbound)/2.0;
1642                                                 }
1643                                                 else
1644                                                 {
1645                                                         lbound = fact;
1646                                                         fact = (fact+ubound)/2.0;                                       
1647                                                 }                               
1648                                                 angleLine = angleBase + angle*fact;
1649                                                 angleBulge = Math.PI*fact;
1650                                                 currP = getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge);
1651                                                 numIter++;
1652                                         }
1653                                         factors.add(fact);
1654                                         prevP = currP;
1655                         }
1656                         rescale = 1.0/(factors.get(factors.size()-1)+factors.get(0));
1657                                 for(int j=0;j<factors.size();j++)
1658                         {
1659                                 factors.set(j,factors.get(j)*rescale);
1660                         }
1661                 }       
1662
1663                 for(int j=0;j<factors.size();j++)
1664                 {
1665                         double newfact = factors.get(j);
1666                         double angleLine = angleBase + angle*newfact;
1667                         angleBulge = Math.PI*newfact;
1668                         result.add(getPoint(angleLine, angleBulge, center,VN,radius, addedRadius, dirBulge));                   
1669                 }
1670                 }       
1671                 return result;
1672         }
1673         
1674         
1675         
1676         void drawLoop(int i, int j, double x, double y, double dirAngle,
1677                         Point2D.Double[] coords, Point2D.Double[] centers, double[] angles, 
1678                         boolean straightBulges) {
1679                 if (i > j) {
1680                         return;
1681                 }
1682
1683                 // BasePaired
1684                 if (_listeBases.get(i).getElementStructure() == j) {
1685                         double normalAngle = Math.PI / 2.0;
1686                         centers[i] = new Point2D.Double(x, y);
1687                         centers[j] = new Point2D.Double(x, y);
1688                         coords[i].x = (x + BASE_PAIR_DISTANCE
1689                                         * Math.cos(dirAngle - normalAngle) / 2.0);
1690                         coords[i].y = (y + BASE_PAIR_DISTANCE
1691                                         * Math.sin(dirAngle - normalAngle) / 2.0);
1692                         coords[j].x = (x + BASE_PAIR_DISTANCE
1693                                         * Math.cos(dirAngle + normalAngle) / 2.0);
1694                         coords[j].y = (y + BASE_PAIR_DISTANCE
1695                                         * Math.sin(dirAngle + normalAngle) / 2.0);
1696                         drawLoop(i + 1, j - 1, x + LOOP_DISTANCE * Math.cos(dirAngle), y
1697                                         + LOOP_DISTANCE * Math.sin(dirAngle), dirAngle, coords,
1698                                         centers, angles, straightBulges);
1699                 } else {
1700                         int k = i;
1701                         Vector<Integer> basesMultiLoop = new Vector<Integer>();
1702                         Vector<Integer> helices = new Vector<Integer>();
1703                         int l;
1704                         while (k <= j) {
1705                                 l = _listeBases.get(k).getElementStructure();
1706                                 if (l > k) {
1707                                         basesMultiLoop.add(new Integer(k));
1708                                         basesMultiLoop.add(new Integer(l));
1709                                         helices.add(new Integer(k));
1710                                         k = l + 1;
1711                                 } else {
1712                                         basesMultiLoop.add(new Integer(k));
1713                                         k++;
1714                                 }
1715                         }
1716                         int mlSize = basesMultiLoop.size() + 2;
1717                         int numHelices = helices.size() + 1;
1718                         double totalLength = MULTILOOP_DISTANCE * (mlSize - numHelices)
1719                                         + BASE_PAIR_DISTANCE * numHelices;
1720                         double multiLoopRadius;
1721                         double angleIncrementML;
1722                         double angleIncrementBP;
1723                         if (mlSize > 3) {
1724                                 multiLoopRadius = determineRadius(numHelices, mlSize
1725                                                 - numHelices, (totalLength) / (2.0 * Math.PI),
1726                                                 BASE_PAIR_DISTANCE, MULTILOOP_DISTANCE);
1727                                 angleIncrementML = -2.0
1728                                                 * Math.asin(((float) MULTILOOP_DISTANCE)
1729                                                                 / (2.0 * multiLoopRadius));
1730                                 angleIncrementBP = -2.0
1731                                                 * Math.asin(((float) BASE_PAIR_DISTANCE)
1732                                                                 / (2.0 * multiLoopRadius));
1733                         } 
1734                         else {
1735                                 multiLoopRadius = 35.0;
1736                                 angleIncrementBP = -2.0
1737                                                 * Math.asin(((float) BASE_PAIR_DISTANCE)
1738                                                                 / (2.0 * multiLoopRadius));
1739                                 angleIncrementML = (-2.0 * Math.PI - angleIncrementBP) / 2.0;
1740                         }
1741                         // System.out.println("MLr:"+multiLoopRadius+" iBP:"+angleIncrementBP+" iML:"+angleIncrementML);
1742
1743                         double centerDist = Math.sqrt(Math.max(Math.pow(multiLoopRadius, 2)
1744                                         - Math.pow(BASE_PAIR_DISTANCE / 2.0, 2), 0.0))
1745                                         - LOOP_DISTANCE;
1746                         Point2D.Double mlCenter = new Point2D.Double(
1747                                         (x + (centerDist * Math.cos(dirAngle))),
1748                                         (y + (centerDist * Math.sin(dirAngle))));
1749
1750                         // Base directing angle for (multi|hairpin) loop, from the center's
1751                         // perspective
1752                         double baseAngle = dirAngle
1753                                         // U-turn
1754                                         + Math.PI
1755                                         // Account for already drawn supporting base-pair
1756                                         + 0.5 * angleIncrementBP
1757                                         // Base cannot be paired twice, so next base is at
1758                                         // "unpaired base distance"
1759                                         + 1.0 * angleIncrementML;
1760                         
1761                         ArrayList<Integer> currUnpaired = new ArrayList<Integer>();
1762                         Couple<Double,Double> currInterval = new Couple<Double,Double>(0.,baseAngle-1.0 * angleIncrementML);
1763                         ArrayList<Couple<ArrayList<Integer>,Couple<Double,Double>>> intervals = new ArrayList<Couple<ArrayList<Integer>,Couple<Double,Double>>>();
1764                         
1765                         for (k = basesMultiLoop.size() - 1; k >= 0; k--) {
1766                                 l = basesMultiLoop.get(k).intValue();
1767                                 //System.out.println(l+" ");
1768                                 centers[l] = mlCenter;
1769                                 boolean isPaired = (_listeBases.get(l).getElementStructure() != -1);
1770                                 boolean isPaired3 = isPaired && (_listeBases.get(l).getElementStructure() < l);
1771                                 boolean isPaired5 = isPaired && !isPaired3;
1772                                 if (isPaired3) {
1773                                         if ((numHelices == 2) && straightBulges)
1774                                         {
1775                                                 baseAngle = dirAngle-angleIncrementBP/2.;
1776                                         }
1777                                         else
1778                                         {
1779                                                 baseAngle = correctHysteresis(baseAngle+angleIncrementBP/2.)-angleIncrementBP/2.;
1780                                         }
1781                                         currInterval.first = baseAngle;
1782                                         intervals.add(new Couple<ArrayList<Integer>,Couple<Double,Double>>(currUnpaired,currInterval));
1783                                         currInterval = new Couple<Double,Double>(-1.,-1.);  
1784                                         currUnpaired = new ArrayList<Integer>();
1785                                 }
1786                                 else if (isPaired5)
1787                                 {
1788                                         currInterval.second = baseAngle;
1789                                 }
1790                                 else
1791                                 {
1792                                         currUnpaired.add(l);
1793                                 }
1794
1795                                 angles[l] = baseAngle;
1796                                 if (isPaired3)
1797                                 { 
1798                                         baseAngle += angleIncrementBP;
1799                                 }
1800                                 else {
1801                                         baseAngle += angleIncrementML;
1802                                 }
1803                         }
1804                         currInterval.first = dirAngle
1805                                         - Math.PI
1806                                         - 0.5 * angleIncrementBP;
1807                         intervals.add(new Couple<ArrayList<Integer>,Couple<Double,Double>>(currUnpaired,currInterval));
1808                         //System.out.println("Inc. ML:"+angleIncrementML+" BP:"+angleIncrementBP);
1809                         
1810                         for(Couple<ArrayList<Integer>,Couple<Double,Double>> inter: intervals)
1811                         {
1812                                 //double mid = inter.second.second;
1813                                 double mina = inter.second.first;
1814                                 double maxa = normalizeAngle(inter.second.second,mina);
1815                                 //System.out.println(""+mina+" " +maxa);
1816                                 
1817                                 for (int n=0;n<inter.first.size();n++)
1818                                 {
1819                                         double ratio = (1.+n)/(1.+inter.first.size());
1820                                         int b = inter.first.get(n);
1821                                         angles[b] = mina + (1.-ratio)*(maxa-mina);
1822                                 }
1823                         }
1824                         
1825                         
1826                         for (k = basesMultiLoop.size() - 1; k >= 0; k--) {
1827                                 l = basesMultiLoop.get(k).intValue();
1828                                 coords[l].x = mlCenter.x + multiLoopRadius
1829                                                 * Math.cos(angles[l]);
1830                                 coords[l].y = mlCenter.y + multiLoopRadius
1831                                                 * Math.sin(angles[l]);
1832                         }       
1833                         
1834                         // System.out.println("n1:"+n1+" n2:"+n2);
1835                         double newAngle;
1836                         int m, n;
1837                         for (k = 0; k < helices.size(); k++) {
1838                                 m = helices.get(k).intValue();
1839                                 n = _listeBases.get(m).getElementStructure();
1840                                 newAngle = (angles[m] + angles[n]) / 2.0;
1841                                 drawLoop(m + 1, n - 1, (LOOP_DISTANCE * Math.cos(newAngle))
1842                                                 + (coords[m].x + coords[n].x) / 2.0,
1843                                                 (LOOP_DISTANCE * Math.sin(newAngle))
1844                                                                 + (coords[m].y + coords[n].y) / 2.0, newAngle,
1845                                                 coords, centers, angles, straightBulges);
1846                         }
1847                 }
1848         }
1849
1850         private Vector<Integer> getPreviousUnpaired(Point h)
1851         {
1852                 Vector<Integer> prevBases = new Vector<Integer>();
1853                 boolean over = false;
1854                 int i = h.y + 1;
1855                 while (!over) {
1856                         if (i >=get_listeBases().size()) {
1857                                 over = true;
1858                         } else {
1859                                 if (get_listeBases().get(i)
1860                                                 .getElementStructure() == -1) {
1861                                         prevBases.add(new Integer(i));
1862                                 } else {
1863                                         over = true;
1864                                 }
1865                         }
1866                         i++;
1867                 }
1868                 return prevBases;
1869         }
1870
1871         private Vector<Integer> getNextUnpaired(Point h)
1872         {
1873                 boolean over = false;
1874                 int i = h.x - 1;
1875                 Vector<Integer> nextBases = new Vector<Integer>();
1876                 while (!over) {
1877                         if (i < 0) {
1878                                 over = true;
1879                         } else {
1880                                 if (get_listeBases().get(i)
1881                                                 .getElementStructure() == -1) {
1882                                         nextBases.add(new Integer(i));
1883                                 } else {
1884                                         over = true;
1885                                 }
1886                         }
1887                         i--;
1888                 }
1889                 return nextBases;
1890         }
1891
1892         
1893         public void rotateEverything(double delta, double base, double pLimL, double pLimR, Point h, Point ml, Hashtable<Integer,Point2D.Double> backupPos)
1894         {
1895                 boolean isDirect = testDirectionality(ml.x, ml.y, h.x);
1896                 Point2D.Double center = get_listeBases().get(h.x).getCenter();
1897                 for(int k=h.x;k<=h.y;k++)
1898                 { backupPos.put(k, getBaseAt(k).getCoords()); }
1899                 rotateHelix(center, h.x, h.y, delta);
1900                 
1901                 // Re-assigns unpaired atoms
1902                 Point2D.Double helixStart = getCoords(h.x);
1903                 Point2D.Double helixStop = getCoords(h.y);
1904                 double pHelR,pHelL;
1905                 if (isDirect) {
1906                         pHelR = computeAngle(center, helixStop) - base;
1907                         pHelL = computeAngle(center, helixStart) - base;
1908                 } else {
1909                         pHelL = computeAngle(center, helixStop) - base;
1910                         pHelR = computeAngle(center, helixStart) - base;
1911                 }
1912
1913                 Vector<Integer> prevBases = getPreviousUnpaired(h);
1914                 Vector<Integer> nextBases = getNextUnpaired(h);
1915                 
1916                 double radius = center.distance(helixStart);
1917
1918                 for (int j = 0; j < prevBases.size(); j++) 
1919                 {
1920                         int k = prevBases.get(j);
1921                         backupPos.put(k, getCoords(k));
1922                 }
1923                 for (int j = 0; j < nextBases.size(); j++) 
1924                 {
1925                         int k = nextBases.get(j);
1926                         backupPos.put(k, getCoords(k));
1927                 }
1928                 fixUnpairedPositions(isDirect, pHelR, pLimL, pLimR, pHelL, radius, base,center,prevBases,nextBases);            
1929         }
1930         
1931         
1932         
1933         public void drawRNARadiate() {
1934                 drawRNARadiate(-1.0, VARNAConfig.DEFAULT_SPACE_BETWEEN_BASES, true, true);
1935         }
1936
1937         public void drawRNARadiate(VARNAConfig conf) {
1938                 drawRNARadiate(-1.0, conf._spaceBetweenBases, conf._flatExteriorLoop, false);
1939         }
1940
1941         public static final double FLAT_RECURSIVE_INCREMENT = 20.;
1942         
1943         public void drawRNARadiate(double dirAngle, double _spaceBetweenBases,
1944                         boolean flatExteriorLoop, boolean straightBulges) {
1945                 _drawn = true;
1946                 straightBulges = true;
1947                 _drawMode = DRAW_MODE_RADIATE;
1948                 Point2D.Double[] coords = new Point2D.Double[_listeBases.size()];
1949                 Point2D.Double[] centers = new Point2D.Double[_listeBases.size()];
1950                 double[] angles = new double[_listeBases.size()];
1951                 for (int i = 0; i < _listeBases.size(); i++) {
1952                         coords[i] = new Point2D.Double(0, 0);
1953                         centers[i] = new Point2D.Double(0, 0);
1954                 }
1955                 if (flatExteriorLoop) {
1956                         dirAngle += 1.0 - Math.PI / 2.0;
1957                         int i = 0;
1958                         double x = 0.0;
1959                         double y = 0.0;
1960                         double vx = -Math.sin(dirAngle);
1961                         double vy = Math.cos(dirAngle);
1962                         while (i < _listeBases.size()) {
1963                                 coords[i].x = x;
1964                                 coords[i].y = y;
1965                                 centers[i].x = x + BASE_PAIR_DISTANCE * vy;
1966                                 centers[i].y = y - BASE_PAIR_DISTANCE * vx;
1967                                 int j = _listeBases.get(i).getElementStructure();
1968                                 if (j > i) {
1969                                         double increment = 0.;
1970                                         if (i+1<_listeBases.size())
1971                                         {
1972                                                 if (_listeBases.get(i+1).getElementStructure()==-1)
1973                                                 {
1974                                                         //increment = -FLAT_RECURSIVE_INCREMENT;
1975                                                 }
1976                                         }
1977                                         drawLoop(i, j, x + (BASE_PAIR_DISTANCE * vx / 2.0), y
1978                                                         + (BASE_PAIR_DISTANCE * vy / 2.0)+increment, dirAngle,
1979                                                         coords, centers, angles, straightBulges);
1980                                         centers[i].x = coords[i].x + BASE_PAIR_DISTANCE * vy;
1981                                         centers[i].y = y - BASE_PAIR_DISTANCE * vx;
1982                                         i = j;
1983                                         x += BASE_PAIR_DISTANCE * vx;
1984                                         y += BASE_PAIR_DISTANCE * vy;
1985                                         centers[i].x = coords[i].x + BASE_PAIR_DISTANCE * vy;
1986                                         centers[i].y = y - BASE_PAIR_DISTANCE * vx;
1987                                 }
1988                                 x += MULTILOOP_DISTANCE * vx;
1989                                 y += MULTILOOP_DISTANCE * vy;
1990                                 i += 1;
1991                         }
1992                 } else {
1993                         drawLoop(0, _listeBases.size() - 1, 0, 0, dirAngle, coords, centers, angles, straightBulges);
1994                 }
1995                 for (int i = 0; i < _listeBases.size(); i++) {
1996                         _listeBases.get(i).setCoords(
1997                                         new Point2D.Double(coords[i].x * _spaceBetweenBases,
1998                                                         coords[i].y * _spaceBetweenBases));
1999                         _listeBases.get(i).setCenter(
2000                                         new Point2D.Double(centers[i].x * _spaceBetweenBases,
2001                                                         centers[i].y * _spaceBetweenBases));
2002                 }
2003
2004                 // TODO
2005                 // change les centres des bases de la premiere helice vers la boucle la
2006                 // plus proche
2007         }
2008
2009         public void drawRNANAView(VARNAConfig conf) throws ExceptionNAViewAlgorithm {
2010                 _drawMode = DRAW_MODE_NAVIEW;
2011                 _drawn = true;
2012
2013                 ArrayList<Double> X = new ArrayList<Double>(_listeBases.size());
2014                 ArrayList<Double> Y = new ArrayList<Double>(_listeBases.size());
2015                 ArrayList<Short> pair_table = new ArrayList<Short>(_listeBases.size());
2016
2017                 for (int i = 0; i < _listeBases.size(); i++) {
2018                         pair_table.add(Short.valueOf(String.valueOf(_listeBases.get(i)
2019                                         .getElementStructure())));
2020                 }
2021                 NAView naView = new NAView();
2022                 naView.naview_xy_coordinates(pair_table, X, Y);
2023
2024                 // Updating individual base positions
2025                 for (int i = 0; i < _listeBases.size(); i++) {
2026                         _listeBases.get(i).setCoords(
2027                                         new Point2D.Double(
2028                                                         X.get(i) * 2.5 * conf._spaceBetweenBases, Y.get(i)
2029                                                                         * 2.5 * conf._spaceBetweenBases));
2030                 }
2031
2032                 // Updating centers
2033                 for (int i = 0; i < _listeBases.size(); i++) {
2034                         int indicePartner = _listeBases.get(i).getElementStructure();
2035                         if (indicePartner != -1) {
2036                                 Point2D.Double base = _listeBases.get(i).getCoords();
2037                                 Point2D.Double partner = _listeBases.get(indicePartner)
2038                                                 .getCoords();
2039                                 _listeBases.get(i).setCenter(
2040                                                 new Point2D.Double((base.x + partner.x) / 2.0,
2041                                                                 (base.y + partner.y) / 2.0));
2042                         } else {
2043                                 Vector<Integer> loop = getLoopBases(i);
2044                                 double tmpx = 0.0;
2045                                 double tmpy = 0.0;
2046                                 for (int j = 0; j < loop.size(); j++) {
2047                                         int partner = loop.elementAt(j);
2048                                         Point2D.Double loopmember = _listeBases.get(partner)
2049                                                         .getCoords();
2050                                         tmpx += loopmember.x;
2051                                         tmpy += loopmember.y;
2052                                 }
2053                                 _listeBases.get(i).setCenter(
2054                                                 new Point2D.Double(tmpx / loop.size(), tmpy
2055                                                                 / loop.size()));
2056                         }
2057                 }
2058         }
2059
2060         /*
2061          * public void drawMOTIFView() { _drawn = true; _drawMode =
2062          * DRAW_MODE_MOTIFVIEW; int spaceBetweenStrand =0; Motif motif = new
2063          * Motif(this,get_listeBases()); motif.listStrand(); for (int i = 0; i <
2064          * motif.getListStrand().sizeStruct(); i++ ){ for (int j = 0; j <
2065          * motif.getListStrand().getStrand(i).sizeStrand(); j++ ){ int indice =
2066          * motif.getListStrand().getStrand(i).getMB(j).getIndex();
2067          * get_listeBases().get(indice).setCoords( new Point2D.Double(0,0));
2068          * get_listeBases().get(indice).setCenter( new Point2D.Double(0, 0));
2069          * 
2070          * } } //Recherche du brin central int centralStrand =
2071          * motif.getCentralStrand();
2072          * 
2073          * //Cas o? l'on a un motif en ?toile if(centralStrand!=-1){ //On positionne
2074          * le brin central motif.positionneSpecificStrand(centralStrand,
2075          * spaceBetweenStrand);
2076          * 
2077          * //On place les autres brins par rapport a ce brin central
2078          * motif.orderStrands(centralStrand); }
2079          * 
2080          * else { centralStrand = 0; motif.positionneStrand(); motif.ajusteStrand();
2081          * } motif.reajustement(); motif.deviationBasePair();
2082          * motif.setCenterMotif(); }
2083          */
2084
2085         public ArrayList<ModeleBase> getAllPartners(int indice) {
2086                 ArrayList<ModeleBase> result = new ArrayList<ModeleBase>();
2087                 ModeleBase me = this.getBaseAt(indice);
2088                 int i = me.getElementStructure();
2089                 if (i != -1) {
2090                         result.add(getBaseAt(i));
2091                 }
2092                 ArrayList<ModeleBP> msbps = getAuxBPs(indice);
2093                 for (ModeleBP m : msbps) {
2094                         result.add(m.getPartner(me));
2095                 }
2096                 return result;
2097         }
2098
2099         public int get_drawMode() {
2100                 return _drawMode;
2101         }
2102
2103         public void setDrawMode(int drawMode) {
2104                 _drawMode = drawMode;
2105         }
2106
2107         public Set<Integer> getSeparatorPositions(String s) {
2108                 HashSet<Integer> result = new HashSet<Integer>();
2109                 int index = s.indexOf(DBNStrandSep);
2110                 while (index >= 0) {
2111                         result.add(index);
2112                         index = s.indexOf(DBNStrandSep, index + 1);
2113                 }
2114                 return result;
2115         }
2116
2117         public static String DBNStrandSep = "&";
2118
2119         public void setRNA(String seq, String str)
2120                         throws ExceptionFileFormatOrSyntax,
2121                         ExceptionUnmatchedClosingParentheses {
2122                 ArrayList<String> al = RNA.explodeSequence(seq);
2123                 Set<Integer> sepPos = getSeparatorPositions(str);
2124                 ArrayList<String> alRes = new ArrayList<String>();
2125                 Set<Integer> resSepPos = new HashSet<Integer>();
2126                 String strRes = "";
2127                 for (int i = 0; i < al.size(); i++) {
2128                         if (sepPos.contains(i) && al.get(i).equals(DBNStrandSep)) {
2129                                 resSepPos.add(alRes.size() - 1);
2130                         } else {
2131                                 alRes.add(al.get(i));
2132                                 if (i<str.length())
2133                                 {
2134                                         strRes += str.charAt(i);
2135                                 }
2136                                 else
2137                                 {
2138                                         strRes += '.';
2139                                 }
2140                         }
2141                 }
2142                 for (int i = al.size(); i < str.length(); i++) {
2143                         alRes.add(" ");
2144                         strRes += str.charAt(i);
2145                 }
2146                 setRNA(alRes, strRes);
2147                 for (int i : resSepPos) {
2148                         _backbone.addElement(new ModeleBackboneElement(i,
2149                                         BackboneType.DISCONTINUOUS_TYPE));
2150                 }
2151         }
2152
2153         public void setRNA(String seq) {
2154                 ArrayList<String> s = RNA.explodeSequence(seq);
2155                 int[] str = new int[s.size()];
2156                 for (int i = 0; i < str.length; i++) {
2157                         str[i] = -1;
2158                 }
2159                 try {
2160                         setRNA(s, str);
2161                 } catch (ExceptionFileFormatOrSyntax e) {
2162                         // TODO Auto-generated catch block
2163                         e.printStackTrace();
2164                 }
2165         }
2166
2167         public void setRNA(String seq, int[] str)
2168                         throws ExceptionFileFormatOrSyntax,
2169                         ExceptionUnmatchedClosingParentheses {
2170                 setRNA(RNA.explodeSequence(seq), str);
2171         }
2172
2173         public void setRNA(String[] seq, int[] str)
2174                         throws ExceptionFileFormatOrSyntax {
2175                 setRNA(seq, str, 1);
2176         }
2177
2178         public void setRNA(List<String> seq, int[] str)
2179                         throws ExceptionFileFormatOrSyntax {
2180                 setRNA(seq.toArray(new String[seq.size()]), str, 1);
2181         }
2182
2183         public void setRNA(List<String> seq, int[] str, int baseIndex)
2184                         throws ExceptionFileFormatOrSyntax {
2185                 setRNA(seq.toArray(new String[seq.size()]), str, baseIndex);
2186         }
2187
2188         public void setRNA(String[] seq, int[] str, int baseIndex)
2189                         throws ExceptionFileFormatOrSyntax {
2190                 clearAnnotations();
2191                 _listeBases = new ArrayList<ModeleBase>();
2192                 if (seq.length != str.length) {
2193                         warningEmition("Sequence length " + seq.length
2194                                         + " differs from that of secondary structure " + str.length
2195                                         + ". \nAdapting sequence length ...");
2196                         if (seq.length < str.length) {
2197                                 String[] nseq = new String[str.length];
2198                                 for (int i = 0; i < seq.length; i++) {
2199                                         nseq[i] = seq[i];
2200                                 }
2201                                 for (int i = seq.length; i < nseq.length; i++) {
2202                                         nseq[i] = "";
2203                                 }
2204                                 seq = nseq;
2205                         } else {
2206                                 String[] seqTmp = new String[str.length];
2207                                 for (int i = 0; i < str.length; i++) {
2208                                         seqTmp[i] = seq[i];
2209                                 }
2210                                 seq = seqTmp;
2211                         }
2212                 }
2213                 for (int i = 0; i < str.length; i++) {
2214                         _listeBases.add(new ModeleBaseNucleotide(seq[i], i, baseIndex + i));
2215                 }
2216                 applyStruct(str);
2217         }
2218
2219         /**
2220          * Sets the RNA to be drawn. Uses when comparison mode is on. Will draw the
2221          * super-structure passed in parameters and apply specials styles to the
2222          * bases owning by each RNA alignment and both.
2223          * 
2224          * @param seq
2225          *            - The sequence of the super-structure This sequence shall be
2226          *            designed like this:
2227          *            <code>firstRNA1stBaseSecondRNA1stBaseFirstRNA2ndBaseSecondRNA2ndBase [...]</code>
2228          * <br>
2229          *            <b>Example:</b> <code>AAC-GUAGA--UGG</code>
2230          * @param struct
2231          *            - The super-structure
2232          * @param basesOwn
2233          *            - The RNA owning bases array (each index will be:0 when common
2234          *            base, 1 when first RNA alignment base, 2 when second RNA
2235          *            alignment base)
2236          * @throws ExceptionUnmatchedClosingParentheses
2237          * @throws ExceptionFileFormatOrSyntax
2238          */
2239         public void setRNA(String seq, String struct, ArrayList<Integer> basesOwn)
2240                         throws ExceptionUnmatchedClosingParentheses,
2241                         ExceptionFileFormatOrSyntax {
2242                 clearAnnotations();
2243                 _listeBases = new ArrayList<ModeleBase>();
2244                 // On "parse" la structure (repérage des points, tiret et couples
2245                 // parentheses ouvrante/fermante)
2246                 int[] array_struct = parseStruct(struct);
2247                 int size = struct.length();
2248                 int j = 0;
2249                 for (int i = 0; i < size; i++) {
2250                         ModeleBase mb;
2251                         if (seq.charAt(j) != seq.charAt(j + 1)) {
2252                                 ModeleBasesComparison mbc = new ModeleBasesComparison(
2253                                                 seq.charAt(j), seq.charAt(j + 1), i);
2254                                 mbc.set_appartenance(basesOwn.get(i));
2255                                 mbc.setBaseNumber(i + 1);
2256                                 mb = mbc;
2257                         } else {
2258                                 mb = new ModeleBaseNucleotide("" + seq.charAt(j), i, i + 1);
2259
2260                         }
2261                         _listeBases.add(mb);
2262                         j += 2;
2263                 }
2264                 for (int i = 0; i < size; i++) {
2265                         if (array_struct[i] != -1) {
2266                                 this.addBPNow(i, array_struct[i]);
2267                         }
2268
2269                         j += 2;
2270                 }
2271         }
2272
2273         public void setRNA(List<String> seq, String dbnStr)
2274                         throws ExceptionUnmatchedClosingParentheses,
2275                         ExceptionFileFormatOrSyntax {
2276                 clearAnnotations();
2277                 int[] finStr = RNAFactory.parseSecStr(dbnStr);
2278                 setRNA(seq, finStr);
2279         }
2280
2281         public static ArrayList<String> explodeSequence(String seq) {
2282                 ArrayList<String> analyzedSeq = new ArrayList<String>();
2283                 int i = 0;
2284                 while (i < seq.length()) {
2285                         if (seq.charAt(i) == '{') {
2286                                 boolean found = false;
2287                                 String buf = "";
2288                                 i++;
2289                                 while (!found & (i < seq.length())) {
2290                                         if (seq.charAt(i) != '}') {
2291                                                 buf += seq.charAt(i);
2292                                                 i++;
2293                                         } else {
2294                                                 found = true;
2295                                         }
2296                                 }
2297                                 analyzedSeq.add(buf);
2298                         } else {
2299                                 analyzedSeq.add("" + seq.charAt(i));
2300                         }
2301                         i++;
2302                 }
2303                 return analyzedSeq;
2304         }
2305
2306         public int[] parseStruct(String str)
2307                         throws ExceptionUnmatchedClosingParentheses,
2308                         ExceptionFileFormatOrSyntax {
2309                 int[] result = new int[str.length()];
2310                 int unexpectedChar = -1;
2311                 Stack<Integer> p = new Stack<Integer>();
2312                 for (int i = 0; i < str.length(); i++) {
2313                         char c = str.charAt(i);
2314                         if (c == '(') {
2315                                 p.push(new Integer(i));
2316                         } else if (c == '.' || c == '-' || c == ':') {
2317                                 result[i] = -1;
2318                         } else if (c == ')') {
2319                                 if (p.size() == 0) {
2320                                         throw new ExceptionUnmatchedClosingParentheses(i + 1);
2321                                 }
2322                                 int j = p.pop().intValue();
2323                                 result[i] = j;
2324                                 result[j] = i;
2325                         } else {
2326                                 if (unexpectedChar == -1)
2327                                         unexpectedChar = i;
2328                                 break;
2329                         }
2330                 }
2331
2332                 if (unexpectedChar != -1) {
2333                         // warningEmition("Unexpected Character at index:" +
2334                         // unexpectedChar);
2335                 }
2336
2337                 if (p.size() != 0) {
2338                         throw new ExceptionUnmatchedClosingParentheses(
2339                                         p.pop().intValue() + 1);
2340                 }
2341
2342                 return result;
2343         }
2344
2345         public Point getHelixInterval(int index) {
2346                 if ((index < 0) || (index >= _listeBases.size())) {
2347                         return new Point(index, index);
2348                 }
2349                 int j = _listeBases.get(index).getElementStructure();
2350                 if (j != -1) {
2351                         int minH = index;
2352                         int maxH = index;
2353                         if (j > index) {
2354                                 maxH = j;
2355                         } else {
2356                                 minH = j;
2357                         }
2358                         boolean over = false;
2359                         while (!over) {
2360                                 if ((minH < 0) || (maxH >= _listeBases.size())) {
2361                                         over = true;
2362                                 } else {
2363                                         if (_listeBases.get(minH).getElementStructure() == maxH) {
2364                                                 minH--;
2365                                                 maxH++;
2366                                         } else {
2367                                                 over = true;
2368                                         }
2369                                 }
2370                         }
2371                         minH++;
2372                         maxH--;
2373                         return new Point(minH, maxH);
2374                 }
2375                 return new Point(0, 0);
2376         }
2377
2378         public Point getExteriorHelix(int index) {
2379                 Point h = getHelixInterval(index);
2380                 int a = h.x;
2381                 int b = h.y;            
2382                 while (!((h.x==0)))
2383                 {                       
2384                         a = h.x;
2385                         b = h.y;                
2386                         h = getHelixInterval(a-1);
2387                 }
2388                 return new Point(a, b);
2389         }
2390         
2391         
2392         public ArrayList<Integer> getHelix(int index) {
2393                 ArrayList<Integer> result = new ArrayList<Integer>();
2394                 if ((index < 0) || (index >= _listeBases.size())) {
2395                         return result;
2396                 }
2397                 Point p = getHelixInterval(index);
2398                 for (int i = p.x; i <= p.y; i++) {
2399                         result.add(i);
2400                         result.add(this._listeBases.get(i).getElementStructure());
2401                 }
2402                 return result;
2403         }
2404
2405         public Point getMultiLoop(int index) {
2406                 if ((index < 0) || (index >= _listeBases.size())) {
2407                         return new Point(index, index);
2408                 }
2409                 Point h = getHelixInterval(index);
2410                 int minH = h.x - 1;
2411                 int maxH = h.y + 1;
2412                 boolean over = false;
2413                 while (!over) {
2414                         if (minH < 0) {
2415                                 over = true;
2416                                 minH = 0;
2417                         } else {
2418                                 if (_listeBases.get(minH).getElementStructure() == -1) {
2419                                         minH--;
2420                                 } else if (_listeBases.get(minH).getElementStructure() < minH) {
2421                                         minH = _listeBases.get(minH).getElementStructure() - 1;
2422                                 } else {
2423                                         over = true;
2424                                 }
2425                         }
2426                 }
2427                 over = false;
2428                 while (!over) {
2429                         if (maxH > _listeBases.size() - 1) {
2430                                 over = true;
2431                                 maxH = _listeBases.size() - 1;
2432                         } else {
2433                                 if (_listeBases.get(maxH).getElementStructure() == -1) {
2434                                         maxH++;
2435                                 } else if (_listeBases.get(maxH).getElementStructure() > maxH) {
2436                                         maxH = _listeBases.get(maxH).getElementStructure() + 1;
2437                                 } else {
2438                                         over = true;
2439                                 }
2440                         }
2441                 }
2442                 return new Point(minH, maxH);
2443         }
2444
2445         public Vector<Integer> getLoopBases(int startIndex) {
2446                 Vector<Integer> result = new Vector<Integer>();
2447
2448                 if ((startIndex < 0) || (startIndex >= _listeBases.size())) {
2449                         return result;
2450                 }
2451                 int index = startIndex;
2452                 result.add(startIndex);
2453                 if (_listeBases.get(index).getElementStructure() <= index) {
2454                         index = (index + 1) % _listeBases.size();
2455                 } else {
2456                         index = _listeBases.get(index).getElementStructure();
2457                         result.add(index);
2458                         index = (index + 1) % _listeBases.size();
2459                 }
2460
2461                 while (index != startIndex) {
2462                         result.add(index);
2463                         if (_listeBases.get(index).getElementStructure() == -1) {
2464                                 index = (index + 1) % _listeBases.size();
2465                         } else {
2466                                 index = _listeBases.get(index).getElementStructure();
2467                                 result.add(index);
2468                                 index = (index + 1) % _listeBases.size();
2469                         }
2470                 }
2471                 return result;
2472         }
2473
2474         /**
2475          * Returns the RNA secondary structure displayed by this panel as a
2476          * well-parenthesized word, accordingly to the DBN format
2477          * 
2478          * @return This panel's secondary structure
2479          */
2480         public String getStructDBN() {
2481                 String result = "";
2482                 for (int i = 0; i < _listeBases.size(); i++) {
2483                         int j = _listeBases.get(i).getElementStructure();
2484                         if (j == -1) {
2485                                 result += ".";
2486                         } else if (i > j) {
2487                                 result += ")";
2488                         } else {
2489                                 result += "(";
2490                         }
2491                 }
2492                 return addStrandSeparators(result);
2493         }
2494
2495         private ArrayList<ModeleBP> getNonCrossingSubset(
2496                         ArrayList<ArrayList<ModeleBP>> rankedBPs) {
2497                 ArrayList<ModeleBP> currentBPs = new ArrayList<ModeleBP>();
2498                 Stack<Integer> pile = new Stack<Integer>();
2499                 for (int i = 0; i < rankedBPs.size(); i++) {
2500                         ArrayList<ModeleBP> lbp = rankedBPs.get(i);
2501                         if (!lbp.isEmpty()) {
2502                                 ModeleBP bp = lbp.get(0);
2503                                 boolean ok = true;
2504                                 if (!pile.empty()) {
2505                                         int x = pile.peek();
2506                                         if ((bp.getIndex3() >= x)) {
2507                                                 ok = false;
2508                                         }
2509                                 }
2510                                 if (ok) {
2511                                         lbp.remove(0);
2512                                         currentBPs.add(bp);
2513                                         pile.add(bp.getIndex3());
2514                                 }
2515                         }
2516                         if (!pile.empty() && (i == pile.peek())) {
2517                                 pile.pop();
2518                         }
2519                 }
2520                 return currentBPs;
2521         }
2522
2523         public ArrayList<int[]> paginateStructure() {
2524                 ArrayList<int[]> result = new ArrayList<int[]>();
2525                 // Mumbo jumbo to sort the basepair list
2526                 ArrayList<ModeleBP> bps = this.getAllBPs();
2527                 ModeleBP[] mt = new ModeleBP[bps.size()];
2528                 bps.toArray(mt);
2529                 Arrays.sort(mt, new Comparator<ModeleBP>() {
2530                         public int compare(ModeleBP arg0, ModeleBP arg1) {
2531                                 if (arg0.getIndex5() != arg1.getIndex5())
2532                                         return arg0.getIndex5() - arg1.getIndex5();
2533                                 else
2534                                         return arg0.getIndex3() - arg1.getIndex3();
2535
2536                         }
2537                 });
2538                 ArrayList<ArrayList<ModeleBP>> rankedBps = new ArrayList<ArrayList<ModeleBP>>();
2539                 for (int i = 0; i < getSize(); i++) {
2540                         rankedBps.add(new ArrayList<ModeleBP>());
2541                 }
2542                 for (int i = 0; i < mt.length; i++) {
2543                         rankedBps.get(mt[i].getIndex5()).add(mt[i]);
2544                 }
2545
2546                 while (!bps.isEmpty()) {
2547                         //System.out.println("Page: " + result.size());
2548                         ArrayList<ModeleBP> currentBPs = getNonCrossingSubset(rankedBps);
2549                         int[] ss = new int[this.getSize()];
2550                         for (int i = 0; i < ss.length; i++) {
2551                                 ss[i] = -1;
2552                         }
2553
2554                         for (int i = 0; i < currentBPs.size(); i++) {
2555                                 ModeleBP mbp = currentBPs.get(i);
2556                                 ss[mbp.getIndex3()] = mbp.getIndex5();
2557                                 ss[mbp.getIndex5()] = mbp.getIndex3();
2558                         }
2559                         bps.removeAll(currentBPs);
2560                         result.add(ss);
2561                 }
2562                 return result;
2563         }
2564
2565         private void showBasic(int[] res) {
2566                 for (int i = 0; i < res.length; i++) {
2567                         System.out.print(res[i] + ",");
2568                 }
2569                 System.out.println();
2570
2571         }
2572         
2573         public int[] getStrandShifts()
2574         {
2575                 int[] result = new int[getSize()];
2576                 int acc = 0;
2577                 for (int i=0;i<getSize();i++)
2578                 {
2579                         if (_backbone.getTypeBefore(i)==BackboneType.DISCONTINUOUS_TYPE)
2580                         {
2581                                 acc++;
2582                         }
2583                         result[i] = acc;
2584                 }
2585                 return result;
2586                 
2587         }
2588         
2589         public String addStrandSeparators(String s)
2590         {
2591                 String res = "";
2592                 for (int i=0;i<s.length();i++)
2593                 {
2594                         res += s.charAt(i);
2595                         if (_backbone.getTypeAfter(i)==BackboneType.DISCONTINUOUS_TYPE)
2596                         {
2597                                 res += DBNStrandSep;
2598                         }
2599                 }
2600                 return res;
2601         }
2602         
2603
2604         public String getStructDBN(boolean includeMostPKs) {
2605                 String result = getStructDBN();
2606                 if (includeMostPKs) {
2607                         ArrayList<int[]> pages = paginateStructure();
2608                         char[] res = new char[getSize()];
2609                         for (int i = 0; i < res.length; i++) {
2610                                 res[i] = '.';
2611                         }
2612                         char[] open = { '(', '[', '{', '<', 'A', 'B', 'C', 'D', 'E', 'F',
2613                                         'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
2614                                         'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
2615                         
2616                         char[] close = new char[open.length];
2617                         close[0] = ')';
2618                         close[1] = ']';
2619                         close[2] = '}';
2620                         close[3] = '>';
2621                         for (int i=4; i<open.length;i++)
2622                         {
2623                                 close[i] = Character.toLowerCase(open[i]);
2624                         }
2625                         
2626                         for (int p = 0; p < Math.min(pages.size(), open.length); p++) {
2627                                 int[] page = pages.get(p);
2628                                 //showBasic(page);
2629                                 for (int i = 0; i < res.length; i++) {
2630                                         if (page[i] != -1 && page[i] > i && res[i] == '.'
2631                                                         && res[page[i]] == '.') {
2632                                                 res[i] = open[p];
2633                                                 res[page[i]] = close[p];
2634                                         }
2635                                 }
2636                         }
2637                         result = "";
2638                         for (int i = 0; i < res.length; i++) {
2639                                 result += res[i];
2640                         }
2641
2642                 }
2643                 return addStrandSeparators(result);
2644
2645         }
2646
2647         public String getStructDBN(int[] str) {
2648                 String result = "";
2649                 for (int i = 0; i < str.length; i++) {
2650                         if (str[i] == -1) {
2651                                 result += ".";
2652                         } else if (str[i] > i) {
2653                                 result += "(";
2654                         } else {
2655                                 result += ")";
2656                         }
2657                 }
2658                 return addStrandSeparators(result);
2659         }
2660
2661         /**
2662          * Returns the raw nucleotides sequence for the displayed RNA
2663          * 
2664          * @return The RNA sequence
2665          */
2666         public String getSeq() {
2667                 String result = "";
2668                 for (int i = 0; i < _listeBases.size(); i++) {
2669                         result += ((ModeleBase) _listeBases.get(i)).getContent();
2670                 }
2671                 return addStrandSeparators(result);
2672         }
2673
2674         public String getStructBPSEQ() {
2675                 String result = "";
2676                 int[] str = getNonOverlappingStruct();
2677                 for (int i = 0; i < _listeBases.size(); i++) {
2678                         result += (i + 1) + " "
2679                                         + ((ModeleBaseNucleotide) _listeBases.get(i)).getContent()
2680                                         + " " + (str[i] + 1) + "\n";
2681                 }
2682                 return result;
2683         }
2684
2685         public int[] getNonCrossingStruct() {
2686                 int[] result = new int[_listeBases.size()];
2687                 // Adding "planar" base-pairs
2688                 for (int i = 0; i < _listeBases.size(); i++) {
2689                         result[i] = _listeBases.get(i).getElementStructure();
2690                 }
2691                 return result;
2692         }
2693
2694         public int[] getNonOverlappingStruct() {
2695                 int[] result = getNonCrossingStruct();
2696                 // Adding additional base pairs when possible (No more than one
2697                 // base-pair per base)
2698                 for (int i = 0; i < _structureAux.size(); i++) {
2699                         ModeleBP msbp = _structureAux.get(i);
2700                         ModeleBase mb5 = msbp.getPartner5();
2701                         ModeleBase mb3 = msbp.getPartner3();
2702                         int j5 = mb5.getIndex();
2703                         int j3 = mb3.getIndex();
2704                         if ((result[j3] == -1) && (result[j5] == -1)) {
2705                                 result[j3] = j5;
2706                                 result[j5] = j3;
2707                         }
2708                 }
2709                 return result;
2710         }
2711
2712         public String getStructCT() {
2713                 String result = "";
2714                 for (int i = 0; i < _listeBases.size(); i++) {
2715                         result += (i + 1) + " "
2716                                         + ((ModeleBase) _listeBases.get(i)).getContent() + " " + i
2717                                         + " " + (i + 2) + " "
2718                                         + (_listeBases.get(i).getElementStructure() + 1) + " "
2719                                         + (i + 1) + "\n";
2720                 }
2721                 return result;
2722         }
2723
2724         public void saveAsBPSEQ(String path, String title)
2725                         throws ExceptionExportFailed, ExceptionPermissionDenied {
2726                 try {
2727                         FileWriter f = new FileWriter(path);
2728                         f.write("# " + title + "\n");
2729                         f.write(this.getStructBPSEQ() + "\n");
2730                         f.close();
2731                 } catch (IOException e) {
2732                         throw new ExceptionExportFailed(e.getMessage(), path);
2733                 }
2734         }
2735
2736         public void saveAsCT(String path, String title)
2737                         throws ExceptionExportFailed, ExceptionPermissionDenied {
2738                 try {
2739                         FileWriter f = new FileWriter(path);
2740                         f.write("" + _listeBases.size() + " " + title + "\n");
2741                         f.write(this.getStructCT() + "\n");
2742                         f.close();
2743                 } catch (IOException e) {
2744                         throw new ExceptionExportFailed(e.getMessage(), path);
2745                 }
2746         }
2747
2748         public void saveAsDBN(String path, String title)
2749                         throws ExceptionExportFailed, ExceptionPermissionDenied {
2750                 try {
2751                         FileWriter f = new FileWriter(path);
2752                         f.write("> " + title + "\n");
2753                         f.write(getListeBasesToString() + "\n");
2754                         f.write(getStructDBN() + "\n");
2755                         f.close();
2756                 } catch (IOException e) {
2757                         throw new ExceptionExportFailed(e.getMessage(), path);
2758                 }
2759         }
2760
2761         public String getListeBasesToString() {
2762                 String s = new String();
2763                 for (int i = 0; i < _listeBases.size(); i++) {
2764                         s += ((ModeleBaseNucleotide) _listeBases.get(i)).getContent();
2765                 }
2766                 return addStrandSeparators(s);
2767         }
2768
2769         public void applyBPs(ArrayList<ModeleBP> allbps) {
2770                 ArrayList<ModeleBP> planar = new ArrayList<ModeleBP>();
2771                 ArrayList<ModeleBP> others = new ArrayList<ModeleBP>();
2772                 // System.err.println("Sequence: "+this.getSeq());
2773                 RNAMLParser.planarize(allbps, planar, others, getSize());
2774                 // System.err.println("All:"+allbps);
2775                 // System.err.println("=> Planar: "+planar);
2776                 // System.err.println("=> Others: "+others);
2777
2778                 for (ModeleBP mb : planar) {
2779                         addBPnow(mb.getPartner5().getIndex(), mb.getPartner3().getIndex(),
2780                                         mb);
2781                 }
2782
2783                 for (ModeleBP mb : others) {
2784                         addBPAux(mb.getPartner5().getIndex(), mb.getPartner3().getIndex(),
2785                                         mb);
2786                 }
2787         }
2788
2789         public void set_listeBases(ArrayList<ModeleBase> _liste) {
2790                 this._listeBases = _liste;
2791         }
2792
2793         public void addVARNAListener(InterfaceVARNAListener rl) {
2794                 _listeVARNAListener.add(rl);
2795         }
2796
2797         public void warningEmition(String warningMessage) {
2798                 for (int i = 0; i < _listeVARNAListener.size(); i++) {
2799                         _listeVARNAListener.get(i).onWarningEmitted(warningMessage);
2800                 }
2801         }
2802
2803         public void applyStyleOnBases(ArrayList<Integer> basesList,
2804                         ModelBaseStyle style) {
2805                 for (int i = 1; i < basesList.size(); i++) {
2806                         _listeBases.get(basesList.get(i)).setStyleBase(style);
2807                 }
2808         }
2809
2810         private int[] correctReciprocity(int[] str) {
2811                 int[] result = new int[str.length];
2812                 for (int i = 0; i < str.length; i++) {
2813                         if (str[i] != -1) {
2814                                 if (i == str[str[i]]) {
2815                                         result[i] = str[i];
2816                                 } else {
2817                                         str[str[i]] = i;
2818                                 }
2819                         } else {
2820                                 result[i] = -1;
2821                         }
2822                 }
2823                 return result;
2824         }
2825
2826         private void applyStruct(int[] str) throws ExceptionFileFormatOrSyntax {
2827                 str = correctReciprocity(str);
2828
2829                 int[] planarSubset = RNAMLParser.planarize(str);
2830                 _structureAux.clear();
2831
2832                 for (int i = 0; i < planarSubset.length; i++) {
2833                         if (str[i] > i) {
2834                                 if (planarSubset[i] > i) {
2835                                         addBPNow(i, planarSubset[i]);
2836                                 } else if ((planarSubset[i] != str[i])) {
2837                                         addBPAux(i, str[i]);
2838                                 }
2839                         }
2840                 }
2841
2842         }
2843
2844         public ArrayList<ModeleBase> get_listeBases() {
2845                 return _listeBases;
2846         }
2847
2848         public int getSize() {
2849                 return _listeBases.size();
2850         }
2851
2852         public ArrayList<Integer> findAll() {
2853                 ArrayList<Integer> listAll = new ArrayList<Integer>();
2854                 for (int i = 0; i < get_listeBases().size(); i++) {
2855                         listAll.add(i);
2856                 }
2857                 return listAll;
2858         }
2859
2860         public ArrayList<Integer> findBulge(int index) {
2861                 ArrayList<Integer> listUp = new ArrayList<Integer>();
2862                 if (get_listeBases().get(index).getElementStructure() == -1) {
2863                         int i = index;
2864                         boolean over = false;
2865                         while ((i < get_listeBases().size()) && !over) {
2866                                 int j = get_listeBases().get(i).getElementStructure();
2867                                 if (j == -1) {
2868                                         listUp.add(i);
2869                                         i++;
2870                                 } else {
2871                                         over = true;
2872                                 }
2873                         }
2874                         i = index - 1;
2875                         over = false;
2876                         while ((i >= 0) && !over) {
2877                                 int j = get_listeBases().get(i).getElementStructure();
2878                                 if (j == -1) {
2879                                         listUp.add(i);
2880                                         i--;
2881                                 } else {
2882                                         over = true;
2883                                 }
2884                         }
2885                 }
2886                 return listUp;
2887         }
2888
2889         public ArrayList<Integer> findStem(int index) {
2890                 ArrayList<Integer> listUp = new ArrayList<Integer>();
2891                 int i = index;
2892                 do {
2893                         listUp.add(i);
2894                         int j = get_listeBases().get(i).getElementStructure();
2895                         if (j == -1) {
2896                                 i = (i + 1) % getSize();
2897                         } else {
2898                                 if ((j < i) && (index <= i) && (j <= index)) {
2899                                         i = j;
2900                                 } else {
2901                                         i = (i + 1) % getSize();
2902                                 }
2903                         }
2904                 } while (i != index);
2905                 return listUp;
2906         }
2907
2908         public int getHelixCountOnLoop(int indice) {
2909                 int cptHelice = 0;
2910                 if (indice < 0 || indice >= get_listeBases().size())
2911                         return cptHelice;
2912                 int i = indice;
2913                 int j = get_listeBases().get(i).getElementStructure();
2914                 // Only way to distinguish "supporting base-pair" from others
2915                 boolean justJumped = false;
2916                 if ((j != -1) && (j < i)) {
2917                         i = j + 1;
2918                         indice = i;
2919                 }
2920                 do {
2921                         j = get_listeBases().get(i).getElementStructure();
2922                         if ((j != -1) && (!justJumped)) {
2923                                 i = j;
2924                                 justJumped = true;
2925                                 cptHelice++;
2926                         } else {
2927                                 i = (i + 1) % get_listeBases().size();
2928                                 justJumped = false;
2929                         }
2930                 } while (i != indice);
2931                 return cptHelice;
2932         }
2933
2934         public ArrayList<Integer> findLoop(int indice) {
2935                 return findLoopForward(indice);
2936         }
2937
2938         public ArrayList<Integer> findLoopForward(int indice) {
2939                 ArrayList<Integer> base = new ArrayList<Integer>();
2940                 if (indice < 0 || indice >= get_listeBases().size())
2941                         return base;
2942                 int i = indice;
2943                 int j = get_listeBases().get(i).getElementStructure();
2944                 // Only way to distinguish "supporting base-pair" from others
2945                 boolean justJumped = false;
2946                 if (j != -1) {
2947                         i = Math.min(i, j) + 1;
2948                         indice = i;
2949                 }
2950                 do {
2951                         base.add(i);
2952                         j = get_listeBases().get(i).getElementStructure();
2953                         if ((j != -1) && (!justJumped)) {
2954                                 i = j;
2955                                 justJumped = true;
2956                         } else {
2957                                 i = (i + 1) % get_listeBases().size();
2958                                 justJumped = false;
2959                         }
2960                 } while (i != indice);
2961                 return base;
2962         }
2963
2964         public ArrayList<Integer> findPair(int indice) {
2965                 ArrayList<Integer> base = new ArrayList<Integer>();
2966                 int j = get_listeBases().get(indice).getElementStructure();
2967                 if (j != -1) {
2968                         base.add(Math.min(indice, j));
2969                         base.add(Math.max(indice, j));
2970                 }
2971
2972                 return base;
2973
2974         }
2975
2976         public ArrayList<Integer> findLoopBackward(int indice) {
2977                 ArrayList<Integer> base = new ArrayList<Integer>();
2978                 if (indice < 0 || indice >= get_listeBases().size())
2979                         return base;
2980                 int i = indice;
2981                 int j = get_listeBases().get(i).getElementStructure();
2982                 // Only way to distinguish "supporting base-pair" from others
2983                 boolean justJumped = false;
2984                 if (j != -1) {
2985                         i = Math.min(i, j) - 1;
2986                         indice = i;
2987                 }
2988                 if (i < 0) {
2989                         return base;
2990                 }
2991                 do {
2992                         base.add(i);
2993                         j = get_listeBases().get(i).getElementStructure();
2994                         if ((j != -1) && (!justJumped)) {
2995                                 i = j;
2996                                 justJumped = true;
2997                         } else {
2998                                 i = (i + get_listeBases().size() - 1) % get_listeBases().size();
2999                                 justJumped = false;
3000                         }
3001                 } while (i != indice);
3002                 return base;
3003         }
3004
3005         public ArrayList<Integer> findHelix(int indice) {
3006                 ArrayList<Integer> list = new ArrayList<Integer>();
3007                 if (get_listeBases().get(indice).getElementStructure() != -1) {
3008                         list.add(indice);
3009                         list.add(get_listeBases().get(indice).getElementStructure());
3010                         int i = 1, prec = get_listeBases().get(indice)
3011                                         .getElementStructure();
3012                         while (indice + i < get_listeBases().size()
3013                                         && get_listeBases().get(indice + i).getElementStructure() != -1
3014                                         && get_listeBases().get(indice + i).getElementStructure() == prec - 1) {
3015                                 list.add(indice + i);
3016                                 list.add(get_listeBases().get(indice + i).getElementStructure());
3017                                 prec = get_listeBases().get(indice + i).getElementStructure();
3018                                 i++;
3019                         }
3020                         i = -1;
3021                         prec = get_listeBases().get(indice).getElementStructure();
3022                         while (indice + i >= 0
3023                                         && get_listeBases().get(indice + i).getElementStructure() != -1
3024                                         && get_listeBases().get(indice + i).getElementStructure() == prec + 1) {
3025                                 list.add(indice + i);
3026                                 list.add(get_listeBases().get(indice + i).getElementStructure());
3027                                 prec = get_listeBases().get(indice + i).getElementStructure();
3028                                 i--;
3029                         }
3030                 }
3031                 return list;
3032         }
3033
3034         public ArrayList<Integer> find3Prime(int indice) {
3035                 ArrayList<Integer> list = new ArrayList<Integer>();
3036                 boolean over = false;
3037                 while ((indice >= 0) && !over) {
3038                         over = (get_listeBases().get(indice).getElementStructure() != -1);
3039                         indice--;
3040                 }
3041                 indice++;
3042                 if (over) {
3043                         indice++;
3044                 }
3045                 for (int i = indice; i < get_listeBases().size(); i++) {
3046                         list.add(i);
3047                         if (get_listeBases().get(i).getElementStructure() != -1) {
3048                                 return new ArrayList<Integer>();
3049                         }
3050                 }
3051                 return list;
3052         }
3053
3054         public ArrayList<Integer> find5Prime(int indice) {
3055                 ArrayList<Integer> list = new ArrayList<Integer>();
3056                 for (int i = 0; i <= indice; i++) {
3057                         list.add(i);
3058                         if (get_listeBases().get(i).getElementStructure() != -1) {
3059                                 return new ArrayList<Integer>();
3060                         }
3061                 }
3062                 return list;
3063         }
3064
3065         public static Double angle(Point2D.Double p1, Point2D.Double p2,
3066                         Point2D.Double p3) {
3067                 Double alpha = Math.atan2(p1.y - p2.y, p1.x - p2.x);
3068                 Double beta = Math.atan2(p3.y - p2.y, p3.x - p2.x);
3069                 Double angle = (beta - alpha);
3070
3071                 // Correction de l'angle pour le resituer entre 0 et 2PI
3072                 while (angle < 0.0 || angle > 2 * Math.PI) {
3073                         if (angle < 0.0)
3074                                 angle += 2 * Math.PI;
3075                         else if (angle > 2 * Math.PI)
3076                                 angle -= 2 * Math.PI;
3077                 }
3078                 return angle;
3079         }
3080
3081         public ArrayList<Integer> findNonPairedBaseGroup(Integer get_nearestBase) {
3082                 // detection 3', 5', bulge
3083                 ArrayList<Integer> list = new ArrayList<Integer>();
3084                 int indice = get_nearestBase;
3085                 boolean nonpairedUp = true, nonpairedDown = true;
3086                 while (indice < get_listeBases().size() && nonpairedUp) {
3087                         if (get_listeBases().get(indice).getElementStructure() == -1) {
3088                                 list.add(indice);
3089                                 indice++;
3090                         } else {
3091                                 nonpairedUp = false;
3092                         }
3093                 }
3094                 indice = get_nearestBase - 1;
3095                 while (indice >= 0 && nonpairedDown) {
3096                         if (get_listeBases().get(indice).getElementStructure() == -1) {
3097                                 list.add(indice);
3098                                 indice--;
3099                         } else {
3100                                 nonpairedDown = false;
3101                         }
3102                 }
3103                 return list;
3104         }
3105
3106         /*
3107          * public boolean getDrawn() { return _drawn; }
3108          */
3109
3110         public ArrayList<ModeleBP> getStructureAux() {
3111                 return _structureAux;
3112         }
3113
3114         /**
3115          * Translates a base number into its corresponding index. Although both
3116          * should be unique, base numbers are not necessarily contiguous, and
3117          * indices should be preferred for any reasonably complex algorithmic
3118          * treatment.
3119          * 
3120          * @param num
3121          *            The base number
3122          * @return The first index whose associated Base model has base number
3123          *         <code>num</code>, <code>-1</code> of no such base model exists.
3124          */
3125
3126         public int getIndexFromBaseNumber(int num) {
3127                 for (int i = 0; i < this._listeBases.size(); i++) {
3128                         if (_listeBases.get(i).getBaseNumber() == num) {
3129                                 return i;
3130                         }
3131                 }
3132                 return -1;
3133         }
3134
3135         /**
3136          * Adds a base pair to this RNA's structure. Tries to add it to the
3137          * secondary structure first, eventually adding it to the 'tertiary'
3138          * interactions if it clashes with the current secondary structure.
3139          * 
3140          * @param baseNumber5
3141          *            - Base number of the origin of this base pair
3142          * @param baseNumber3
3143          *            - Base number of the destination of this base pair
3144          */
3145
3146         public void addBPToStructureUsingNumbers(int baseNumber5, int baseNumber3) {
3147                 int i = getIndexFromBaseNumber(baseNumber5);
3148                 int j = getIndexFromBaseNumber(baseNumber3);
3149                 addBP(i, j);
3150         }
3151
3152         /**
3153          * Adds a base pair to this RNA's structure. Tries to add it to the
3154          * secondary structure first, possibly adding it to the 'tertiary'
3155          * interactions if it clashes with the current secondary structure.
3156          * 
3157          * @param number5
3158          *            - Base number of the origin of this base pair
3159          * @param number3
3160          *            - Base number of the destination of this base pair
3161          */
3162
3163         public void addBPToStructureUsingNumbers(int number5, int number3,
3164                         ModeleBP msbp) {
3165                 addBP(getIndexFromBaseNumber(number5), getIndexFromBaseNumber(number3),
3166                                 msbp);
3167         }
3168
3169         public void addBP(int index5, int index3) {
3170                 int i = index5;
3171                 int j = index3;
3172                 ModeleBase part5 = _listeBases.get(i);
3173                 ModeleBase part3 = _listeBases.get(j);
3174                 ModeleBP msbp = new ModeleBP(part5, part3);
3175                 addBP(i, j, msbp);
3176         }
3177
3178         public void addBP(int index5, int index3, ModeleBP msbp) {
3179                 int i = index5;
3180                 int j = index3;
3181
3182                 if (j < i) {
3183                         int k = j;
3184                         j = i;
3185                         i = k;
3186                 }
3187                 if (i != -1) {
3188                         for (int k = i; k <= j; k++) {
3189                                 ModeleBase tmp = _listeBases.get(k);
3190                                 int l = tmp.getElementStructure();
3191                                 if (l != -1) {
3192                                         if ((l <= i) || (l >= j)) {
3193                                                 addBPAux(i, j, msbp);
3194                                                 return;
3195                                         }
3196                                 }
3197                         }
3198                         addBPnow(i, j, msbp);
3199                 }
3200         }
3201
3202         public void removeBP(ModeleBP ms) {
3203                 if (_structureAux.contains(ms)) {
3204                         _structureAux.remove(ms);
3205                 } else {
3206                         ModeleBase m5 = ms.getPartner5();
3207                         ModeleBase m3 = ms.getPartner3();
3208                         int i = m5.getIndex();
3209                         int j = m3.getIndex();
3210                         if ((m5.getElementStructure() == m3.getIndex())
3211                                         && (m3.getElementStructure() == m5.getIndex())) {
3212                                 m5.removeElementStructure();
3213                                 m3.removeElementStructure();
3214                         }
3215                 }
3216         }
3217
3218         /**
3219          * Register base-pair, no question asked. More precisely, this function will
3220          * not try to determine if the base-pairs crosses any other.
3221          * 
3222          * @param i
3223          * @param j
3224          * @param msbp
3225          */
3226         private void addBPNow(int i, int j) {
3227                 if (j < i) {
3228                         int k = j;
3229                         j = i;
3230                         i = k;
3231                 }
3232
3233                 ModeleBase part5 = _listeBases.get(i);
3234                 ModeleBase part3 = _listeBases.get(j);
3235                 ModeleBP msbp = new ModeleBP(part5, part3);
3236                 addBPnow(i, j, msbp);
3237         }
3238
3239         /**
3240          * Register base-pair, no question asked. More precisely, this function will
3241          * not try to determine if the base-pairs crosses any other.
3242          * 
3243          * @param i
3244          * @param j
3245          * @param msbp
3246          */
3247         private void addBPnow(int i, int j, ModeleBP msbp) {
3248                 if (j < i) {
3249                         int k = j;
3250                         j = i;
3251                         i = k;
3252                 }
3253                 ModeleBase part5 = _listeBases.get(i);
3254                 ModeleBase part3 = _listeBases.get(j);
3255                 msbp.setPartner5(part5);
3256                 msbp.setPartner3(part3);
3257                 part5.setElementStructure(j, msbp);
3258                 part3.setElementStructure(i, msbp);
3259         }
3260
3261         public void addBPAux(int i, int j) {
3262                 ModeleBase part5 = _listeBases.get(i);
3263                 ModeleBase part3 = _listeBases.get(j);
3264                 ModeleBP msbp = new ModeleBP(part5, part3);
3265                 addBPAux(i, j, msbp);
3266         }
3267
3268         public void addBPAux(int i, int j, ModeleBP msbp) {
3269                 if (j < i) {
3270                         int k = j;
3271                         j = i;
3272                         i = k;
3273                 }
3274                 ModeleBase part5 = _listeBases.get(i);
3275                 ModeleBase part3 = _listeBases.get(j);
3276                 msbp.setPartner5(part5);
3277                 msbp.setPartner3(part3);
3278                 _structureAux.add(msbp);
3279         }
3280
3281         public ArrayList<ModeleBP> getBPsAt(int i) {
3282                 ArrayList<ModeleBP> result = new ArrayList<ModeleBP>();
3283                 if (_listeBases.get(i).getElementStructure() != -1) {
3284                         result.add(_listeBases.get(i).getStyleBP());
3285                 }
3286                 for (int k = 0; k < _structureAux.size(); k++) {
3287                         ModeleBP bp = _structureAux.get(k);
3288                         if ((bp.getPartner5().getIndex() == i)
3289                                         || (bp.getPartner3().getIndex() == i)) {
3290                                 result.add(bp);
3291                         }
3292                 }
3293                 return result;
3294
3295         }
3296
3297         public ModeleBP getBPStyle(int i, int j) {
3298                 ModeleBP result = null;
3299                 if (i > j) {
3300                         int k = j;
3301                         j = i;
3302                         i = k;
3303                 }
3304                 if (_listeBases.get(i).getElementStructure() == j) {
3305                         result = _listeBases.get(i).getStyleBP();
3306                 }
3307                 for (int k = 0; k < _structureAux.size(); k++) {
3308                         ModeleBP bp = _structureAux.get(k);
3309                         if ((bp.getPartner5().getIndex() == i)
3310                                         && (bp.getPartner3().getIndex() == j)) {
3311                                 result = bp;
3312                         }
3313                 }
3314                 return result;
3315         }
3316
3317         public ArrayList<ModeleBP> getSecStrBPs() {
3318                 ArrayList<ModeleBP> result = new ArrayList<ModeleBP>();
3319                 for (int i = 0; i < this.getSize(); i++) {
3320                         ModeleBase mb = _listeBases.get(i);
3321                         int k = mb.getElementStructure();
3322                         if ((k != -1) && (k > i)) {
3323                                 result.add(mb.getStyleBP());
3324                         }
3325                 }
3326                 return result;
3327         }
3328
3329         public ArrayList<ModeleBP> getAuxBPs() {
3330                 ArrayList<ModeleBP> result = new ArrayList<ModeleBP>();
3331                 for (ModeleBP bp : _structureAux) {
3332                         result.add(bp);
3333                 }
3334                 return result;
3335         }
3336
3337         public ArrayList<ModeleBP> getAllBPs() {
3338                 ArrayList<ModeleBP> result = new ArrayList<ModeleBP>();
3339                 result.addAll(getSecStrBPs());
3340                 result.addAll(getAuxBPs());
3341                 return result;
3342         }
3343
3344         public ArrayList<ModeleBP> getAuxBPs(int i) {
3345                 ArrayList<ModeleBP> result = new ArrayList<ModeleBP>();
3346                 for (ModeleBP bp : _structureAux) {
3347                         if ((bp.getPartner5().getIndex() == i)
3348                                         || (bp.getPartner3().getIndex() == i)) {
3349                                 result.add(bp);
3350                         }
3351                 }
3352                 return result;
3353         }
3354
3355         public void setBaseInnerColor(Color c) {
3356                 for (int i = 0; i < _listeBases.size(); i++) {
3357                         ModeleBase mb = _listeBases.get(i);
3358                         mb.getStyleBase().setBaseInnerColor(c);
3359                 }
3360         }
3361
3362         public void setBaseNumbersColor(Color c) {
3363                 for (int i = 0; i < _listeBases.size(); i++) {
3364                         ModeleBase mb = _listeBases.get(i);
3365                         mb.getStyleBase().setBaseNumberColor(c);
3366                 }
3367         }
3368
3369         public void setBaseNameColor(Color c) {
3370                 for (int i = 0; i < _listeBases.size(); i++) {
3371                         ModeleBase mb = _listeBases.get(i);
3372                         mb.getStyleBase().setBaseNameColor(c);
3373                 }
3374         }
3375
3376         public void setBaseOutlineColor(Color c) {
3377                 for (int i = 0; i < _listeBases.size(); i++) {
3378                         ModeleBase mb = _listeBases.get(i);
3379                         mb.getStyleBase().setBaseOutlineColor(c);
3380                 }
3381         }
3382
3383         public String getName() {
3384                 return _name;
3385         }
3386
3387         public void setName(String n) {
3388                 _name = n;
3389         }
3390
3391         public ArrayList<TextAnnotation> getAnnotations() {
3392                 return _listeAnnotations;
3393         }
3394
3395         public boolean removeAnnotation(TextAnnotation t) {
3396                 return _listeAnnotations.remove(t);
3397         }
3398
3399         public void addAnnotation(TextAnnotation t) {
3400                 _listeAnnotations.add(t);
3401         }
3402
3403         public void removeAnnotation(String filter) {
3404                 ArrayList<TextAnnotation> condamne = new ArrayList<TextAnnotation>();
3405                 for (TextAnnotation t : _listeAnnotations) {
3406                         if (t.getTexte().contains(filter)) {
3407                                 condamne.add(t);
3408                         }
3409                 }
3410                 for (TextAnnotation t : condamne) {
3411                         _listeAnnotations.remove(t);
3412                 }
3413         }
3414
3415         public void clearAnnotations() {
3416                 _listeAnnotations.clear();
3417         }
3418
3419         private boolean _strandEndsAnnotated = false;
3420
3421         public void autoAnnotateStrandEnds() {
3422                 if (!_strandEndsAnnotated) {
3423                         int tailleListBases = _listeBases.size();
3424                         boolean endAnnotate = false;
3425                         addAnnotation(new TextAnnotation("5'", _listeBases.get(0)));
3426                         for (int i = 0; i < _listeBases.size() - 1; i++) {
3427                                 int realposA = _listeBases.get(i).getBaseNumber();
3428                                 int realposB = _listeBases.get(i + 1).getBaseNumber();
3429                                 if (realposB - realposA != 1) {
3430                                         addAnnotation(new TextAnnotation("3'", _listeBases.get(i)));
3431                                         addAnnotation(new TextAnnotation("5'",
3432                                                         _listeBases.get(i + 1)));
3433                                         if (i + 1 == _listeBases.size() - 1) {
3434                                                 endAnnotate = true;
3435                                         }
3436                                 }
3437                         }
3438                         if (!endAnnotate) {
3439                                 addAnnotation(new TextAnnotation("3'",
3440                                                 _listeBases.get(tailleListBases - 1)));
3441                         }
3442                         _strandEndsAnnotated = true;
3443                 } else {
3444                         removeAnnotation("3'");
3445                         removeAnnotation("5'");
3446                         _strandEndsAnnotated = false;
3447                 }
3448         }
3449
3450         public void autoAnnotateHelices() {
3451                 Stack<Integer> p = new Stack<Integer>();
3452                 p.push(0);
3453                 int nbH = 1;
3454                 while (!p.empty()) {
3455                         int i = p.pop();
3456                         if (i < _listeBases.size()) {
3457                                 ModeleBase mb = _listeBases.get(i);
3458                                 int j = mb.getElementStructure();
3459                                 if (j == -1) {
3460                                         p.push(i + 1);
3461                                 } else {
3462                                         if (j > i) {
3463                                                 ModeleBase mbp = _listeBases.get(j);
3464                                                 p.push(j + 1);
3465                                                 ArrayList<ModeleBase> h = new ArrayList<ModeleBase>();
3466                                                 int k = 1;
3467                                                 while (mb.getElementStructure() == mbp.getIndex()) {
3468                                                         h.add(mb);
3469                                                         h.add(mbp);
3470                                                         mb = _listeBases.get(i + k);
3471                                                         mbp = _listeBases.get(j - k);
3472
3473                                                         k++;
3474                                                 }
3475                                                 try {
3476                                                         addAnnotation(new TextAnnotation("H" + nbH++, h,
3477                                                                         TextAnnotation.AnchorType.HELIX));
3478                                                 } catch (Exception e) {
3479                                                         e.printStackTrace();
3480                                                 }
3481                                                 p.push(i + k);
3482                                         }
3483                                 }
3484                         }
3485                 }
3486         }
3487
3488         public void autoAnnotateTerminalLoops() {
3489                 Stack<Integer> p = new Stack<Integer>();
3490                 p.push(0);
3491                 int nbT = 1;
3492                 while (!p.empty()) {
3493                         int i = p.pop();
3494                         if (i < _listeBases.size()) {
3495                                 ModeleBase mb = _listeBases.get(i);
3496                                 int j = mb.getElementStructure();
3497                                 if (j == -1) {
3498                                         int k = 1;
3499                                         ArrayList<ModeleBase> t = new ArrayList<ModeleBase>();
3500                                         while ((i + k < getSize())
3501                                                         && (mb.getElementStructure() == -1)) {
3502                                                 t.add(mb);
3503                                                 mb = _listeBases.get(i + k);
3504                                                 k++;
3505                                         }
3506                                         if (mb.getElementStructure() != -1) {
3507                                                 if (mb.getElementStructure() == i - 1) {
3508                                                         try {
3509                                                                 t.add(_listeBases.get(i - 1));
3510                                                                 t.add(_listeBases.get(i + k - 1));
3511                                                                 addAnnotation(new TextAnnotation("T" + nbT++,
3512                                                                                 t, TextAnnotation.AnchorType.LOOP));
3513                                                         } catch (Exception e) {
3514                                                                 e.printStackTrace();
3515                                                         }
3516                                                 }
3517                                                 p.push(i + k - 1);
3518                                         }
3519
3520                                 } else {
3521                                         if (j > i) {
3522                                                 p.push(j + 1);
3523                                                 p.push(i + 1);
3524                                         }
3525                                 }
3526                         }
3527                 }
3528         }
3529
3530         public void autoAnnotateInteriorLoops() {
3531                 Stack<Integer> p = new Stack<Integer>();
3532                 p.push(0);
3533                 int nbT = 1;
3534                 while (!p.empty()) {
3535                         int i = p.pop();
3536                         if (i < _listeBases.size()) {
3537                                 ModeleBase mb = _listeBases.get(i);
3538                                 int j = mb.getElementStructure();
3539                                 if (j == -1) {
3540                                         int k = i + 1;
3541                                         ArrayList<ModeleBase> t = new ArrayList<ModeleBase>();
3542                                         boolean terminal = true;
3543                                         while ((k < getSize())
3544                                                         && ((mb.getElementStructure() >= i) || (mb
3545                                                                         .getElementStructure() == -1))) {
3546                                                 t.add(mb);
3547                                                 mb = _listeBases.get(k);
3548                                                 if ((mb.getElementStructure() == -1)
3549                                                                 || (mb.getElementStructure() < k))
3550                                                         k++;
3551                                                 else {
3552                                                         p.push(k);
3553                                                         terminal = false;
3554                                                         k = mb.getElementStructure();
3555                                                 }
3556                                         }
3557                                         if (mb.getElementStructure() != -1) {
3558                                                 if ((mb.getElementStructure() == i - 1) && !terminal) {
3559                                                         try {
3560                                                                 t.add(_listeBases.get(i - 1));
3561                                                                 t.add(_listeBases.get(k - 1));
3562                                                                 addAnnotation(new TextAnnotation("I" + nbT++,
3563                                                                                 t, TextAnnotation.AnchorType.LOOP));
3564                                                         } catch (Exception e) {
3565                                                                 e.printStackTrace();
3566                                                         }
3567                                                         p.push(k - 1);
3568                                                 }
3569                                         }
3570                                 } else {
3571                                         if (j > i) {
3572                                                 p.push(i + 1);
3573                                         }
3574                                 }
3575                         }
3576                 }
3577         }
3578
3579         @SuppressWarnings("unchecked")
3580         public TextAnnotation getAnnotation(TextAnnotation.AnchorType type,
3581                         ModeleBase base) {
3582                 TextAnnotation result = null;
3583                 for (TextAnnotation t : _listeAnnotations) {
3584                         if (t.getType() == type) {
3585                                 switch (type) {
3586                                 case BASE:
3587                                         if (base == (ModeleBase) t.getAncrage())
3588                                                 return t;
3589                                         break;
3590                                 case HELIX:
3591                                 case LOOP: {
3592                                         ArrayList<ModeleBase> mbl = (ArrayList<ModeleBase>) t
3593                                                         .getAncrage();
3594                                         if (mbl.contains(base))
3595                                                 return t;
3596                                 }
3597                                         break;
3598                                 }
3599                         }
3600                 }
3601                 return result;
3602         }
3603
3604         public void addChemProbAnnotation(ChemProbAnnotation cpa) {
3605                 //System.err.println(cpa.isOut());
3606                 _chemProbAnnotations.add(cpa);
3607         }
3608
3609         public ArrayList<ChemProbAnnotation> getChemProbAnnotations() {
3610                 return _chemProbAnnotations;
3611         }
3612
3613         public void setColorMapValues(Double[] values, ModeleColorMap cm) {
3614                 setColorMapValues(values, cm, false);
3615         }
3616
3617         public void adaptColorMapToValues(ModeleColorMap cm) {
3618                 double min = Double.MAX_VALUE;
3619                 double max = Double.MIN_VALUE;
3620                 for (int i = 0; i < Math.min(_listeBases.size(), _listeBases.size()); i++) {
3621                         ModeleBase mb = _listeBases.get(i);
3622                         max = Math.max(max, mb.getValue());
3623                         min = Math.min(min, mb.getValue());
3624                 }
3625                 cm.rescale(min, max);
3626         }
3627         
3628         
3629         private ArrayList<Double> loadDotPlot(StreamTokenizer st)
3630         {
3631                 ArrayList<Double> result = new ArrayList<Double>();
3632                 try {
3633                         boolean inSeq = false;
3634                         String sequence = "";
3635                         ArrayList<Double> accumulator = new ArrayList<Double>();
3636                         int type = st.nextToken();
3637                         Hashtable<Couple<Integer,Integer>,Double> BP = new Hashtable<Couple<Integer,Integer>,Double>();
3638                         while (type != StreamTokenizer.TT_EOF) {
3639                                 switch (type) {
3640                                         case (StreamTokenizer.TT_NUMBER):
3641                                                 accumulator.add(st.nval);
3642                                                 break;
3643                                         case (StreamTokenizer.TT_EOL):
3644                                                 break;
3645                                         case (StreamTokenizer.TT_WORD):
3646                                                 if (st.sval.equals("/sequence"))
3647                                                 {
3648                                                         inSeq = true;
3649                                                 }
3650                                                 else if (st.sval.equals("ubox"))
3651                                                 {
3652                                                         int i = accumulator.get(accumulator.size()-3).intValue()-1;
3653                                                         int j = accumulator.get(accumulator.size()-2).intValue()-1;
3654                                                         double val = accumulator.get(accumulator.size()-1);
3655                                                         //System.err.println((char) type);                      
3656                                                         BP.put(new Couple<Integer, Integer>(Math.min(i, j), Math.max(i, j)),val*val);
3657                                                         accumulator.clear();
3658                                                 }
3659                                                 else if (inSeq)
3660                                                 {
3661                                                         sequence += st.sval;
3662                                                 }
3663                                                 break;
3664                                         case ')':
3665                                                 inSeq = false;
3666                                         break;
3667                                 }
3668                                 type = st.nextToken();
3669                         }
3670                         for (int i = 0; i < getSize(); i++) {
3671                                 int j = getBaseAt(i).getElementStructure();
3672                                 if (j != -1) {
3673                                         Couple<Integer, Integer> coor = new Couple<Integer, Integer>(
3674                                                         Math.min(i, j), Math.max(i, j));
3675                                         if (BP.containsKey(coor)) {
3676                                                 result.add(BP.get(coor));
3677                                         } else {
3678                                                 result.add(0.);
3679                                         }
3680                                 } else {
3681                                         double acc = 1.0;
3682                                         for (int k = 0; k < getSize(); k++) {
3683                                                 Couple<Integer, Integer> coor = new Couple<Integer, Integer>(
3684                                                                 Math.min(i, k), Math.max(i, k));
3685                                                 if (BP.containsKey(coor)) {
3686                                                         acc -= BP.get(coor);
3687                                                 }
3688                                         }
3689                                         result.add(acc);
3690                                 }
3691                         }
3692                 } catch (IOException e) {
3693                         // TODO Auto-generated catch block
3694                         e.printStackTrace();
3695                 }
3696                 return result;
3697         }
3698
3699         public void readValues(Reader r, ModeleColorMap cm) {
3700                 try {
3701                         StreamTokenizer st = new StreamTokenizer(r);
3702                         st.eolIsSignificant(true);
3703                         st.wordChars('/', '/');
3704                         st.parseNumbers();
3705                         ArrayList<Double> vals = new ArrayList<Double>();
3706                         ArrayList<Double> curVals = new ArrayList<Double>();
3707                         int type = st.nextToken();
3708                         boolean isDotPlot = false;
3709                         if (type=='%')
3710                         {
3711                                 vals = loadDotPlot(st);
3712                                 isDotPlot = true;
3713                         }
3714                         else
3715                         {       
3716                                 while (type != StreamTokenizer.TT_EOF) {
3717                                         switch (type) {
3718                                         case (StreamTokenizer.TT_NUMBER):
3719                                                 curVals.add(st.nval);
3720                                                 break;
3721                                         case (StreamTokenizer.TT_EOL):
3722                                                 if (curVals.size() > 0) {
3723                                                         vals.add(curVals.get(curVals.size()-1));
3724                                                         curVals = new ArrayList<Double>();
3725                                                 }
3726                                                 break;
3727                                         }
3728                                         type = st.nextToken();
3729                                 }
3730                                 if (curVals.size() > 0)
3731                                         vals.add(curVals.get(curVals.size()-1));
3732                         }
3733
3734                         Double[] v = new Double[vals.size()];
3735                         for (int i = 0; i < Math.min(vals.size(), getSize()); i++) {
3736                                 v[i] = vals.get(i);
3737                         }
3738                         setColorMapValues(v, cm, true);
3739                         if (isDotPlot)
3740                         {
3741                                 cm.setMinValue(0.0);
3742                                 cm.setMaxValue(1.0);
3743                         }
3744                 } catch (IOException e) {
3745                         e.printStackTrace();
3746                 }
3747         }
3748
3749         public void setColorMapValues(Double[] values, ModeleColorMap cm,
3750                         boolean rescaleColorMap) {
3751                 if (values.length > 0) {
3752                         for (int i = 0; i < Math.min(values.length, _listeBases.size()); i++) {
3753                                 ModeleBase mb = _listeBases.get(i);
3754                                 mb.setValue(values[i]);
3755                         }
3756                         if (rescaleColorMap) {
3757                                 adaptColorMapToValues(cm);
3758                         }
3759                 }
3760         }
3761
3762         public Double[] getColorMapValues() {
3763                 Double[] values = new Double[_listeBases.size()];
3764                 for (int i = 0; i < _listeBases.size(); i++) {
3765                         values[i] = _listeBases.get(i).getValue();
3766                 }
3767                 return values;
3768         }
3769
3770         public void rescaleColorMap(ModeleColorMap cm) {
3771                 Double max = Double.MIN_VALUE;
3772                 Double min = Double.MAX_VALUE;
3773                 for (int i = 0; i < _listeBases.size(); i++) {
3774                         Double value = _listeBases.get(i).getValue();
3775                         max = Math.max(max, value);
3776                         min = Math.min(min, value);
3777                 }
3778                 cm.rescale(min, max);
3779         }
3780
3781         public void addBase(ModeleBase mb) {
3782                 _listeBases.add(mb);
3783         }
3784
3785         public void setSequence(String s) {
3786                 setSequence(RNA.explodeSequence(s));
3787         }
3788
3789         public void setSequence(List<String> s) {
3790                 int i = 0;
3791                 int j = 0;
3792                 while ((i < s.size()) && (j < _listeBases.size())) {
3793                         ModeleBase mb = _listeBases.get(j);
3794                         if (mb instanceof ModeleBaseNucleotide) {
3795                                 ((ModeleBaseNucleotide) mb).setBase(s.get(i));
3796                                 i++;
3797                                 j++;
3798                         } else if (mb instanceof ModeleBasesComparison) {
3799                                 ((ModeleBasesComparison) mb)
3800                                                 .setBase1(((s.get(i).length() > 0) ? s.get(i).charAt(0)
3801                                                                 : ' '));
3802                                 ((ModeleBasesComparison) mb)
3803                                                 .setBase2(((s.get(i + 1).length() > 0) ? s.get(i + 1)
3804                                                                 .charAt(0) : ' '));
3805                                 i += 2;
3806                                 j++;
3807                         } else
3808                                 j++;
3809                 }
3810                 for (i = _listeBases.size(); i < s.size(); i++) {
3811                         _listeBases.add(new ModeleBaseNucleotide(s.get(i), i));
3812                 }
3813         }
3814
3815         public void eraseSequence() {
3816                 int j = 0;
3817                 while ((j < _listeBases.size())) {
3818                         ModeleBase mb = _listeBases.get(j);
3819                         if (mb instanceof ModeleBaseNucleotide) {
3820                                 ((ModeleBaseNucleotide) mb).setBase("");
3821                                 j++;
3822                         } else if (mb instanceof ModeleBasesComparison) {
3823                                 ((ModeleBasesComparison) mb).setBase1(' ');
3824                                 ((ModeleBasesComparison) mb).setBase2(' ');
3825                                 j++;
3826                         } else
3827                                 j++;
3828                 }
3829         }
3830
3831         public RNA clone() {
3832                 try {
3833                         ByteArrayOutputStream out = new ByteArrayOutputStream();
3834                         ObjectOutputStream oout = new ObjectOutputStream(out);
3835                         oout.writeObject(this);
3836
3837                         ObjectInputStream in = new ObjectInputStream(
3838                                         new ByteArrayInputStream(out.toByteArray()));
3839                         return (RNA) in.readObject();
3840                 } catch (Exception e) {
3841                         throw new RuntimeException("cannot clone class ["
3842                                         + this.getClass().getName() + "] via serialization: "
3843                                         + e.toString());
3844                 }
3845         }
3846
3847         /**
3848          * Returns the base at index <code>index</code>. Indices are contiguous in
3849          * the sequence over an interval <code>[0,this.getSize()-1]</code>, where
3850          * <code>n</code> is the length of the sequence.
3851          * 
3852          * @param index
3853          *            The index, <code>0 &le; index < this.getSize()</code>, of the
3854          *            base model
3855          * @return The base model of index <code>index</code>
3856          */
3857         public ModeleBase getBaseAt(int index) {
3858                 return this._listeBases.get(index);
3859         }
3860
3861         /**
3862          * Returns the set of bases of indices in <code>indices</code>. Indices are
3863          * contiguous in the sequence, and belong to an interval
3864          * <code>[0,n-1]</code>, where <code>n</code> is the length of the sequence.
3865          * 
3866          * @param indices
3867          *            A Collection of indices <code>i</code>,
3868          *            <code>0 &le; index < this.getSize()</code>, where some base
3869          *            models are found.
3870          * @return A list of base model of indices in <code>indices</code>
3871          */
3872         public ArrayList<ModeleBase> getBasesAt(
3873                         Collection<? extends Integer> indices) {
3874                 ArrayList<ModeleBase> mbs = new ArrayList<ModeleBase>();
3875                 for (int i : indices) {
3876                         mbs.add(getBaseAt(i));
3877                 }
3878                 return mbs;
3879         }
3880
3881         public ArrayList<ModeleBase> getBasesBetween(int from, int to) {
3882                 ArrayList<ModeleBase> mbs = new ArrayList<ModeleBase>();
3883                 int bck = Math.min(from, to);
3884                 to = Math.max(from, to);
3885                 from = bck;
3886                 for (int i = from; i <= to; i++) {
3887                         mbs.add(getBaseAt(i));
3888                 }
3889                 return mbs;
3890         }
3891
3892         public void addHighlightRegion(HighlightRegionAnnotation n) {
3893                 _listeRegionHighlights.add(n);
3894         }
3895
3896         public void removeHighlightRegion(HighlightRegionAnnotation n) {
3897                 _listeRegionHighlights.remove(n);
3898         }
3899
3900         public void removeChemProbAnnotation(ChemProbAnnotation a) {
3901                 _chemProbAnnotations.remove(a);
3902         }
3903
3904         public void clearChemProbAnnotations() {
3905                 _chemProbAnnotations.clear();
3906         }
3907
3908         public void addHighlightRegion(int from, int to, Color fill, Color outline,
3909                         double radius) {
3910                 _listeRegionHighlights.add(new HighlightRegionAnnotation(
3911                                 getBasesBetween(from, to), fill, outline, radius));
3912         }
3913
3914         public void addHighlightRegion(int from, int to) {
3915                 _listeRegionHighlights.add(new HighlightRegionAnnotation(
3916                                 getBasesBetween(from, to)));
3917         }
3918
3919         public ArrayList<HighlightRegionAnnotation> getHighlightRegion() {
3920                 return _listeRegionHighlights;
3921         }
3922
3923         /**
3924          * Rotates the RNA coordinates by a certain angle
3925          * 
3926          * @param angleDegres
3927          *            Rotation angle, in degrees
3928          */
3929         public void globalRotation(Double angleDegres) {
3930                 if (_listeBases.size() > 0) {
3931
3932                         // angle en radian
3933                         Double angle = angleDegres * Math.PI / 180;
3934
3935                         // initialisation du minimum et dumaximum
3936                         Double maxX = _listeBases.get(0).getCoords().x;
3937                         Double maxY = _listeBases.get(0).getCoords().y;
3938                         Double minX = _listeBases.get(0).getCoords().x;
3939                         Double minY = _listeBases.get(0).getCoords().y;
3940                         // mise a jour du minimum et du maximum
3941                         for (int i = 0; i < _listeBases.size(); i++) {
3942                                 if (_listeBases.get(i).getCoords().getX() < minX)
3943                                         minX = _listeBases.get(i).getCoords().getX();
3944                                 if (_listeBases.get(i).getCoords().getY() < minY)
3945                                         minY = _listeBases.get(i).getCoords().getY();
3946                                 if (_listeBases.get(i).getCoords().getX() > maxX)
3947                                         maxX = _listeBases.get(i).getCoords().getX();
3948                                 if (_listeBases.get(i).getCoords().getX() > maxY)
3949                                         maxY = _listeBases.get(i).getCoords().getY();
3950                         }
3951                         // creation du point central
3952                         Point2D.Double centre = new Point2D.Double((maxX - minX) / 2,
3953                                         (maxY - minY) / 2);
3954                         Double x, y;
3955                         for (int i = 0; i < _listeBases.size(); i++) {
3956                                 // application de la rotation au centre de chaque base
3957                                 // x' = cos(theta)*(x-xc) - sin(theta)*(y-yc) + xc
3958                                 x = Math.cos(angle)
3959                                                 * (_listeBases.get(i).getCenter().getX() - centre.x)
3960                                                 - Math.sin(angle)
3961                                                 * (_listeBases.get(i).getCenter().getY() - centre.y)
3962                                                 + centre.x;
3963                                 // y' = sin(theta)*(x-xc) + cos(theta)*(y-yc) + yc
3964                                 y = Math.sin(angle)
3965                                                 * (_listeBases.get(i).getCenter().getX() - centre.x)
3966                                                 + Math.cos(angle)
3967                                                 * (_listeBases.get(i).getCenter().getY() - centre.y)
3968                                                 + centre.y;
3969                                 _listeBases.get(i).setCenter(new Point2D.Double(x, y));
3970
3971                                 // application de la rotation au coordonnees de chaque
3972                                 // base
3973                                 // x' = cos(theta)*(x-xc) - sin(theta)*(y-yc) + xc
3974                                 x = Math.cos(angle)
3975                                                 * (_listeBases.get(i).getCoords().getX() - centre.x)
3976                                                 - Math.sin(angle)
3977                                                 * (_listeBases.get(i).getCoords().getY() - centre.y)
3978                                                 + centre.x;
3979                                 // y' = sin(theta)*(x-xc) + cos(theta)*(y-yc) + yc
3980                                 y = Math.sin(angle)
3981                                                 * (_listeBases.get(i).getCoords().getX() - centre.x)
3982                                                 + Math.cos(angle)
3983                                                 * (_listeBases.get(i).getCoords().getY() - centre.y)
3984                                                 + centre.y;
3985                                 _listeBases.get(i).setCoords(new Point2D.Double(x, y));
3986                         }
3987                 }
3988         }
3989         
3990         private static double MIN_DISTANCE = 10.;
3991         
3992         
3993         /**
3994          * Flip an helix around its supporting base
3995          */
3996         public void flipHelix(Point h) {
3997                 if (h.x!=-1 && h.y!=-1 && h.x!=h.y)
3998                 {
3999                         int hBeg=h.x;
4000                         int hEnd=h.y;
4001                         Point2D.Double A = getCoords(hBeg);
4002                         Point2D.Double B = getCoords(hEnd);
4003                         Point2D.Double AB = new Point2D.Double(B.x - A.x, B.y - A.y);
4004                         double normAB = Math.sqrt(AB.x * AB.x + AB.y * AB.y);
4005                         // Creating a coordinate system centered on A and having
4006                         // unit x-vector Ox.
4007                         Point2D.Double O = A;
4008                         Point2D.Double Ox = new Point2D.Double(AB.x / normAB, AB.y / normAB);
4009                         Hashtable<Integer,Point2D.Double> old = new Hashtable<Integer,Point2D.Double>(); 
4010                         for (int i = hBeg + 1; i < hEnd; i++) {
4011                                 Point2D.Double P = getCoords(i);
4012                                 Point2D.Double nP = project(O, Ox, P);
4013                                 old.put(i, nP);
4014                                 setCoord(i, nP);
4015                                 Point2D.Double Center = getCenter(i);
4016                                 setCenter(i, project(O, Ox, Center));
4017                         }
4018                 }
4019         }
4020
4021         public static Point2D.Double project(Point2D.Double O, Point2D.Double Ox,
4022                         Point2D.Double C) {
4023                 Point2D.Double OC = new Point2D.Double(C.x - O.x, C.y - O.y);
4024                 // Projection of OC on OI => OX
4025                 double normOX = (Ox.x * OC.x + Ox.y * OC.y);
4026                 Point2D.Double OX = new Point2D.Double((normOX * Ox.x), (normOX * Ox.y));
4027                 // Portion of OC orthogonal to Ox => XC
4028                 Point2D.Double XC = new Point2D.Double(OC.x - OX.x, OC.y - OX.y);
4029                 // Reflexive image of C with respect to Ox => CP
4030                 Point2D.Double OCP = new Point2D.Double(OX.x - XC.x, OX.y - XC.y);
4031                 Point2D.Double CP = new Point2D.Double(O.x + OCP.x, O.y + OCP.y);
4032                 return CP;
4033         }
4034
4035
4036         public boolean testDirectionality(int i, int j, int k) {
4037
4038                 // Which direction are we heading toward?
4039                 Point2D.Double pi = getCoords(i);
4040                 Point2D.Double pj = getCoords(j);
4041                 Point2D.Double pk = getCoords(k);
4042                 return testDirectionality(pi, pj, pk);
4043         }
4044
4045         public static boolean testDirectionality(Point2D.Double pi,
4046                         Point2D.Double pj, Point2D.Double pk) {
4047
4048                 // Which direction are we heading toward?
4049                 double test = (pj.x - pi.x) * (pk.y - pj.y) - (pj.y - pi.y)
4050                                 * (pk.x - pj.x);
4051                 return test < 0.0;
4052         }
4053
4054         public double getOrientation() {
4055                 double maxDist = Double.MIN_VALUE;
4056                 double angle = 0;
4057                 for (int i = 0; i < _listeBases.size(); i++) {
4058                         ModeleBase b1 = _listeBases.get(i);
4059                         for (int j = i + 1; j < _listeBases.size(); j++) {
4060                                 ModeleBase b2 = _listeBases.get(j);
4061                                 Point2D.Double p1 = b1._coords.toPoint2D();
4062                                 Point2D.Double p2 = b2._coords.toPoint2D();
4063                                 double dist = p1.distance(p2);
4064                                 if (dist > maxDist) {
4065                                         maxDist = dist;
4066                                         angle = computeAngle(p1, p2);
4067                                 }
4068                         }
4069                 }
4070                 return angle;
4071         }
4072
4073         public boolean hasVirtualLoops() {
4074                 boolean consecutiveBPs = false;
4075                 for (int i = 0; i < _listeBases.size(); i++) {
4076                         int j = _listeBases.get(i).getElementStructure();
4077                         if (j == i + 1) {
4078                                 consecutiveBPs = true;
4079                         }
4080
4081                 }
4082                 return ((_drawMode != DRAW_MODE_LINEAR)
4083                                 && (_drawMode != DRAW_MODE_CIRCULAR) && (consecutiveBPs));
4084         }
4085
4086         public String getHTMLDescription() {
4087                 String result = "<table>";
4088                 result += "<tr><td><b>Name:</b></td><td>" + this._name + "</td></tr>";
4089                 result += "<tr><td><b>Length:</b></td><td>" + this.getSize()
4090                                 + " nts</td></tr>";
4091                 result += "<tr><td><b>Base-pairs:</b></td><td>"
4092                                 + this.getAllBPs().size() + " </td></tr>";
4093                 return result + "</table>";
4094         }
4095
4096         public String getID() {
4097                 return _id;
4098         }
4099
4100         public void setID(String id) {
4101                 _id = id;
4102         }
4103
4104         public static ArrayList<Integer> getGapPositions(String gapString) {
4105                 ArrayList<Integer> result = new ArrayList<Integer>();
4106                 for (int i = 0; i < gapString.length(); i++) {
4107                         char c = gapString.charAt(i);
4108                         if (c == '.' || c == ':') {
4109                                 result.add(i);
4110                         }
4111                 }
4112                 return result;
4113         }
4114
4115         public RNA restrictTo(String gapString) {
4116                 return restrictTo(getGapPositions(gapString));
4117         }
4118
4119         public RNA restrictTo(ArrayList<Integer> positions) {
4120                 RNA result = new RNA();
4121                 String oldSeq = this.getSeq();
4122                 String newSeq = "";
4123                 HashSet<Integer> removedPos = new HashSet<Integer>(positions);
4124                 int[] matching = new int[oldSeq.length()];
4125                 int j = 0;
4126                 for (int i = 0; i < oldSeq.length(); i++) {
4127                         matching[i] = j;
4128                         if (!removedPos.contains(i)) {
4129                                 newSeq += oldSeq.charAt(i);
4130                                 j++;
4131                         }
4132                 }
4133                 result.setRNA(newSeq);
4134                 for (ModeleBP m : getAllBPs()) {
4135                         if (removedPos.contains(m.getIndex5())
4136                                         || removedPos.contains(m.getIndex3())) {
4137                                 int i5 = matching[m.getIndex5()];
4138                                 int i3 = matching[m.getIndex3()];
4139                                 ModeleBP msbp = new ModeleBP(result.getBaseAt(i5),
4140                                                 result.getBaseAt(i3), m.getEdgePartner5(),
4141                                                 m.getEdgePartner3(), m.getStericity());
4142                                 result.addBP(i5, i3, msbp);
4143                         }
4144                 }
4145                 return result;
4146         }
4147
4148         public void rescale(double d) {
4149                 for (ModeleBase mb : _listeBases) {
4150                         mb._coords.x *= d;
4151                         mb._coords.y *= d;
4152                         mb._center.x *= d;
4153                         mb._center.y *= d;
4154                 }
4155         }
4156
4157         /**
4158          * Necessary for DrawRNATemplate (which is why the method is
4159          * package-visible).
4160          */
4161         ArrayList<ModeleBase> getListeBases() {
4162                 return _listeBases;
4163         }
4164 }