JAL-3026 srcjar files for VARNA and log4j
[jalview.git] / srcjar / fr / orsay / lri / varna / applications / templateEditor / Helix.java
diff --git a/srcjar/fr/orsay/lri/varna/applications/templateEditor/Helix.java b/srcjar/fr/orsay/lri/varna/applications/templateEditor/Helix.java
new file mode 100644 (file)
index 0000000..4c81bed
--- /dev/null
@@ -0,0 +1,687 @@
+package fr.orsay.lri.varna.applications.templateEditor;
+
+import java.awt.Graphics2D;
+import java.awt.Polygon;
+import java.awt.Shape;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
+import fr.orsay.lri.varna.models.rna.RNA;
+import fr.orsay.lri.varna.models.templates.RNATemplate;
+import fr.orsay.lri.varna.models.templates.RNATemplate.EdgeEndPointPosition;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
+import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
+
+
+public class Helix extends GraphicalTemplateElement{
+       
+  RNATemplateHelix _h;
+  
+  public Helix(double x, double y, RNATemplate tmp, List<GraphicalTemplateElement> existingRNAElements)
+  {
+         this(x,y,getNextAutomaticCaption(existingRNAElements),tmp);
+  }
+  
+  public Helix(double x, double y, String cap, RNATemplate tmp)
+  {
+         _h = tmp.new RNATemplateHelix(cap);
+         _h.setStartPosition(new Point2D.Double(x,y));
+         _h.setEndPosition(new Point2D.Double(x,y));
+         _h.setLength(1);
+         _h.setCaption(cap);
+  }
+  
+  public Helix(RNATemplateHelix templateHelix) {
+         _h = templateHelix;
+  }
+  
+  
+  private static String getNextAutomaticCaption(List<GraphicalTemplateElement> existingRNAElements) {
+         // Find which captions are already used
+         Set<String> captions = new HashSet<String>();
+         for (GraphicalTemplateElement element: existingRNAElements) {
+                 if (element instanceof Helix) {
+                         Helix helix = (Helix) element;
+                         if (helix.getCaption() != null) {
+                                 captions.add(helix.getCaption());
+                         }
+                 }
+         }
+         // Find a non-conflicting name for this helix
+         for (int i=1;;i++) {
+                 String candidateCaption = "H" + i;
+                 if (! captions.contains(candidateCaption)) {
+                         return candidateCaption;
+                 }
+         }
+  }
+  
+  public void toggleFlipped()
+  {
+         _h.setFlipped(!_h.isFlipped());
+         updateAttachedUnpairedRegions();
+  }
+  
+
+  /**
+   * When an helix is moved/resized/etc... it is necessary to update
+   * the positions of endpoints from unpaired regions that are attached
+   * to the helix. This function updates the endpoints positions of
+   * attached unpaired regions.
+   */
+  public void updateAttachedUnpairedRegions() {
+         for (RelativePosition rpos: getConnectedEdges()) {
+                 Couple<RelativePosition,GraphicalTemplateElement> c = getAttachedElement(rpos);
+                 if (c != null && c.second instanceof UnpairedRegion) {
+                         UnpairedRegion unpairedRegion = (UnpairedRegion) c.second;
+                         Point2D.Double pos = getEdgePosition(rpos);
+                         if (c.first == RelativePosition.RP_CONNECT_START5) {
+                                 unpairedRegion.setEdge5(pos);
+                         } else if (c.first == RelativePosition.RP_CONNECT_END3) {
+                                 unpairedRegion.setEdge3(pos);
+                         }
+                 }
+         }
+  }
+
+  public double getPosX()
+  {
+         return _h.getStartPosition().x;
+  }
+
+  public String getCaption()
+  {
+         return _h.getCaption();
+  }
+  
+  public double getPosY()
+  {
+         return _h.getStartPosition().y;
+  }
+  
+  public RNATemplateHelix getTemplateElement()
+  {
+         return _h;
+  }
+
+  public void setX(double x)
+  {
+         _h.getStartPosition().x = x;
+  }
+  
+  public void setY(double y)
+  {
+         _h.getStartPosition().y = y;
+  }
+
+  public void setPos(Point2D.Double p)
+  {
+        _h.setStartPosition(p);
+         updateLength();
+  }
+
+  public void setPos(double x, double y)
+  {
+         setPos(new Point2D.Double(x,y));
+  }
+
+  public Point2D.Double getPos()
+  {
+         return _h.getStartPosition();
+  }
+
+  public void moveCenter(double x, double y)
+  {
+         Point2D.Double center = new Point2D.Double((_h.getStartPosition().x+_h.getEndPosition().x)/2.0,(_h.getStartPosition().y+_h.getEndPosition().y)/2.0);
+         double dx = x-center.x;
+         double dy = y-center.y;
+         _h.setStartPosition(new Point2D.Double(_h.getStartPosition().x+dx,_h.getStartPosition().y+dy));
+         _h.setEndPosition(new Point2D.Double(_h.getEndPosition().x+dx,_h.getEndPosition().y+dy));
+  }
+
+  
+  public void setExtent(double x, double y)
+  {
+        setExtent(new Point2D.Double(x,y));
+  }
+
+  private void updateLength()
+  {
+         _h.setLength(getNbBP());
+  }
+  
+  public void setExtent(Point2D.Double p)
+  {
+        _h.setEndPosition(p);
+         updateLength();
+  }
+
+  public double getExtentX()
+  {
+         return _h.getEndPosition().x;
+  }
+
+  public Point2D.Double getExtent()
+  {
+         return _h.getEndPosition();
+  }
+
+  public double getExtentY()
+  {
+         return _h.getEndPosition().y;
+  }
+  
+       public static final double BASE_PAIR_DISTANCE = RNA.BASE_PAIR_DISTANCE;
+       public static final double LOOP_DISTANCE = RNA.LOOP_DISTANCE;
+       public static final double SELECTION_RADIUS = 15.0;
+       
+       
+       
+
+       public Point2D.Double getAbsStart5()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Point2D.Double start5 = new Point2D.Double((getPosX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()-Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               return start5;
+       }
+
+       public Point2D.Double getAbsStart3()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Point2D.Double start3 = new Point2D.Double((getPosX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()+Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               return start3;
+       }
+
+       public Point2D.Double getAbsEnd5()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Point2D.Double end5 = new Point2D.Double((getExtentX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()-Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               return end5;
+       }
+
+       public Point2D.Double getAbsEnd3()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Point2D.Double end3 = new Point2D.Double((getExtentX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()+Helix.BASE_PAIR_DISTANCE*ny/2.0));
+               return end3;
+       }
+       
+       public Point2D.Double getStart5()
+       {
+               if (_h.isFlipped())
+                       return getAbsStart3();
+               else
+                       return getAbsStart5();
+               
+       }
+
+       public Point2D.Double getStart3()
+       {
+               if (_h.isFlipped())
+                       return getAbsStart5();
+               else
+                       return getAbsStart3();
+       }
+
+       public Point2D.Double getEnd5()
+       {
+               if (_h.isFlipped())
+                       return getAbsEnd3();
+               else
+                       return getAbsEnd5();
+       }
+
+       public Point2D.Double getEnd3()
+       {
+               if (_h.isFlipped())
+                       return getAbsEnd5();
+               else
+                       return getAbsEnd3();
+       }
+
+       public Polygon getBoundingPolygon()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Point2D.Double start5 = new Point2D.Double((getPosX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()+Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               Point2D.Double end5 = new Point2D.Double((getExtentX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()+Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               Point2D.Double start3 = new Point2D.Double((getPosX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()-Helix.BASE_PAIR_DISTANCE*ny/2.0)); 
+               Point2D.Double end3 = new Point2D.Double((getExtentX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()-Helix.BASE_PAIR_DISTANCE*ny/2.0));
+               Polygon p = new Polygon();
+               p.addPoint((int)start5.x, (int)start5.y);
+               p.addPoint((int)end5.x, (int)end5.y);
+               p.addPoint((int)end3.x, (int)end3.y);
+               p.addPoint((int)start3.x, (int)start3.y);
+               return p;
+       }
+
+       
+       public Point2D.Double getCenter()
+       {
+               return new Point2D.Double((int)((_h.getStartPosition().x+_h.getEndPosition().x)/2.0),
+                               (int)((_h.getStartPosition().y+_h.getEndPosition().y)/2.0));
+       }
+
+
+       public Point2D.Double getCenterEditStart()
+       {
+               double dist = _h.getStartPosition().distance(_h.getEndPosition());
+               double dx = (_h.getEndPosition().x-_h.getStartPosition().x)/(dist);
+               double dy = (_h.getEndPosition().y-_h.getStartPosition().y)/(dist);
+               return new Point2D.Double((int)(_h.getStartPosition().x+(dist-10.0)*dx),
+                               (int)(_h.getStartPosition().y+(dist-10.0)*dy));
+       }       
+
+       public Point2D.Double getCenterEditEnd()
+       {
+               double dist = _h.getStartPosition().distance(_h.getEndPosition());
+               double dx = (_h.getEndPosition().x-_h.getStartPosition().x)/(dist);
+               double dy = (_h.getEndPosition().y-_h.getStartPosition().y)/(dist);
+               return new Point2D.Double((int)(_h.getStartPosition().x+(10.0)*dx),
+                               (int)(_h.getStartPosition().y+(10.0)*dy));
+       }       
+
+       
+       public Shape getSelectionBox()
+       {
+               double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
+               double nx = dy;
+               double ny = -dx;
+               Polygon hbox = getBoundingPolygon();
+               Polygon p = new Polygon();
+               Point2D.Double start5 = new Point2D.Double(hbox.xpoints[0]+SELECTION_RADIUS*(dx+nx),hbox.ypoints[0]+SELECTION_RADIUS*(dy+ny)); 
+               Point2D.Double end5 = new Point2D.Double(hbox.xpoints[1]+SELECTION_RADIUS*(-dx+nx),hbox.ypoints[1]+SELECTION_RADIUS*(-dy+ny)); 
+               Point2D.Double end3  = new Point2D.Double(hbox.xpoints[2]+SELECTION_RADIUS*(-dx-nx),hbox.ypoints[2]+SELECTION_RADIUS*(-dy-ny));;
+               Point2D.Double start3 = new Point2D.Double(hbox.xpoints[3]+SELECTION_RADIUS*(dx-nx),hbox.ypoints[3]+SELECTION_RADIUS*(dy-ny));; 
+               p.addPoint((int)start5.x, (int)start5.y);
+               p.addPoint((int)end5.x, (int)end5.y);
+               p.addPoint((int)end3.x, (int)end3.y);
+               p.addPoint((int)start3.x, (int)start3.y);
+               return p;
+       }
+
+       public Shape getArea()
+       {
+               return getSelectionBox();
+       }
+
+       
+       public static final double EDIT_RADIUS = 10.0;
+       public static final double MOVE_RADIUS = 13.0;
+       public static final double BASE_RADIUS = 8.0;
+       public static final double EDGE_BASE_RADIUS = 7.0;
+       
+       
+       public RelativePosition getRelativePosition(double x, double y)
+       {
+               Point2D.Double current = new Point2D.Double(x,y);
+               Shape p = getSelectionBox();
+               if (p.contains(current))
+               {
+                       if (getCenterEditStart().distance(current)<EDIT_RADIUS)
+                       {
+                               return RelativePosition.RP_EDIT_START;
+                       }
+                       else if (getCenterEditEnd().distance(current)<EDIT_RADIUS)
+                       {
+                               return RelativePosition.RP_EDIT_END;
+                       }
+                       else if (getCenter().distance(current)<MOVE_RADIUS)
+                       {
+                               return RelativePosition.RP_INNER_MOVE;
+                       }
+                       else if (getEnd3().distance(current)<EDGE_BASE_RADIUS)
+                       {
+                               return RelativePosition.RP_CONNECT_END3;
+                       }
+                       else if (getEnd5().distance(current)<EDGE_BASE_RADIUS)
+                       {
+                               return RelativePosition.RP_CONNECT_END5;
+                       }
+                       else if (getStart3().distance(current)<EDGE_BASE_RADIUS)
+                       {
+                               return RelativePosition.RP_CONNECT_START3;
+                       }
+                       else if (getStart5().distance(current)<EDGE_BASE_RADIUS)
+                       {
+                               return RelativePosition.RP_CONNECT_START5;
+                       }
+                               
+                       return RelativePosition.RP_INNER_GENERAL;
+               }
+               else 
+                       return RelativePosition.RP_OUTER;  
+       }
+
+       public RelativePosition getClosestEdge(double x, double y)
+       {
+               RelativePosition result = RelativePosition.RP_OUTER;
+               double dist = Double.MAX_VALUE;
+               Point2D.Double current = new Point2D.Double(x,y);
+               double dcand = getStart5().distance(current);
+           if (dcand<dist)
+           {
+               dist = dcand;
+               result = RelativePosition.RP_CONNECT_START5;
+           }
+               dcand = getStart3().distance(current);
+           if (dcand<dist)
+           {
+               dist = dcand;
+               result = RelativePosition.RP_CONNECT_START3;
+           }
+               dcand = getEnd5().distance(current);
+           if (dcand<dist)
+           {
+               dist = dcand;
+               result = RelativePosition.RP_CONNECT_END5;
+           }
+               dcand = getEnd3().distance(current);
+           if (dcand<dist)
+           {
+               dist = dcand;
+               result = RelativePosition.RP_CONNECT_END3;
+           }
+               return result;
+       }
+       
+       public Point2D.Double getEdgePosition(Helix.RelativePosition edge)
+       {
+               switch (edge)
+               {
+                       case RP_CONNECT_END3:
+                               return getEnd3();
+                       case RP_CONNECT_END5:
+                               return getEnd5();
+                       case RP_CONNECT_START5:
+                               return getStart5();
+                       case RP_CONNECT_START3:
+                               return getStart3();
+                       case RP_EDIT_START:
+                               return getPos();
+                       case RP_EDIT_END:
+                               return  getExtent();
+                       case RP_INNER_MOVE:
+                               return  getCenter();
+               }
+               return getCenter();
+       }
+
+       public RelativePosition getConnectedEdge(RelativePosition edge)
+       {
+               switch (edge)
+               {
+                       case RP_CONNECT_END3:
+                               return RelativePosition.RP_CONNECT_START3;
+                       case RP_CONNECT_END5:
+                               return RelativePosition.RP_CONNECT_START5;
+                       case RP_CONNECT_START5:
+                               return RelativePosition.RP_CONNECT_END5;
+                       case RP_CONNECT_START3:
+                               return RelativePosition.RP_CONNECT_END3;
+               }
+               return RelativePosition.RP_OUTER;
+       }
+       
+       public boolean isAnchored5Start()
+       {
+               return (_h.getIn1().getOtherElement()!=null);
+       }
+
+       public boolean isAnchored5End()
+       {
+               return (_h.getOut1().getOtherElement()!=null);
+       }
+       
+       public boolean isAnchored3Start()
+       {
+               return (_h.getOut2().getOtherElement()!=null);
+       }
+
+       public boolean isAnchored3End()
+       {
+               return (_h.getIn2().getOtherElement()!=null);
+       }
+       
+       public int getNbBP()
+       {
+               Point2D.Double pos = getPos();
+               Point2D.Double extent = getExtent();
+               double helLength =  pos.distance(extent);
+               return Math.max((int)Math.round(helLength/Helix.LOOP_DISTANCE) + 1, 2);
+       }
+       
+       public void draw(Graphics2D g2d,boolean isSelected)
+       {
+               g2d.setStroke(_solidStroke);
+               Point2D.Double pos = getPos();
+               Point2D.Double extent = getExtent();
+               double dx = (pos.x-extent.x)/pos.distance(extent);
+               double dy = (pos.y-extent.y)/pos.distance(extent);
+               double nx = Helix.BASE_PAIR_DISTANCE*dy/2.0;
+               double ny = -Helix.BASE_PAIR_DISTANCE*dx/2.0;
+               Point2D.Double start5 = getStart5(); 
+               Point2D.Double end5 = getEnd5(); 
+               Point2D.Double start3 = getStart3(); 
+               Point2D.Double end3 = getEnd3();
+               
+               for (RelativePosition e:this.getConnectedEdges())
+               {
+                       g2d.setStroke(_solidStroke);
+                       g2d.setColor(BACKBONE_COLOR);
+                       Point2D.Double p1 = this.getEdgePosition(e);
+                       Point2D.Double p2 = this.getEdgePosition(getConnectedEdge(e));
+                       if (_mainColors.containsKey(e))
+                       { 
+                               g2d.setColor(_mainColors.get(e));
+                               g2d.setStroke(this._boldStroke);
+                       }
+                       g2d.drawLine((int)p1.x,(int)p1.y,(int)(p1.x+p2.x)/2,(int)(p1.y+p2.y)/2);
+               }
+
+               g2d.setColor(NUMBER_COLOR);
+               double captionx = (_h.isFlipped()?-1.0:1.0)*1.5*nx+(start3.x+end3.x)/2.0;
+               double captiony = (_h.isFlipped()?-1.0:1.0)*1.5*ny+(start3.y+end3.y)/2.0;
+               drawStringCentered(g2d, getCaption(),captionx,captiony);
+
+               int nbBasePairs = _h.getLength();
+
+               g2d.setStroke(_solidStroke);
+
+               for (int i=0;i<nbBasePairs;i++)
+               {
+                       g2d.setColor(BASE_PAIR_COLOR);
+                       Point2D.Double p5 = new Point2D.Double(
+                                       (i*start5.x+(nbBasePairs-1-i)*end5.x)/(nbBasePairs-1),
+                                       (i*start5.y+(nbBasePairs-1-i)*end5.y)/(nbBasePairs-1));
+                       Point2D.Double p3 = new Point2D.Double(
+                                       (i*start3.x+(nbBasePairs-1-i)*end3.x)/(nbBasePairs-1),
+                                       (i*start3.y+(nbBasePairs-1-i)*end3.y)/(nbBasePairs-1));
+                       g2d.drawLine((int)p3.x,(int)p3.y,(int)p5.x,(int)p5.y);
+                       if (i==0)
+                       {
+                               if (isAnchored5End())
+                               { drawMagnet(g2d, p5); }
+                               else
+                               { drawAnchor3(g2d, p5); }
+                               
+                               if (isAnchored3End())
+                               { drawMagnet(g2d, p3); }
+                               else
+                               { drawAnchor5(g2d, p3); }
+                       }
+                       else if (i==nbBasePairs-1)
+                       {
+                               if (isAnchored5Start())
+                               { drawMagnet(g2d, p5); }
+                               else
+                               { drawAnchor5(g2d, p5); }
+                               
+                               if (isAnchored3Start())
+                               { drawMagnet(g2d, p3); }
+                               else
+                               { drawAnchor3(g2d, p3); }
+                       }
+                       else
+                       {
+                               drawBase(g2d, p3);
+                               drawBase(g2d, p5);
+                       }
+               }
+
+               if (isSelected)
+               {
+                       nx = dy;
+                       ny = -dx;
+                       Shape p = getSelectionBox();
+                       g2d.setColor(BACKBONE_COLOR);
+                       g2d.setStroke(_dashedStroke);
+                       g2d.draw(p);
+                       Point2D.Double center = getCenter();
+                       g2d.setStroke(_solidStroke);
+                       drawMove(g2d,center);
+                       drawEditStart(g2d,this,-dx,-dy,nx,ny);                  
+                       drawEditEnd(g2d,this,-dx,-dy,nx,ny);                    
+               }
+       }
+
+
+       public void translate(double x, double y) {
+                 Point2D.Double pos = getPos();
+                 Point2D.Double extent = getExtent();
+                 setPos(pos.x+x,pos.y+y);
+                 setExtent(extent.x+x,extent.y+y);
+       }
+
+       public RNATemplateHelix getHelix() {
+               return _h;
+       }
+       
+
+       public EdgeEndPoint getEndPoint(RelativePosition r) {
+               switch(r)
+               {
+                       case RP_CONNECT_START5:
+                       return _h.getIn1();                             
+                       case RP_CONNECT_START3:
+                       return _h.getOut2();                            
+                       case RP_CONNECT_END3:
+                               return _h.getIn2();
+                       case RP_CONNECT_END5:
+                               return _h.getOut1();
+               }
+               return null;
+       }
+
+       public boolean isIn(RelativePosition r) {
+               switch(r)
+               {
+               case RP_CONNECT_START5:
+                       return true;                            
+               case RP_CONNECT_START3:
+                       return false;                           
+               case RP_CONNECT_END3:
+                       return true;
+               case RP_CONNECT_END5:
+                       return false;
+               }
+               return true;
+       }
+       
+       
+       public void attach(GraphicalTemplateElement e, RelativePosition edgeOrig, RelativePosition edgeDest) throws ExceptionInvalidRNATemplate
+       {
+               super.attach(e,edgeOrig,edgeDest);
+               EdgeEndPoint e1 = this.getEndPoint(edgeOrig);
+               EdgeEndPoint e2 = e.getEndPoint(edgeDest);
+               boolean parity1 = this.isIn(edgeOrig);
+               boolean parity2 = e.isIn(edgeDest);
+               if ((e1!=null)&&(e2!=null)&&(parity1!=parity2))
+               {
+                  e1.disconnect();
+                  e2.disconnect();
+                  e1.connectTo(e2);
+               }
+       }
+       
+
+
+       public void detach(RelativePosition edge)
+       {
+               // If the underlying template element is still connected, disconnect it
+               if (getEndPoint(edge).isConnected())
+               {
+                       getEndPoint(edge).disconnect();
+               }
+               
+               // Call the parent class detach function, which will also take care to disconnect this other endpoint of this edge
+               super.detach(edge);
+       }
+
+       public void setEdgePosition(RelativePosition edge, java.awt.geom.Point2D.Double pos) {
+               switch (edge)
+               {
+                       case RP_EDIT_START:
+                               setPos(pos);
+                               break;
+                       case RP_EDIT_END:
+                               setExtent(pos);
+                               break;
+                       case RP_INNER_MOVE:
+                               moveCenter(pos.x,pos.y);
+                               break;
+               }
+               updateAttachedUnpairedRegions();
+       }
+
+       public ArrayList<RelativePosition> getConnectedEdges() {
+               ArrayList<RelativePosition> result = new ArrayList<RelativePosition>();
+               result.add(RelativePosition.RP_CONNECT_START5);
+               result.add(RelativePosition.RP_CONNECT_START3);
+               result.add(RelativePosition.RP_CONNECT_END5);
+               result.add(RelativePosition.RP_CONNECT_END3);
+               return result;
+       }
+       
+       public String toString()
+       {
+               return "Helix " + getCaption();
+       }
+
+       public RelativePosition relativePositionFromEdgeEndPointPosition(
+                       EdgeEndPointPosition pos) {
+               switch (pos) {
+               case IN1:
+                       return RelativePosition.RP_CONNECT_START5;
+               case OUT1:
+                       return RelativePosition.RP_CONNECT_END5;
+               case IN2:
+                       return RelativePosition.RP_CONNECT_END3;
+               case OUT2:
+                       return RelativePosition.RP_CONNECT_START3;
+               default:
+                       return null;
+               }
+       }
+
+}
+