JAL-3026 srcjar files for VARNA and log4j
[jalview.git] / srcjar / fr / orsay / lri / varna / models / rna / DrawRNATemplate.java
diff --git a/srcjar/fr/orsay/lri/varna/models/rna/DrawRNATemplate.java b/srcjar/fr/orsay/lri/varna/models/rna/DrawRNATemplate.java
new file mode 100644 (file)
index 0000000..37b41b8
--- /dev/null
@@ -0,0 +1,1421 @@
+/**
+ * File written by Raphael Champeimont
+ * UMR 7238 Genomique des Microorganismes
+ */
+package fr.orsay.lri.varna.models.rna;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Line2D;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
+import fr.orsay.lri.varna.models.VARNAConfig;
+import fr.orsay.lri.varna.models.geom.ComputeArcCenter;
+import fr.orsay.lri.varna.models.geom.ComputeEllipseAxis;
+import fr.orsay.lri.varna.models.geom.CubicBezierCurve;
+import fr.orsay.lri.varna.models.geom.HalfEllipse;
+import fr.orsay.lri.varna.models.geom.LinesIntersect;
+import fr.orsay.lri.varna.models.geom.MiscGeom;
+import fr.orsay.lri.varna.models.templates.DrawRNATemplateCurveMethod;
+import fr.orsay.lri.varna.models.templates.DrawRNATemplateMethod;
+import fr.orsay.lri.varna.models.templates.RNANodeValueTemplate;
+import fr.orsay.lri.varna.models.templates.RNANodeValueTemplateBasePair;
+import fr.orsay.lri.varna.models.templates.RNATemplate;
+import fr.orsay.lri.varna.models.templates.RNATemplateAlign;
+import fr.orsay.lri.varna.models.templates.RNATemplateDrawingAlgorithmException;
+import fr.orsay.lri.varna.models.templates.RNATemplateMapping;
+import fr.orsay.lri.varna.models.templates.RNATemplate.EdgeEndPointPosition;
+import fr.orsay.lri.varna.models.templates.RNATemplate.In1Is;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateUnpairedSequence;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
+import fr.orsay.lri.varna.models.treealign.Tree;
+
+public class DrawRNATemplate {
+       private RNA rna;
+       private RNATemplateMapping mapping;
+       private ArrayList<ModeleBase> _listeBases;
+       
+       
+       public DrawRNATemplate(RNA rna) {
+               this.rna = rna;
+               this._listeBases = rna.getListeBases();
+       }
+       
+       public RNATemplateMapping getMapping() {
+               return mapping;
+       }
+       
+       
+       
+       /**
+        * Draw this RNA like the given template.
+        * The helixLengthAdjustmentMethod argument tells what to do in case
+        * some helices are of a different length in the template and the
+        * actual helix. See class DrawRNATemplateMethod above for possible values.
+        * @param straightBulges 
+        */
+       public void drawRNATemplate(
+                       RNATemplate template,
+                       VARNAConfig conf,
+                       DrawRNATemplateMethod helixLengthAdjustmentMethod,
+                       DrawRNATemplateCurveMethod curveMethod, boolean straightBulges)
+       throws RNATemplateDrawingAlgorithmException {
+               
+               // debug
+//             try {
+//                     RNA perfectMatchingRNA = template.toRNA();
+//                     System.out.println("An RNA that would perfectly match this template would be:");
+//                     System.out.println(perfectMatchingRNA.getStructDBN());
+//             } catch (ExceptionInvalidRNATemplate e) {
+//                     e.printStackTrace();
+//             }
+               
+               mapping = RNATemplateAlign.mapRNAWithTemplate(rna, template);
+               //System.out.println(mapping.showCompact(this));
+               
+               // debug
+//                     RNATemplateAlign.printMapping(mapping, template, getSeq());
+//                     try {
+//                             TreeGraphviz.treeToGraphvizPostscript(alignment, "alignment_graphviz.ps");
+//                     } catch (IOException e) {
+//                             e.printStackTrace();
+//                     }
+               
+               
+
+               Iterator<RNATemplateElement> iter;
+               double globalIncreaseFactor = 1;
+               Map<RNATemplateHelix, Point2D.Double> translateVectors = null;
+               if (helixLengthAdjustmentMethod == DrawRNATemplateMethod.MAXSCALINGFACTOR) {
+                       // Compute increase factors for helices.
+                       Map<RNATemplateHelix, Double> lengthIncreaseFactor = new HashMap<RNATemplateHelix, Double>();
+                       double maxLengthIncreaseFactor = Double.NEGATIVE_INFINITY;
+                       //RNATemplateHelix maxIncreaseHelix = null;
+                       iter = template.rnaIterator();
+                       while (iter.hasNext()) {
+                               RNATemplateElement element = iter.next();
+                               if (element instanceof RNATemplateHelix
+                                               && mapping.getAncestor(element) != null
+                                               && !lengthIncreaseFactor.containsKey(element)) {
+                                       RNATemplateHelix helix = (RNATemplateHelix) element;
+                                       int[] basesInHelixArray = RNATemplateAlign.intArrayFromList(mapping.getAncestor(helix));
+                                       Arrays.sort(basesInHelixArray);
+                                       double l = computeLengthIncreaseFactor(basesInHelixArray, helix, straightBulges);
+                                       lengthIncreaseFactor.put(helix, l);
+                                       if (l > maxLengthIncreaseFactor) {
+                                               maxLengthIncreaseFactor = l;
+                                               //maxIncreaseHelix = helix;
+                                       }
+                               }
+                       }
+                       
+                       // debug
+                       //System.out.println("Max helix length increase factor = " + maxLengthIncreaseFactor + " reached with helix " + maxIncreaseHelix);;
+                       
+                       globalIncreaseFactor = Math.max(1, maxLengthIncreaseFactor);
+                       
+               } else if (helixLengthAdjustmentMethod == DrawRNATemplateMethod.HELIXTRANSLATE) {
+                       try {
+                               // Now we need to propagate this helices translations
+                               Tree<RNANodeValueTemplate> templateAsTree = template.toTree();
+                               translateVectors = computeHelixTranslations(templateAsTree, mapping, straightBulges);
+                       
+                       } catch (ExceptionInvalidRNATemplate e) {
+                               throw (new RNATemplateDrawingAlgorithmException("ExceptionInvalidRNATemplate: " + e.getMessage()));
+                       }
+               }
+               
+               // Allocate the coords and centers arrays
+               // We create Point2D.Double objects in it but the algorithms
+               // we use may choose to create new Point2D.Double objects or to
+               // modify those created here.
+               Point2D.Double[] coords = new Point2D.Double[_listeBases.size()];
+               Point2D.Double[] centers = new Point2D.Double[_listeBases.size()];
+               double[] angles = new double[_listeBases.size()];
+               for (int i = 0; i < _listeBases.size(); i++) {
+                       coords[i] = new Point2D.Double(0, 0);
+                       centers[i] = new Point2D.Double(0, 0);
+               }
+               
+               boolean computeCoords = true;
+               while (computeCoords) {
+                       computeCoords = false;
+                       // Compute coords and centers
+                       Set<RNATemplateHelix> alreadyDrawnHelixes = new HashSet<RNATemplateHelix>();
+                       RNATemplateHelix lastMappedHelix = null;
+                       EdgeEndPoint howWeGotOutOfLastHelix = null;
+                       int howWeGotOutOfLastHelixBaseIndex = 0;
+                       iter = template.rnaIterator();
+                       RNATemplateElement element = null;
+                       while (iter.hasNext()) {
+                               element = iter.next();
+                               if (element instanceof RNATemplateHelix
+                                               && mapping.getAncestor(element) != null) {
+                                       // We have a mapping between an helix in the RNA sequence
+                                       // and an helix in the template.
+                                       
+                                       RNATemplateHelix helix = (RNATemplateHelix) element;
+                                       boolean firstTimeWeMeetThisHelix;
+                                       int[] basesInHelixArray = RNATemplateAlign.intArrayFromList(mapping.getAncestor(helix));
+                                       Arrays.sort(basesInHelixArray);
+                                       
+                                       // Draw this helix if it has not already been done
+                                       if (!alreadyDrawnHelixes.contains(helix)) {
+                                               firstTimeWeMeetThisHelix = true;
+                                               drawHelixLikeTemplateHelix(basesInHelixArray, helix, coords, centers,angles, globalIncreaseFactor, translateVectors, straightBulges);
+                                               alreadyDrawnHelixes.add(helix);
+                                       } else {
+                                               firstTimeWeMeetThisHelix = false;
+                                       }
+                                       
+                                       EdgeEndPoint howWeGetInCurrentHelix;
+                                       if (firstTimeWeMeetThisHelix) {
+                                               if (helix.getIn1Is() == In1Is.IN1_IS_5PRIME) {
+                                                       howWeGetInCurrentHelix = helix.getIn1();
+                                               } else {
+                                                       howWeGetInCurrentHelix = helix.getIn2();
+                                               }
+                                       } else {
+                                               if (helix.getIn1Is() == In1Is.IN1_IS_5PRIME) {
+                                                       howWeGetInCurrentHelix = helix.getIn2();
+                                               } else {
+                                                       howWeGetInCurrentHelix = helix.getIn1();
+                                               }
+                                       }
+                                       
+                                       Point2D.Double P0 = new Point2D.Double();
+                                       Point2D.Double P3 = new Point2D.Double();
+                                       
+                                       if (lastMappedHelix != null) {
+                                               // Now draw the RNA sequence (possibly containing helixes)
+                                               // between the last template drawn helix and this one.
+       
+                                               if (lastMappedHelix == helix) {
+                                                       // Last helix is the same as the current one so
+                                                       // nothing matched (or at best a single
+                                                       // non-paired sequence) so we will just
+                                                       // use the Radiate algorithm
+                                                       
+                                                       Point2D.Double helixVector = new Point2D.Double();
+                                                       computeHelixEndPointDirections(howWeGotOutOfLastHelix, helixVector, new Point2D.Double());
+                                                       
+                                                       double angle = MiscGeom.angleFromVector(helixVector);
+                                                       int b1 = basesInHelixArray[basesInHelixArray.length/2 - 1];
+                                                       P0.setLocation(coords[b1]);
+                                                       int b2 = basesInHelixArray[basesInHelixArray.length/2];
+                                                       P3.setLocation(coords[b2]);
+                                                       Point2D.Double loopCenter = new Point2D.Double((P0.x + P3.x)/2, (P0.y + P3.y)/2);
+                                                       rna.drawLoop(b1,
+                                                                        b2,
+                                                                        loopCenter.x,
+                                                                        loopCenter.y,
+                                                                        angle,
+                                                                        coords,
+                                                                        centers,
+                                                                        angles,
+                                                                        straightBulges);
+                                                       // If the helix is flipped, we need to compute the symmetric
+                                                       // of the whole loop.
+                                                       if (helix.isFlipped()) {
+                                                               symmetric(loopCenter, helixVector, coords, b1, b2);
+                                                               symmetric(loopCenter, helixVector, centers, b1, b2);
+                                                       }
+                                               } else {
+                                                       // No helices matched between the last helix and
+                                                       // the current one, so we draw what is between
+                                                       // using the radiate algorithm but on the Bezier curve.
+                                                       
+                                                       int b1 = howWeGotOutOfLastHelixBaseIndex;
+                                                       int b2 = firstTimeWeMeetThisHelix ? basesInHelixArray[0] : basesInHelixArray[basesInHelixArray.length/2];
+                                                       P0.setLocation(coords[b1]);
+                                                       P3.setLocation(coords[b2]);
+                                                       
+                                                       Point2D.Double P1, P2;
+                                                       
+                                                       if (howWeGotOutOfLastHelix.getOtherElement() instanceof RNATemplateUnpairedSequence
+                                                                       && howWeGetInCurrentHelix.getOtherElement() instanceof RNATemplateUnpairedSequence) {
+                                                               // We will draw the bases on a Bezier curve
+                                                               P1 = new Point2D.Double();
+                                                               computeBezierTangentVectorTarget(howWeGotOutOfLastHelix, P0, P1);
+                                                               
+                                                               P2 = new Point2D.Double();
+                                                               computeBezierTangentVectorTarget(howWeGetInCurrentHelix, P3, P2);
+                                                       } else {
+                                                               // We will draw the bases on a straight line between P0 and P3
+                                                               P1 = null;
+                                                               P2 = null;
+                                                       }
+                                                       
+                                                       drawAlongCurve(b1, b2, P0, P1, P2, P3, coords, centers, angles, curveMethod, lastMappedHelix.isFlipped(), straightBulges);
+                                               }
+                                               
+                                       } else if (basesInHelixArray[0] > 0) {
+                                               // Here we draw what is before the first mapped helix.
+       
+                                               RNATemplateUnpairedSequence templateSequence;
+                                               // Try to find our template sequence as the mapped element of base 0
+                                               RNATemplateElement templateSequenceCandidate = mapping.getPartner(0);
+                                               if (templateSequenceCandidate != null
+                                                               && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                                       templateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                                               } else {
+                                                       // Try other idea: first template element if it is a sequence
+                                                       templateSequenceCandidate = template.getFirst();
+                                                       if (templateSequenceCandidate != null
+                                                                       && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                                               templateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                                                       } else {
+                                                               // We don't know where to start
+                                                               templateSequence = null;
+                                                       }
+                                               }
+                                               
+                                               int b1 = 0;
+                                               int b2 = firstTimeWeMeetThisHelix ? basesInHelixArray[0] : basesInHelixArray[basesInHelixArray.length/2];
+                                               P3.setLocation(coords[b2]);
+                                               
+                                               if (templateSequence != null) {
+                                                       coords[0].setLocation(templateSequence.getVertex5());
+                                                       coords[0].x *= globalIncreaseFactor;
+                                                       coords[0].y *= globalIncreaseFactor;
+                                               } else {
+                                                       // Put b1 at an ideal distance from b2, using the "flat exterior loop" method
+                                                       double idealLength = computeStraightLineIdealLength(b1, b2);
+                                                       Point2D.Double j = new Point2D.Double();
+                                                       if (howWeGetInCurrentHelix != null) {
+                                                               computeHelixEndPointDirections(howWeGetInCurrentHelix, new Point2D.Double(), j);
+                                                       } else {
+                                                               j.setLocation(1, 0);
+                                                       }
+                                                       coords[b1].setLocation(coords[b2].x + j.x*idealLength, coords[b2].y + j.y*idealLength);
+                                               }
+                                               P0.setLocation(coords[0]);
+                                               
+                                               Point2D.Double P1, P2;
+                                               
+                                               if (howWeGetInCurrentHelix.getOtherElement() instanceof RNATemplateUnpairedSequence
+                                                               && templateSequence != null) {
+                                                       // We will draw the bases on a Bezier curve
+                                                       P1 = new Point2D.Double();
+                                                       computeBezierTangentVectorTarget(templateSequence.getIn(), P0, P1);
+                                                       
+                                                       P2 = new Point2D.Double();
+                                                       computeBezierTangentVectorTarget(howWeGetInCurrentHelix, P3, P2);
+                                               } else {
+                                                       // We will draw the bases on a straight line between P0 and P3
+                                                       P1 = null;
+                                                       P2 = null;
+                                               }
+                                               
+                                               drawAlongCurve(b1, b2, P0, P1, P2, P3, coords, centers, angles, curveMethod, false, straightBulges);
+                                       }
+                                       
+                                       lastMappedHelix = helix;
+                                       howWeGotOutOfLastHelix = howWeGetInCurrentHelix.getNextEndPoint();
+                                       if (firstTimeWeMeetThisHelix) {
+                                               howWeGotOutOfLastHelixBaseIndex = basesInHelixArray[basesInHelixArray.length/2-1];
+                                       } else {
+                                               howWeGotOutOfLastHelixBaseIndex = basesInHelixArray[basesInHelixArray.length-1];
+                                       }
+                               }
+                       } // end template iteration
+                       
+                       
+                       // Now we need to draw what is after the last mapped helix.
+                       if (howWeGotOutOfLastHelixBaseIndex < coords.length-1
+                                       && element != null
+                                       && coords.length > 1) {
+                               
+                               RNATemplateUnpairedSequence beginTemplateSequence = null;
+                               if (lastMappedHelix == null) {
+                                       // No helix at all matched between the template and RNA!
+                                       // So the sequence we want to draw is the full RNA.
+                                       
+                                       // Try to find our template sequence as the mapped element of base 0
+                                       RNATemplateElement templateSequenceCandidate = mapping.getPartner(0);
+                                       if (templateSequenceCandidate != null
+                                                       && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                               beginTemplateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                                       } else {
+                                               // Try other idea: first template element if it is a sequence
+                                               templateSequenceCandidate = template.getFirst();
+                                               if (templateSequenceCandidate != null
+                                                               && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                                       beginTemplateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                                               } else {
+                                                       // We don't know where to start
+                                                       beginTemplateSequence = null;
+                                               }
+                                       }
+                                       
+                                       if (beginTemplateSequence != null) {
+                                               coords[0].setLocation(beginTemplateSequence.getVertex5());
+                                               coords[0].x *= globalIncreaseFactor;
+                                               coords[0].y *= globalIncreaseFactor;
+                                       }
+                                       
+                               }
+                               
+                               RNATemplateUnpairedSequence endTemplateSequence;
+                               // Try to find our template sequence as the mapped element of last base
+                               RNATemplateElement templateSequenceCandidate = mapping.getPartner(coords.length-1);
+                               if (templateSequenceCandidate != null
+                                               && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                       endTemplateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                               } else {
+                                       // Try other idea: last template element if it is a sequence
+                                       templateSequenceCandidate = element;
+                                       if (templateSequenceCandidate != null
+                                                       && templateSequenceCandidate instanceof RNATemplateUnpairedSequence) {
+                                               endTemplateSequence = (RNATemplateUnpairedSequence) templateSequenceCandidate;
+                                       } else {
+                                               // We don't know where to end
+                                               endTemplateSequence = null;
+                                       }
+                               }
+                               
+                               int b1 = howWeGotOutOfLastHelixBaseIndex;
+                               int b2 = coords.length - 1;
+                               
+                               if (endTemplateSequence != null) {
+                                       coords[b2].setLocation(endTemplateSequence.getVertex3());
+                                       coords[b2].x *= globalIncreaseFactor;
+                                       coords[b2].y *= globalIncreaseFactor;
+                               } else {
+                                       // Put b2 at an ideal distance from b1, using the "flat exterior loop" method
+                                       double idealLength = computeStraightLineIdealLength(b1, b2);
+                                       Point2D.Double j = new Point2D.Double();
+                                       if (howWeGotOutOfLastHelix != null) {
+                                               computeHelixEndPointDirections(howWeGotOutOfLastHelix, new Point2D.Double(), j);
+                                       } else {
+                                               j.setLocation(1, 0);
+                                       }
+                                       coords[b2].setLocation(coords[b1].x + j.x*idealLength, coords[b1].y + j.y*idealLength);
+                               }
+
+                               
+                               Point2D.Double P0 = new Point2D.Double();
+                               Point2D.Double P3 = new Point2D.Double();
+                               
+                               P0.setLocation(coords[b1]);
+                               P3.setLocation(coords[b2]);
+                               
+                               Point2D.Double P1, P2;
+                               
+                               if (howWeGotOutOfLastHelix != null
+                                               && howWeGotOutOfLastHelix.getOtherElement() instanceof RNATemplateUnpairedSequence
+                                               && endTemplateSequence != null) {
+                                       // We will draw the bases on a Bezier curve
+                                       P1 = new Point2D.Double();
+                                       computeBezierTangentVectorTarget(howWeGotOutOfLastHelix, P0, P1);
+                                       
+                                       P2 = new Point2D.Double();
+                                       computeBezierTangentVectorTarget(endTemplateSequence.getOut(), P3, P2);
+                               } else if (lastMappedHelix == null
+                                               && beginTemplateSequence != null
+                                               && endTemplateSequence != null) {
+                                       // We will draw the bases on a Bezier curve
+                                       P1 = new Point2D.Double();
+                                       computeBezierTangentVectorTarget(beginTemplateSequence.getIn(), P0, P1);
+                                       
+                                       P2 = new Point2D.Double();
+                                       computeBezierTangentVectorTarget(endTemplateSequence.getOut(), P3, P2);
+                               } else {
+                                       // We will draw the bases on a straight line between P0 and P3
+                                       P1 = null;
+                                       P2 = null;
+                               }
+                               
+                               drawAlongCurve(b1, b2, P0, P1, P2, P3, coords, centers, angles, curveMethod, lastMappedHelix != null ? lastMappedHelix.isFlipped() : false, straightBulges);
+                       
+                       }
+                       
+                       
+                       if (helixLengthAdjustmentMethod == DrawRNATemplateMethod.NOINTERSECT && coords.length > 3) {
+                               // Are we happy with this value of globalIncreaseFactor?
+                               Line2D.Double[] lines = new Line2D.Double[coords.length-1];
+                               for (int i=0; i<coords.length-1; i++) {
+                                       lines[i] = new Line2D.Double(coords[i], coords[i+1]);
+                               }
+                               int intersectLines = 0;
+                               for (int i=0; i<lines.length; i++) {
+                                       for (int j=i+2; j<lines.length; j++) {
+                                               if (LinesIntersect.linesIntersect(lines[i], lines[j])) {
+                                                       intersectLines++;
+                                               }
+                                       }
+                               }
+                               // If no intersection we keep this globalIncreaseFactor value
+                               if (intersectLines > 0) {
+                                       // Don't increase more than a maximum value
+                                       if (globalIncreaseFactor < 3) {
+                                               globalIncreaseFactor += 0.1;
+                                               //System.out.println("globalIncreaseFactor increased to " + globalIncreaseFactor);
+                                               // Compute the drawing again
+                                               computeCoords = true;
+                                       }
+                               }
+                       }
+                       
+               }
+               
+               // debug
+               if (helixLengthAdjustmentMethod == DrawRNATemplateMethod.MAXSCALINGFACTOR
+                               || helixLengthAdjustmentMethod == DrawRNATemplateMethod.NOINTERSECT) {
+                       //System.out.println("globalIncreaseFactor = " + globalIncreaseFactor);
+               }
+               
+               // Now we actually move the bases, according to arrays coords and centers
+               // and taking in account the space between bases parameter.
+               for (int i = 0; i < _listeBases.size(); i++) {
+                       _listeBases.get(i).setCoords(
+                                       new Point2D.Double(coords[i].x * conf._spaceBetweenBases,
+                                                       coords[i].y * conf._spaceBetweenBases));
+                       _listeBases.get(i).setCenter(
+                                       new Point2D.Double(centers[i].x * conf._spaceBetweenBases,
+                                                       centers[i].y * conf._spaceBetweenBases));
+               }
+       
+       }
+       
+       
+       
+       
+       
+       
+       /**
+        * IN: Argument helixEndPoint is an IN argument (will be read),
+        * and must contain an helix edge endpoint.
+        * 
+        * The other arguments are OUT arguments
+        * (must be existing objects, content will be overwritten).
+        * 
+        * OUT: The i argument will contain a vector colinear to the vector
+        * from the helix startPosition to endPosition or the opposite
+        * depending on there the endpoint is (the endpoint will be on the
+        * destination side of the vector). ||i|| = 1
+        * 
+        * OUT: The j vector will contain an vector that is colinear
+        * to the last/first base pair connection on the side of this endpoint.
+        * The vector will be oriented to the side of the given endpoint.
+        * ||j|| = 1
+        */
+       private void computeHelixEndPointDirections(
+                       EdgeEndPoint helixEndPoint, // IN
+                       Point2D.Double i, // OUT
+                       Point2D.Double j // OUT
+                       ) {
+               RNATemplateHelix helix = (RNATemplateHelix) helixEndPoint.getElement();
+               Point2D.Double startpos = helix.getStartPosition();
+               Point2D.Double endpos = helix.getEndPosition();
+               Point2D.Double helixVector = new Point2D.Double();
+               switch (helixEndPoint.getPosition()) {
+               case IN1:
+               case OUT2:
+                       helixVector.x = startpos.x - endpos.x;
+                       helixVector.y = startpos.y - endpos.y;
+                       break;
+               case IN2:
+               case OUT1:
+                       helixVector.x = endpos.x - startpos.x;
+                       helixVector.y = endpos.y - startpos.y;
+                       break;
+               }
+               double helixVectorLength = Math.hypot(helixVector.x, helixVector.y);
+               // i is the vector which is colinear to helixVector and such that ||i|| = 1
+               i.x = helixVector.x / helixVectorLength;
+               i.y = helixVector.y / helixVectorLength;
+               // Find j such that it is orthogonal to i, ||j|| = 1
+               // and j goes to the side where the sequence will be connected
+               switch (helixEndPoint.getPosition()) {
+               case IN1:
+               case IN2:
+                       // rotation of +pi/2
+                       j.x = - i.y;
+                       j.y =   i.x;
+                       break;
+               case OUT1:
+               case OUT2:
+                       // rotation of -pi/2
+                       j.x =   i.y;
+                       j.y = - i.x;
+                       break;
+               }
+               if (helix.isFlipped()) {
+                       j.x = - j.x;
+                       j.y = - j.y;
+               }
+
+       }
+       
+       /**
+        * A cubic Bezier curve can be defined by 4 points,
+        * see http://en.wikipedia.org/wiki/Bezier_curve#Cubic_B.C3.A9zier_curves
+        * For each of the curve end points, there is the last/first point of the
+        * curve and a point which gives the direction and length of the tangent
+        * vector on that side. This two points are respectively curveEndPoint
+        * and curveVectorOtherPoint.
+        * IN:  Argument helixVector is the vector formed by the helix,
+        *      in the right direction for our sequence.
+        * IN:  Argument curveEndPoint is the position of the endpoint on the helix.
+        * OUT: Argument curveVectorOtherPoint must be allocated
+        *      and the values will be modified.
+        */
+       private void computeBezierTangentVectorTarget(
+                       EdgeEndPoint endPoint,
+                       Point2D.Double curveEndPoint,
+                       Point2D.Double curveVectorOtherPoint)
+                       throws RNATemplateDrawingAlgorithmException {
+               
+               boolean sequenceEndPointIsIn;
+               RNATemplateUnpairedSequence sequence;
+               
+               if (endPoint.getElement() instanceof RNATemplateHelix) {
+                       sequence = (RNATemplateUnpairedSequence) endPoint.getOtherElement();
+                       EdgeEndPointPosition endPointPositionOnHelix = endPoint.getPosition();
+                       switch (endPointPositionOnHelix) {
+                       case IN1:
+                       case IN2:
+                               sequenceEndPointIsIn = false;
+                               break;
+                       default:
+                               sequenceEndPointIsIn = true;
+                       }
+                       
+                       EdgeEndPoint endPointOnHelix =
+                               sequenceEndPointIsIn ?
+                                               sequence.getIn().getOtherEndPoint() :
+                                               sequence.getOut().getOtherEndPoint();
+                       if (endPointOnHelix == null) {
+                               throw (new RNATemplateDrawingAlgorithmException("Sequence is not connected to an helix."));
+                       }
+               } else {
+                       // The endpoint is on an unpaired sequence.
+                       sequence = (RNATemplateUnpairedSequence) endPoint.getElement();
+                       if (endPoint == sequence.getIn()) {
+                               // endpoint is 5'
+                               sequenceEndPointIsIn = true;
+                       } else {
+                               sequenceEndPointIsIn = false;
+                       }
+               }
+
+               double l =
+                       sequenceEndPointIsIn ?
+                               sequence.getInTangentVectorLength() :
+                               sequence.getOutTangentVectorLength();
+               
+               // Compute the absolute angle our line makes to the helix
+               double theta =
+                       sequenceEndPointIsIn ?
+                               sequence.getInTangentVectorAngle() :
+                               sequence.getOutTangentVectorAngle();
+               
+               // Compute v, the tangent vector of the Bezier curve
+               Point2D.Double v = new Point2D.Double();
+               v.x = l * Math.cos(theta);
+               v.y = l * Math.sin(theta);
+               curveVectorOtherPoint.x = curveEndPoint.x + v.x;
+               curveVectorOtherPoint.y = curveEndPoint.y + v.y;
+       }
+       
+
+       /**
+        * Compute (actual helix length / helix length in template).
+        * @param straightBulges 
+        */
+       private double computeLengthIncreaseFactor(
+                       int[] basesInHelixArray,  // IN
+                       RNATemplateHelix helix,    // IN
+                       boolean straightBulges
+                       ) {
+               double templateLength = computeHelixTemplateLength(helix);
+               double realLength = computeHelixRealLength(basesInHelixArray,straightBulges);
+               return realLength / templateLength;
+       }
+       
+       /**
+        * Compute (actual helix vector - helix vector in template).
+        */
+       private Point2D.Double computeLengthIncreaseDelta(
+                       int[] basesInHelixArray,  // IN
+                       RNATemplateHelix helix,   // IN
+                       boolean straightBulges
+                       ) {
+               double templateLength = computeHelixTemplateLength(helix);
+               double realLength = computeHelixRealLength(basesInHelixArray,straightBulges);
+               Point2D.Double i = new Point2D.Double();
+               computeTemplateHelixVectors(helix, null, i, null);
+               return new Point2D.Double(i.x*(realLength-templateLength), i.y*(realLength-templateLength));
+       }
+       
+       /**
+        * Compute helix interesting vectors from template helix.
+        * @param helix The template helix you want to compute the vectors from.
+        * @param o This point coordinates will be set the origin of the helix (or not if null),
+        *          ie. the point in the middle of the base pair with the two most extreme bases.
+        * @param i Will be set to the normalized helix vector. (nothing done if null)
+        * @param j Will be set to the normalized helix base pair vector (5' -> 3'). (nothing done if null)
+        */
+       private void computeTemplateHelixVectors(
+                       RNATemplateHelix helix,  // IN
+                       Point2D.Double o,        // OUT
+                       Point2D.Double i,        // OUT
+                       Point2D.Double j         // OUT
+                       ) {
+               Point2D.Double startpos, endpos;
+               if (helix.getIn1Is() == In1Is.IN1_IS_5PRIME) {
+                       startpos = helix.getStartPosition();
+                       endpos = helix.getEndPosition();
+               } else {
+                       endpos = helix.getStartPosition();
+                       startpos = helix.getEndPosition();
+               }
+               if (o != null) {
+                       o.x = startpos.x;
+                       o.y = startpos.y;
+               }
+               if (i != null || j != null) {
+                       // (i_x,i_y) is the vector between two consecutive bases of the same side of an helix
+                       if (i == null)
+                               i = new Point2D.Double();
+                       i.x = (endpos.x - startpos.x);
+                       i.y = (endpos.y - startpos.y);
+                       double i_original_norm = Math.hypot(i.x, i.y);
+                       // change its norm to 1
+                       i.x = i.x / i_original_norm;
+                       i.y = i.y / i_original_norm;
+                       if (j != null) {
+                               j.x = - i.y;
+                               j.y =   i.x;
+                               if (helix.isFlipped()) {
+                                       j.x = - j.x;
+                                       j.y = - j.y;
+                               }
+                               double j_original_norm = Math.hypot(j.x, j.y);
+                               // change (j_x,j_y) so that its norm is 1
+                               j.x = j.x / j_original_norm;
+                               j.y = j.y / j_original_norm;
+                       }
+               }
+       }
+       
+       
+       /**
+        * Estimate bulge arc length.
+        */
+       private double estimateBulgeArcLength(int firstBase, int lastBase) {
+               if (firstBase + 1 == lastBase)
+                       return RNA.LOOP_DISTANCE; // there is actually no bulge
+               double len = 0.0;
+               int k = firstBase;
+               while (k < lastBase) {
+                       int l = _listeBases.get(k).getElementStructure();
+                       if (k < l && l < lastBase) {
+                               len += RNA.BASE_PAIR_DISTANCE;
+                               k = l;
+                       } else {
+                               len += RNA.LOOP_DISTANCE;
+                               k++;
+                       }
+               }
+               return len;
+       }
+       
+       
+       /**
+        * Estimate bulge width, the given first and last bases must be those in the helix.
+        */
+       private double estimateBulgeWidth(int firstBase, int lastBase) {
+               double len = estimateBulgeArcLength(firstBase, lastBase);
+               return 2 * (len / Math.PI);
+       }
+       
+       
+       /**
+        * Get helix length in template.
+        */
+       private double computeHelixTemplateLength(RNATemplateHelix helix) {
+               return Math.hypot(helix.getStartPosition().x - helix.getEndPosition().x,
+                               helix.getStartPosition().y - helix.getEndPosition().y);
+       }
+       
+       
+       /**
+        * Compute helix actual length (as drawHelixLikeTemplateHelix() would draw it).
+        */
+       private double computeHelixRealLength(int[] basesInHelixArray, boolean straightBulges) {
+               return drawHelixLikeTemplateHelix(basesInHelixArray, null, null, null, null, 0, null,straightBulges);
+       }
+       
+       
+       /**
+        * Result type of countUnpairedLine(), see below.
+        */
+       private static class UnpairedLineCounts {
+               public int nBP, nLD, total;
+       }
+       /**
+        * If we are drawing an unpaired region that may contains helices,
+        * and we are drawing it on a line (curve or straight, doesn't matter),
+        * how many intervals should have a base-pair length (start of an helix)
+        * and how many should have a consecutive unpaired bases length?
+        * Returns an array with three elements:
+        * - answer to first question
+        * - answer to second question
+        * - sum of both, ie. total number of intervals
+        *   (this is NOT lastBase-firstBase because bases deep in helices do not count)
+        */
+       private UnpairedLineCounts countUnpairedLine(int firstBase, int lastBase) {
+               UnpairedLineCounts counts = new UnpairedLineCounts();
+               int nBP = 0;
+               int nLD = 0;
+               {
+                       int b = firstBase;
+                       while (b < lastBase) {
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       nBP++;
+                                       b = l;
+                               } else {
+                                       nLD++;
+                                       b++;
+                               }
+                       }
+               }
+               counts.nBP = nBP;
+               counts.nLD = nLD;
+               counts.total = nBP + nLD;
+               return counts;
+       }
+       
+       
+       /**
+        * Draw the given helix (given as a *SORTED* array of indexes)
+        * like defined in the given template helix.
+        * OUT: The bases positions are not changed in fact,
+        *      instead the coords and centers arrays are modified.
+        * IN:  The helix origin position is multiplied by scaleHelixOrigin
+        *      and translateVectors.get(helix) is added.
+        * RETURN VALUE:
+        *      The length of the drawn helix.
+        * @param straightBulges 
+        * 
+        */
+       private double drawHelixLikeTemplateHelix(
+                       int[] basesInHelixArray,  // IN
+                       RNATemplateHelix helix,   // IN  (optional, ie. may be null)
+                       Point2D.Double[] coords,  // OUT (optional, ie. may be null)
+                       Point2D.Double[] centers, // OUT (optional, ie. may be null)
+                       double[] angles,  // OUT 
+                       double scaleHelixOrigin,  // IN
+                       Map<RNATemplateHelix, Point2D.Double> translateVectors // IN (optional, ie. may be null)
+, boolean straightBulges
+                       ) {
+               int n = basesInHelixArray.length / 2;
+               if (n == 0)
+                       return 0;
+                // Default values when not template helix is provided:
+               Point2D.Double o = new Point2D.Double(0, 0);
+               Point2D.Double i = new Point2D.Double(1, 0);
+               Point2D.Double j = new Point2D.Double(0, 1);
+               boolean flipped = false;
+               if (helix != null) {
+                       computeTemplateHelixVectors(helix, o, i, j);
+                       flipped = helix.isFlipped();
+               }
+               Point2D.Double li = new Point2D.Double(i.x*RNA.LOOP_DISTANCE, i.y*RNA.LOOP_DISTANCE);
+               // We want o to be the point where the first base (5' end) is
+               o.x = (o.x - j.x * RNA.BASE_PAIR_DISTANCE / 2) * scaleHelixOrigin;
+               o.y = (o.y - j.y * RNA.BASE_PAIR_DISTANCE / 2) * scaleHelixOrigin;
+               if (translateVectors != null && translateVectors.containsKey(helix)) {
+                       Point2D.Double v = translateVectors.get(helix);
+                       o.x = o.x + v.x;
+                       o.y = o.y + v.y;
+               }
+               
+               // We need this array so that we can store positions even if coords == null
+               Point2D.Double[] helixBasesPositions = new Point2D.Double[basesInHelixArray.length];
+               for (int k=0; k<helixBasesPositions.length; k++) {
+                       helixBasesPositions[k] = new Point2D.Double();  
+               }
+               Point2D.Double accDelta = new Point2D.Double(0, 0);
+               for (int k=0; k<n; k++) {
+                       int kp = 2*n-k-1;
+                       Point2D.Double p1 = helixBasesPositions[k]; // we assign the point *reference*
+                       Point2D.Double p2 = helixBasesPositions[kp];
+                       // Do we have a bulge between previous base pair and this one?
+                       boolean bulge = k >= 1 && (basesInHelixArray[k] != basesInHelixArray[k-1] + 1
+                                                          || basesInHelixArray[kp+1] != basesInHelixArray[kp] + 1);
+                       if (k >= 1) {
+                               if (basesInHelixArray[k] < basesInHelixArray[k-1]
+                                   || basesInHelixArray[kp+1] < basesInHelixArray[kp]) {
+                                       throw new Error("Internal bug: basesInHelixArray must be sorted");
+                               }
+                               if (bulge) {
+                                       // Estimate a good distance (delta) between the previous base pair and this one
+                                       double delta1 = estimateBulgeWidth(basesInHelixArray[k-1], basesInHelixArray[k]);
+                                       double delta2 = estimateBulgeWidth(basesInHelixArray[kp], basesInHelixArray[kp+1]);
+                                       // The biggest bulge defines the width
+                                       double delta = Math.max(delta1, delta2);
+
+                                       if (coords != null) {
+                                               // Now, where do we put the bases that are part of the bulge?
+                                               for (int side=0; side<2; side++) {
+                                                       Point2D.Double pstart = new Point2D.Double();
+                                                       Point2D.Double pend = new Point2D.Double();
+                                                       Point2D.Double bisectVect = new Point2D.Double();
+                                                       Point2D.Double is = new Point2D.Double();
+                                                       int firstBase, lastBase;
+                                                       double alphasign = flipped ? -1 : 1;
+                                                       if (side == 0) {
+                                                               firstBase = basesInHelixArray[k-1];
+                                                               lastBase  = basesInHelixArray[k];
+                                                               pstart.setLocation(o.x + accDelta.x,
+                                                                                  o.y + accDelta.y);
+                                                               pend.setLocation(o.x + accDelta.x + i.x*delta,
+                                                                                o.y + accDelta.y + i.y*delta);
+                                                               bisectVect.setLocation(-j.x, -j.y);
+                                                               is.setLocation(i);
+                                                       } else {
+                                                               firstBase = basesInHelixArray[kp];
+                                                               lastBase  = basesInHelixArray[kp+1];
+                                                               pstart.setLocation(o.x + accDelta.x + i.x*delta + j.x*RNA.BASE_PAIR_DISTANCE,
+                                                                          o.y + accDelta.y + i.y*delta + j.y*RNA.BASE_PAIR_DISTANCE);
+                                                               pend.setLocation(o.x + accDelta.x + j.x*RNA.BASE_PAIR_DISTANCE,
+                                                                                o.y + accDelta.y + j.y*RNA.BASE_PAIR_DISTANCE);
+
+                                                               bisectVect.setLocation(j);
+                                                               is.setLocation(-i.x, -i.y);
+                                                       }
+                                                       double arclen = estimateBulgeArcLength(firstBase, lastBase);
+                                                       double centerOnBisect = ComputeArcCenter.computeArcCenter(delta, arclen);
+                                                       
+                                                       // Should we draw the base on an arc or simply use a line?
+                                                       if (centerOnBisect > -1000) {
+                                                               Point2D.Double center = new Point2D.Double(pstart.x + is.x*delta/2 + bisectVect.x*centerOnBisect,
+                                                                                                          pstart.y + is.y*delta/2 + bisectVect.y*centerOnBisect);
+                                                               int b = firstBase;
+                                                               double len = 0;
+                                                               double r = Math.hypot(pstart.x - center.x, pstart.y - center.y);
+                                                               double alpha0 = MiscGeom.angleFromVector(pstart.x - center.x, pstart.y - center.y);
+                                                               while (b < lastBase) {
+                                                                       int l = _listeBases.get(b).getElementStructure();
+                                                                       if (b < l && l < lastBase) {
+                                                                               len += RNA.BASE_PAIR_DISTANCE;
+                                                                               b = l;
+                                                                       } else {
+                                                                               len += RNA.LOOP_DISTANCE;
+                                                                               b++;
+                                                                       }
+                                                                       if (b < lastBase) {
+                                                                               coords[b].x = center.x + r*Math.cos(alpha0 + alphasign*len/r);
+                                                                               coords[b].y = center.y + r*Math.sin(alpha0 + alphasign*len/r);
+                                                                       }
+                                                               }
+                                                       } else {
+                                                               // Draw on a line
+                                                               
+                                                               // Distance between paired bases cannot be changed
+                                                               // (imposed by helix width) but distance between other
+                                                               // bases can be adjusted.
+                                                               UnpairedLineCounts counts = countUnpairedLine(firstBase, lastBase);
+                                                               double LD = Math.max((delta - counts.nBP*RNA.BASE_PAIR_DISTANCE) / (double) counts.nLD, 0);
+                                                               //System.out.println("nBP=" + nBP + " nLD=" + nLD);
+                                                               double len = 0;
+                                                               {
+                                                                       int b = firstBase;
+                                                                       while (b < lastBase) {
+                                                                               int l = _listeBases.get(b).getElementStructure();
+                                                                               if (b < l && l < lastBase) {
+                                                                                       len += RNA.BASE_PAIR_DISTANCE;
+                                                                                       b = l;
+                                                                               } else {
+                                                                                       len += LD;
+                                                                                       b++;
+                                                                               }
+                                                                               if (b < lastBase) {
+                                                                                       coords[b].x = pstart.x + is.x*len;
+                                                                                       coords[b].y = pstart.y + is.y*len;
+                                                                               }
+                                                                       }
+                                                               }
+                                                               //System.out.println("len=" + len + " delta=" + delta + " d(pstart,pend)=" + Math.hypot(pend.x-pstart.x, pend.y-pstart.y));
+                                                       }
+                                                       
+                                                       // Does the bulge contain an helix?
+                                                       // If so, use drawLoop() to draw it.
+                                                       {
+                                                               int b = firstBase;
+                                                               while (b < lastBase) {
+                                                                       int l = _listeBases.get(b).getElementStructure();
+                                                                       if (b < l && l < lastBase) {
+                                                                               // Helix present in bulge
+                                                                               Point2D.Double b1pos = coords[b];
+                                                                               Point2D.Double b2pos = coords[l];
+                                                                               double beta = MiscGeom.angleFromVector(b2pos.x - b1pos.x, b2pos.y - b1pos.y) - Math.PI / 2 + (flipped ? Math.PI : 0);
+                                                                               Point2D.Double loopCenter = new Point2D.Double((b1pos.x + b2pos.x)/2, (b1pos.y + b2pos.y)/2);
+                                                                               rna.drawLoop(b,
+                                                                                                l,
+                                                                                                loopCenter.x,
+                                                                                                loopCenter.y,
+                                                                                                beta,
+                                                                                                coords,
+                                                                                                centers,
+                                                                                                angles,
+                                                                                                straightBulges);
+                                                                               // If the helix is flipped, we need to compute the symmetric
+                                                                               // of the whole loop.
+                                                                               if (helix.isFlipped()) {
+                                                                                       Point2D.Double v = new Point2D.Double(Math.cos(beta), Math.sin(beta));
+                                                                                       symmetric(loopCenter, v, coords, b, l);
+                                                                                       symmetric(loopCenter, v, centers, b, l);
+                                                                               }
+                                                                               // Continue
+                                                                               b = l;
+                                                                       } else {
+                                                                               b++;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       
+                                       accDelta.x += i.x*delta;
+                                       accDelta.y += i.y*delta;
+                                       p1.x = o.x + accDelta.x;
+                                       p1.y = o.y + accDelta.y;
+                                       p2.x = p1.x + j.x*RNA.BASE_PAIR_DISTANCE;
+                                       p2.y = p1.y + j.y*RNA.BASE_PAIR_DISTANCE;
+                                       
+                               } else {
+                                       accDelta.x += li.x;
+                                       accDelta.y += li.y;
+                                       p1.x = o.x + accDelta.x;
+                                       p1.y = o.y + accDelta.y;
+                                       p2.x = p1.x + j.x*RNA.BASE_PAIR_DISTANCE;
+                                       p2.y = p1.y + j.y*RNA.BASE_PAIR_DISTANCE;
+                               }
+                       } else {
+                               // First base pair
+                               p1.x = o.x;
+                               p1.y = o.y;
+                               p2.x = p1.x + j.x*RNA.BASE_PAIR_DISTANCE;
+                               p2.y = p1.y + j.y*RNA.BASE_PAIR_DISTANCE;
+                       }
+               }
+               
+               Point2D.Double p1 = helixBasesPositions[0];
+               Point2D.Double p2 = helixBasesPositions[n-1];
+               
+               if (coords != null) {
+                       for (int k=0; k<helixBasesPositions.length; k++) {
+                               coords[basesInHelixArray[k]] = helixBasesPositions[k];
+                       }
+               }
+               
+               return Math.hypot(p2.x-p1.x, p2.y-p1.y);
+       }
+       
+       
+       
+
+       /**
+        * Return ideal length to draw unpaired region from firstBase to lastBase.
+        */
+       private double computeStraightLineIdealLength(int firstBase, int lastBase) {
+               UnpairedLineCounts counts = countUnpairedLine(firstBase, lastBase);
+               return RNA.BASE_PAIR_DISTANCE*counts.nBP + RNA.LOOP_DISTANCE*counts.nLD;
+       }
+       
+       /**
+        * Like drawOnBezierCurve(), but on a straight line.
+        */
+       private boolean drawOnStraightLine(
+                       int firstBase,
+                       int lastBase,
+                       Point2D.Double P0,
+                       Point2D.Double P3,
+                       Point2D.Double[] coords,
+                       Point2D.Double[] centers,
+                       boolean cancelIfNotGood) {
+               
+               Point2D.Double vector = new Point2D.Double(P3.x - P0.x, P3.y - P0.y); 
+               double vectorNorm = Math.hypot(vector.x, vector.y);
+
+               UnpairedLineCounts counts = countUnpairedLine(firstBase, lastBase);
+               double LD = Math.max((vectorNorm - counts.nBP*RNA.BASE_PAIR_DISTANCE) / (double) counts.nLD, 0);
+               
+               if (cancelIfNotGood && LD < RNA.LOOP_DISTANCE*0.75) {
+                       // Bases would be drawn "too" near (0.75 is heuristic)
+                       return false;
+               }
+               
+               double len = 0;
+               {
+                       int b = firstBase;
+                       while (b <= lastBase) {
+                               coords[b].x = P0.x + vector.x*len/vectorNorm;
+                               coords[b].y = P0.y + vector.y*len/vectorNorm;
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       len += RNA.BASE_PAIR_DISTANCE;
+                                       b = l;
+                               } else {
+                                       len += LD;
+                                       b++;
+                               }
+                       }
+               }
+               
+               return true;
+       }
+       
+       
+
+
+       /**
+        * A Bezier curve can be defined by four points,
+        * see http://en.wikipedia.org/wiki/Bezier_curve#Cubic_B.C3.A9zier_curves
+        * Here we give this four points and an array of bases indexes
+        * (which must be indexes in this RNA sequence) which will be moved
+        * to be on the Bezier curve.
+        * The bases positions are not changed in fact, instead the coords and
+        * centers arrays are modified.
+        * If cancelIfNotGood, draw only if it would look good,
+        * otherwise just return false.
+        */
+       private boolean drawOnBezierCurve(
+                       int firstBase,
+                       int lastBase,
+                       Point2D.Double P0,
+                       Point2D.Double P1,
+                       Point2D.Double P2,
+                       Point2D.Double P3,
+                       Point2D.Double[] coords,
+                       Point2D.Double[] centers,
+                       boolean cancelIfNotGood) {
+               
+               UnpairedLineCounts counts = countUnpairedLine(firstBase, lastBase);
+               
+               // Draw the bases of the sequence along a Bezier curve
+               int n = counts.total;
+               
+               // We choose to approximate the Bezier curve by 10*n straight lines.
+               CubicBezierCurve bezier = new CubicBezierCurve(P0, P1, P2, P3, 10*n);
+               double curveLength = bezier.getApproxCurveLength();
+               
+               
+               double LD = Math.max((curveLength - counts.nBP*RNA.BASE_PAIR_DISTANCE) / (double) counts.nLD, 0);
+               
+               if (cancelIfNotGood && LD < RNA.LOOP_DISTANCE*0.75) {
+                       // Bases would be drawn "too" near (0.75 is heuristic)
+                       return false;
+               }
+               
+               double[] t = new double[n+1];
+
+               {
+                       double len = 0;
+                       int k = 0;
+                       int b = firstBase;
+                       while (b <= lastBase) {
+                               t[k] = len;
+                               k++;
+                               
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       len += RNA.BASE_PAIR_DISTANCE;
+                                       b = l;
+                               } else {
+                                       len += LD;
+                                       b++;
+                               }
+                       }
+               }
+               
+               Point2D.Double[] sequenceBasesCoords = bezier.uniformParam(t);
+               
+               {
+                       int k = 0;
+                       int b = firstBase;
+                       while (b <= lastBase) {
+                               coords[b] = sequenceBasesCoords[k];
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       b = l;
+                               } else {
+                                       b++;
+                               }
+                               k++;
+                       }
+               }
+
+               return true;
+       }
+       
+       
+       
+       private void drawOnEllipse(
+                       int firstBase,
+                       int lastBase,
+                       Point2D.Double P0,
+                       Point2D.Double P3,
+                       Point2D.Double[] coords,
+                       Point2D.Double[] centers,
+                       boolean reverse) {              
+
+               UnpairedLineCounts counts = countUnpairedLine(firstBase, lastBase);
+               double halfEllipseLength = RNA.BASE_PAIR_DISTANCE*counts.nBP + RNA.LOOP_DISTANCE*counts.nLD;
+               double fullEllipseLength = halfEllipseLength*2;
+               
+               double axisA = P0.distance(P3)/2;
+               double axisB = ComputeEllipseAxis.computeEllipseAxis(axisA, fullEllipseLength);
+               
+               if (axisB == 0) {
+                       // Ellipse is in fact a line, and it is much faster to draw it as such.
+                       drawOnStraightLine(firstBase, lastBase, P0, P3, coords, centers, false);
+                       return;
+               }
+               
+               //System.out.println("Ellipse a=" + axisA + " b=" + axisB);
+               
+               int n = counts.total;
+               
+               // We choose to approximate the Bezier curve by 10*n straight lines.
+               HalfEllipse curve = new HalfEllipse(axisA, axisB, 10*n);
+               double curveLength = curve.getApproxCurveLength();
+               
+               
+               double LD = Math.max((curveLength - counts.nBP*RNA.BASE_PAIR_DISTANCE) / (double) counts.nLD, 0);
+               
+               double[] t = new double[n+1];
+
+               {
+                       double len = 0;
+                       int k = 0;
+                       int b = firstBase;
+                       while (b <= lastBase) {
+                               t[k] = len;
+                               k++;
+                               
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       len += RNA.BASE_PAIR_DISTANCE;
+                                       b = l;
+                               } else {
+                                       len += LD;
+                                       b++;
+                               }
+                       }
+               }
+               
+               // Ellipse with axis = X,Y
+               Point2D.Double[] sequenceBasesCoords = curve.uniformParam(t);
+               
+               // Compute the symmetric half-ellipse
+               if (reverse) {
+                       AffineTransform tranform1 = new AffineTransform();
+                       tranform1.scale(1, -1);
+                       tranform1.transform(sequenceBasesCoords, 0, sequenceBasesCoords, 0, sequenceBasesCoords.length);
+               }
+
+               // Translate + rotate such that ellipse is at the right place
+               AffineTransform tranform = HalfEllipse.matchAxisA(P0, P3);
+               tranform.transform(sequenceBasesCoords, 0, sequenceBasesCoords, 0, sequenceBasesCoords.length);
+               
+               {
+                       int k = 0;
+                       int b = firstBase;
+                       while (b <= lastBase) {
+                               coords[b] = sequenceBasesCoords[k];
+                               //coords[b] = curve.standardParam((double) k/n);
+                               //System.out.println(b + " " + coords[b]);
+                               int l = _listeBases.get(b).getElementStructure();
+                               if (b < l && l < lastBase) {
+                                       b = l;
+                               } else {
+                                       b++;
+                               }
+                               k++;
+                       }
+               }
+
+       }
+       
+       /**
+        * This functions draws the RNA sequence between (including)
+        * firstBase and lastBase along a curve.
+        * The sequence may contain helices.
+        * 
+        * Bezier curve:
+        * A Bezier curve can be defined by four points,
+        * see http://en.wikipedia.org/wiki/Bezier_curve#Cubic_B.C3.A9zier_curves
+        * 
+        * Straight line:
+        * If P1 and P2 are null, the bases are drawn on a straight line.
+        * 
+        * OUT: The bases positions are not changed in fact,
+        * instead the coords and centers arrays are modified.
+        * 
+        * Argument reverse says if we should reverse the direction of inserted helices.
+        */
+       private void drawAlongCurve(
+                       int firstBase,
+                       int lastBase,
+                       Point2D.Double P0,
+                       Point2D.Double P1,
+                       Point2D.Double P2,
+                       Point2D.Double P3,
+                       Point2D.Double[] coords,
+                       Point2D.Double[] centers,
+                       double[] angles,
+                       DrawRNATemplateCurveMethod curveMethod,
+                       boolean reverse,
+                       boolean straightBulges) {
+               
+               // First we find the bases which are directly on the Bezier curve
+               ArrayList<Integer> alongBezierCurve = new ArrayList<Integer>();
+               for (int depth=0, i=firstBase; i<=lastBase; i++) {
+                       int k = _listeBases.get(i).getElementStructure();
+                       if (k < 0 || k > lastBase || k < firstBase) {
+                               if (depth == 0) {
+                                       alongBezierCurve.add(i);
+                               }
+                       } else {
+                               if (i < k) {
+                                       if (depth == 0) {
+                                               alongBezierCurve.add(i);
+                                               alongBezierCurve.add(k);
+                                       }
+                                       depth++;
+                               } else {
+                                       depth--;
+                               }
+                       }
+               }
+               // number of bases along the Bezier curve
+               int n = alongBezierCurve.size();
+               int[] alongBezierCurveArray = RNATemplateAlign.intArrayFromList(alongBezierCurve);
+               if (n > 0) {
+                       if (curveMethod == DrawRNATemplateCurveMethod.ALWAYS_REPLACE_BY_ELLIPSES) {
+                               drawOnEllipse(firstBase, lastBase, P0, P3, coords, centers, reverse);
+                       } else if (curveMethod == DrawRNATemplateCurveMethod.SMART) {
+                               boolean passed;
+                               if (P1 != null && P2 != null) {
+                                       passed = drawOnBezierCurve(firstBase, lastBase, P0, P1, P2, P3, coords, centers, true);
+                               } else {
+                                       passed = drawOnStraightLine(firstBase, lastBase, P0, P3, coords, centers, true);
+                               }
+                               if (!passed) {
+                                       drawOnEllipse(firstBase, lastBase, P0, P3, coords, centers, reverse);
+                               }
+                       } else {
+                               if (P1 != null && P2 != null) {
+                                       drawOnBezierCurve(firstBase, lastBase, P0, P1, P2, P3, coords, centers, false);
+                               } else {
+                                       drawOnStraightLine(firstBase, lastBase, P0, P3, coords, centers, false);
+                               }
+                       }
+               }
+               // Now use the radiate algorithm to draw the helixes
+               for (int k=0; k<n-1; k++) {
+                       int b1 = alongBezierCurveArray[k];
+                       int b2 = alongBezierCurveArray[k+1];
+                       if (_listeBases.get(b1).getElementStructure() == b2) {
+                               Point2D.Double b1pos = coords[b1];
+                               Point2D.Double b2pos = coords[b2];
+                               double alpha = MiscGeom.angleFromVector(b2pos.x - b1pos.x, b2pos.y - b1pos.y);
+                               rna.drawLoop(b1,
+                                                b2,
+                                                (b1pos.x + b2pos.x)/2,
+                                                (b1pos.y + b2pos.y)/2,
+                                                alpha - Math.PI / 2,
+                                                coords,
+                                                centers,
+                                                angles,
+                                                straightBulges);
+                               if (reverse) {
+                                       Point2D.Double symAxisVect = new Point2D.Double(coords[b2].x - coords[b1].x, coords[b2].y - coords[b1].y); 
+                                       symmetric(coords[b1], symAxisVect, coords, b1, b2);
+                                       symmetric(coords[b1], symAxisVect, centers, b1, b2);
+                               }
+                       }
+               }       
+       }
+       
+       
+       /**
+        * Compute the symmetric of all the points from first to last in the points array
+        * relative to the line that goes through p and has director vector v.
+        * The array is modified in-place.
+        */
+       private static void symmetric(
+                       Point2D.Double p,
+                       Point2D.Double v,
+                       Point2D.Double[] points,
+                       int first,
+                       int last) {
+               // ||v||^2
+               double lv = v.x*v.x + v.y*v.y;
+               for (int i=first; i<=last; i++) {
+                       // A is the coordinates of points[i] after moving the origin at p
+                       Point2D.Double A = new Point2D.Double(points[i].x - p.x, points[i].y - p.y);
+                       // Symmetric of A
+                       Point2D.Double B = new Point2D.Double(
+                                       -(A.x*v.y*v.y - 2*A.y*v.x*v.y - A.x*v.x*v.x) / lv,
+                                        (A.y*v.y*v.y + 2*A.x*v.x*v.y - A.y*v.x*v.x) / lv);
+                       // Change the origin back
+                       points[i].x = B.x + p.x;
+                       points[i].y = B.y + p.y;
+               }
+       }
+       
+       private void computeHelixTranslations(
+                       Tree<RNANodeValueTemplate> tree, // IN
+                       Map<RNATemplateHelix, Point2D.Double> translateVectors, // OUT (must be initialized)
+                       RNATemplateMapping mapping,      // IN
+                       Point2D.Double parentDeltaVector, // IN
+                       boolean straightBulges
+       ) {
+               RNANodeValueTemplate nvt = tree.getValue();
+               Point2D.Double newDeltaVector = parentDeltaVector;
+               if (nvt instanceof RNANodeValueTemplateBasePair) {
+                       RNATemplateHelix helix = ((RNANodeValueTemplateBasePair) nvt).getHelix();
+                       if (! translateVectors.containsKey(helix)) {
+                               translateVectors.put(helix, parentDeltaVector);
+                               int[] basesInHelixArray;
+                               if (mapping.getAncestor(helix) != null) {
+                                       basesInHelixArray = RNATemplateAlign.intArrayFromList(mapping.getAncestor(helix));
+                                       Arrays.sort(basesInHelixArray);
+                               } else {
+                                       basesInHelixArray = new int[0];
+                               }
+                               Point2D.Double helixDeltaVector = computeLengthIncreaseDelta(basesInHelixArray, helix, straightBulges);
+                               newDeltaVector = new Point2D.Double(parentDeltaVector.x+helixDeltaVector.x, parentDeltaVector.y+helixDeltaVector.y);
+                       } 
+               }
+               for (Tree<RNANodeValueTemplate> subtree: tree.getChildren()) {
+                       computeHelixTranslations(subtree, translateVectors, mapping, newDeltaVector, straightBulges);
+               }
+       }
+       
+       private Map<RNATemplateHelix, Point2D.Double> computeHelixTranslations(
+                       Tree<RNANodeValueTemplate> tree, // IN
+                       RNATemplateMapping mapping,       // IN
+                       boolean straightBulges
+       ) {
+               Map<RNATemplateHelix, Point2D.Double> translateVectors = new HashMap<RNATemplateHelix, Point2D.Double>();
+               computeHelixTranslations(tree, translateVectors, mapping, new Point2D.Double(0,0), straightBulges);
+               return translateVectors;
+       }
+}