1 package fr.orsay.lri.varna.applications.templateEditor;
3 import java.awt.Graphics2D;
4 import java.awt.Polygon;
6 import java.awt.geom.Point2D;
7 import java.util.ArrayList;
8 import java.util.HashSet;
12 import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
13 import fr.orsay.lri.varna.models.rna.RNA;
14 import fr.orsay.lri.varna.models.templates.RNATemplate;
15 import fr.orsay.lri.varna.models.templates.RNATemplate.EdgeEndPointPosition;
16 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
17 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
20 public class Helix extends GraphicalTemplateElement{
24 public Helix(double x, double y, RNATemplate tmp, List<GraphicalTemplateElement> existingRNAElements)
26 this(x,y,getNextAutomaticCaption(existingRNAElements),tmp);
29 public Helix(double x, double y, String cap, RNATemplate tmp)
31 _h = tmp.new RNATemplateHelix(cap);
32 _h.setStartPosition(new Point2D.Double(x,y));
33 _h.setEndPosition(new Point2D.Double(x,y));
38 public Helix(RNATemplateHelix templateHelix) {
43 private static String getNextAutomaticCaption(List<GraphicalTemplateElement> existingRNAElements) {
44 // Find which captions are already used
45 Set<String> captions = new HashSet<String>();
46 for (GraphicalTemplateElement element: existingRNAElements) {
47 if (element instanceof Helix) {
48 Helix helix = (Helix) element;
49 if (helix.getCaption() != null) {
50 captions.add(helix.getCaption());
54 // Find a non-conflicting name for this helix
56 String candidateCaption = "H" + i;
57 if (! captions.contains(candidateCaption)) {
58 return candidateCaption;
63 public void toggleFlipped()
65 _h.setFlipped(!_h.isFlipped());
66 updateAttachedUnpairedRegions();
71 * When an helix is moved/resized/etc... it is necessary to update
72 * the positions of endpoints from unpaired regions that are attached
73 * to the helix. This function updates the endpoints positions of
74 * attached unpaired regions.
76 public void updateAttachedUnpairedRegions() {
77 for (RelativePosition rpos: getConnectedEdges()) {
78 Couple<RelativePosition,GraphicalTemplateElement> c = getAttachedElement(rpos);
79 if (c != null && c.second instanceof UnpairedRegion) {
80 UnpairedRegion unpairedRegion = (UnpairedRegion) c.second;
81 Point2D.Double pos = getEdgePosition(rpos);
82 if (c.first == RelativePosition.RP_CONNECT_START5) {
83 unpairedRegion.setEdge5(pos);
84 } else if (c.first == RelativePosition.RP_CONNECT_END3) {
85 unpairedRegion.setEdge3(pos);
91 public double getPosX()
93 return _h.getStartPosition().x;
96 public String getCaption()
98 return _h.getCaption();
101 public double getPosY()
103 return _h.getStartPosition().y;
106 public RNATemplateHelix getTemplateElement()
111 public void setX(double x)
113 _h.getStartPosition().x = x;
116 public void setY(double y)
118 _h.getStartPosition().y = y;
121 public void setPos(Point2D.Double p)
123 _h.setStartPosition(p);
127 public void setPos(double x, double y)
129 setPos(new Point2D.Double(x,y));
132 public Point2D.Double getPos()
134 return _h.getStartPosition();
137 public void moveCenter(double x, double y)
139 Point2D.Double center = new Point2D.Double((_h.getStartPosition().x+_h.getEndPosition().x)/2.0,(_h.getStartPosition().y+_h.getEndPosition().y)/2.0);
140 double dx = x-center.x;
141 double dy = y-center.y;
142 _h.setStartPosition(new Point2D.Double(_h.getStartPosition().x+dx,_h.getStartPosition().y+dy));
143 _h.setEndPosition(new Point2D.Double(_h.getEndPosition().x+dx,_h.getEndPosition().y+dy));
147 public void setExtent(double x, double y)
149 setExtent(new Point2D.Double(x,y));
152 private void updateLength()
154 _h.setLength(getNbBP());
157 public void setExtent(Point2D.Double p)
159 _h.setEndPosition(p);
163 public double getExtentX()
165 return _h.getEndPosition().x;
168 public Point2D.Double getExtent()
170 return _h.getEndPosition();
173 public double getExtentY()
175 return _h.getEndPosition().y;
178 public static final double BASE_PAIR_DISTANCE = RNA.BASE_PAIR_DISTANCE;
179 public static final double LOOP_DISTANCE = RNA.LOOP_DISTANCE;
180 public static final double SELECTION_RADIUS = 15.0;
185 public Point2D.Double getAbsStart5()
187 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
188 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
191 Point2D.Double start5 = new Point2D.Double((getPosX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()-Helix.BASE_PAIR_DISTANCE*ny/2.0));
195 public Point2D.Double getAbsStart3()
197 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
198 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
201 Point2D.Double start3 = new Point2D.Double((getPosX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()+Helix.BASE_PAIR_DISTANCE*ny/2.0));
205 public Point2D.Double getAbsEnd5()
207 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
208 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
211 Point2D.Double end5 = new Point2D.Double((getExtentX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()-Helix.BASE_PAIR_DISTANCE*ny/2.0));
215 public Point2D.Double getAbsEnd3()
217 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
218 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
221 Point2D.Double end3 = new Point2D.Double((getExtentX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()+Helix.BASE_PAIR_DISTANCE*ny/2.0));
225 public Point2D.Double getStart5()
228 return getAbsStart3();
230 return getAbsStart5();
234 public Point2D.Double getStart3()
237 return getAbsStart5();
239 return getAbsStart3();
242 public Point2D.Double getEnd5()
250 public Point2D.Double getEnd3()
258 public Polygon getBoundingPolygon()
260 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
261 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
264 Point2D.Double start5 = new Point2D.Double((getPosX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()+Helix.BASE_PAIR_DISTANCE*ny/2.0));
265 Point2D.Double end5 = new Point2D.Double((getExtentX()+Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()+Helix.BASE_PAIR_DISTANCE*ny/2.0));
266 Point2D.Double start3 = new Point2D.Double((getPosX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getPosY()-Helix.BASE_PAIR_DISTANCE*ny/2.0));
267 Point2D.Double end3 = new Point2D.Double((getExtentX()-Helix.BASE_PAIR_DISTANCE*nx/2.0),(getExtentY()-Helix.BASE_PAIR_DISTANCE*ny/2.0));
268 Polygon p = new Polygon();
269 p.addPoint((int)start5.x, (int)start5.y);
270 p.addPoint((int)end5.x, (int)end5.y);
271 p.addPoint((int)end3.x, (int)end3.y);
272 p.addPoint((int)start3.x, (int)start3.y);
277 public Point2D.Double getCenter()
279 return new Point2D.Double((int)((_h.getStartPosition().x+_h.getEndPosition().x)/2.0),
280 (int)((_h.getStartPosition().y+_h.getEndPosition().y)/2.0));
284 public Point2D.Double getCenterEditStart()
286 double dist = _h.getStartPosition().distance(_h.getEndPosition());
287 double dx = (_h.getEndPosition().x-_h.getStartPosition().x)/(dist);
288 double dy = (_h.getEndPosition().y-_h.getStartPosition().y)/(dist);
289 return new Point2D.Double((int)(_h.getStartPosition().x+(dist-10.0)*dx),
290 (int)(_h.getStartPosition().y+(dist-10.0)*dy));
293 public Point2D.Double getCenterEditEnd()
295 double dist = _h.getStartPosition().distance(_h.getEndPosition());
296 double dx = (_h.getEndPosition().x-_h.getStartPosition().x)/(dist);
297 double dy = (_h.getEndPosition().y-_h.getStartPosition().y)/(dist);
298 return new Point2D.Double((int)(_h.getStartPosition().x+(10.0)*dx),
299 (int)(_h.getStartPosition().y+(10.0)*dy));
303 public Shape getSelectionBox()
305 double dx = (_h.getStartPosition().x-_h.getEndPosition().x)/(_h.getStartPosition().distance(_h.getEndPosition()));
306 double dy = (_h.getStartPosition().y-_h.getEndPosition().y)/(_h.getStartPosition().distance(_h.getEndPosition()));
309 Polygon hbox = getBoundingPolygon();
310 Polygon p = new Polygon();
311 Point2D.Double start5 = new Point2D.Double(hbox.xpoints[0]+SELECTION_RADIUS*(dx+nx),hbox.ypoints[0]+SELECTION_RADIUS*(dy+ny));
312 Point2D.Double end5 = new Point2D.Double(hbox.xpoints[1]+SELECTION_RADIUS*(-dx+nx),hbox.ypoints[1]+SELECTION_RADIUS*(-dy+ny));
313 Point2D.Double end3 = new Point2D.Double(hbox.xpoints[2]+SELECTION_RADIUS*(-dx-nx),hbox.ypoints[2]+SELECTION_RADIUS*(-dy-ny));;
314 Point2D.Double start3 = new Point2D.Double(hbox.xpoints[3]+SELECTION_RADIUS*(dx-nx),hbox.ypoints[3]+SELECTION_RADIUS*(dy-ny));;
315 p.addPoint((int)start5.x, (int)start5.y);
316 p.addPoint((int)end5.x, (int)end5.y);
317 p.addPoint((int)end3.x, (int)end3.y);
318 p.addPoint((int)start3.x, (int)start3.y);
322 public Shape getArea()
324 return getSelectionBox();
328 public static final double EDIT_RADIUS = 10.0;
329 public static final double MOVE_RADIUS = 13.0;
330 public static final double BASE_RADIUS = 8.0;
331 public static final double EDGE_BASE_RADIUS = 7.0;
334 public RelativePosition getRelativePosition(double x, double y)
336 Point2D.Double current = new Point2D.Double(x,y);
337 Shape p = getSelectionBox();
338 if (p.contains(current))
340 if (getCenterEditStart().distance(current)<EDIT_RADIUS)
342 return RelativePosition.RP_EDIT_START;
344 else if (getCenterEditEnd().distance(current)<EDIT_RADIUS)
346 return RelativePosition.RP_EDIT_END;
348 else if (getCenter().distance(current)<MOVE_RADIUS)
350 return RelativePosition.RP_INNER_MOVE;
352 else if (getEnd3().distance(current)<EDGE_BASE_RADIUS)
354 return RelativePosition.RP_CONNECT_END3;
356 else if (getEnd5().distance(current)<EDGE_BASE_RADIUS)
358 return RelativePosition.RP_CONNECT_END5;
360 else if (getStart3().distance(current)<EDGE_BASE_RADIUS)
362 return RelativePosition.RP_CONNECT_START3;
364 else if (getStart5().distance(current)<EDGE_BASE_RADIUS)
366 return RelativePosition.RP_CONNECT_START5;
369 return RelativePosition.RP_INNER_GENERAL;
372 return RelativePosition.RP_OUTER;
375 public RelativePosition getClosestEdge(double x, double y)
377 RelativePosition result = RelativePosition.RP_OUTER;
378 double dist = Double.MAX_VALUE;
379 Point2D.Double current = new Point2D.Double(x,y);
380 double dcand = getStart5().distance(current);
384 result = RelativePosition.RP_CONNECT_START5;
386 dcand = getStart3().distance(current);
390 result = RelativePosition.RP_CONNECT_START3;
392 dcand = getEnd5().distance(current);
396 result = RelativePosition.RP_CONNECT_END5;
398 dcand = getEnd3().distance(current);
402 result = RelativePosition.RP_CONNECT_END3;
407 public Point2D.Double getEdgePosition(Helix.RelativePosition edge)
411 case RP_CONNECT_END3:
413 case RP_CONNECT_END5:
415 case RP_CONNECT_START5:
417 case RP_CONNECT_START3:
429 public RelativePosition getConnectedEdge(RelativePosition edge)
433 case RP_CONNECT_END3:
434 return RelativePosition.RP_CONNECT_START3;
435 case RP_CONNECT_END5:
436 return RelativePosition.RP_CONNECT_START5;
437 case RP_CONNECT_START5:
438 return RelativePosition.RP_CONNECT_END5;
439 case RP_CONNECT_START3:
440 return RelativePosition.RP_CONNECT_END3;
442 return RelativePosition.RP_OUTER;
445 public boolean isAnchored5Start()
447 return (_h.getIn1().getOtherElement()!=null);
450 public boolean isAnchored5End()
452 return (_h.getOut1().getOtherElement()!=null);
455 public boolean isAnchored3Start()
457 return (_h.getOut2().getOtherElement()!=null);
460 public boolean isAnchored3End()
462 return (_h.getIn2().getOtherElement()!=null);
467 Point2D.Double pos = getPos();
468 Point2D.Double extent = getExtent();
469 double helLength = pos.distance(extent);
470 return Math.max((int)Math.round(helLength/Helix.LOOP_DISTANCE) + 1, 2);
473 public void draw(Graphics2D g2d,boolean isSelected)
475 g2d.setStroke(_solidStroke);
476 Point2D.Double pos = getPos();
477 Point2D.Double extent = getExtent();
478 double dx = (pos.x-extent.x)/pos.distance(extent);
479 double dy = (pos.y-extent.y)/pos.distance(extent);
480 double nx = Helix.BASE_PAIR_DISTANCE*dy/2.0;
481 double ny = -Helix.BASE_PAIR_DISTANCE*dx/2.0;
482 Point2D.Double start5 = getStart5();
483 Point2D.Double end5 = getEnd5();
484 Point2D.Double start3 = getStart3();
485 Point2D.Double end3 = getEnd3();
487 for (RelativePosition e:this.getConnectedEdges())
489 g2d.setStroke(_solidStroke);
490 g2d.setColor(BACKBONE_COLOR);
491 Point2D.Double p1 = this.getEdgePosition(e);
492 Point2D.Double p2 = this.getEdgePosition(getConnectedEdge(e));
493 if (_mainColors.containsKey(e))
495 g2d.setColor(_mainColors.get(e));
496 g2d.setStroke(this._boldStroke);
498 g2d.drawLine((int)p1.x,(int)p1.y,(int)(p1.x+p2.x)/2,(int)(p1.y+p2.y)/2);
501 g2d.setColor(NUMBER_COLOR);
502 double captionx = (_h.isFlipped()?-1.0:1.0)*1.5*nx+(start3.x+end3.x)/2.0;
503 double captiony = (_h.isFlipped()?-1.0:1.0)*1.5*ny+(start3.y+end3.y)/2.0;
504 drawStringCentered(g2d, getCaption(),captionx,captiony);
506 int nbBasePairs = _h.getLength();
508 g2d.setStroke(_solidStroke);
510 for (int i=0;i<nbBasePairs;i++)
512 g2d.setColor(BASE_PAIR_COLOR);
513 Point2D.Double p5 = new Point2D.Double(
514 (i*start5.x+(nbBasePairs-1-i)*end5.x)/(nbBasePairs-1),
515 (i*start5.y+(nbBasePairs-1-i)*end5.y)/(nbBasePairs-1));
516 Point2D.Double p3 = new Point2D.Double(
517 (i*start3.x+(nbBasePairs-1-i)*end3.x)/(nbBasePairs-1),
518 (i*start3.y+(nbBasePairs-1-i)*end3.y)/(nbBasePairs-1));
519 g2d.drawLine((int)p3.x,(int)p3.y,(int)p5.x,(int)p5.y);
522 if (isAnchored5End())
523 { drawMagnet(g2d, p5); }
525 { drawAnchor3(g2d, p5); }
527 if (isAnchored3End())
528 { drawMagnet(g2d, p3); }
530 { drawAnchor5(g2d, p3); }
532 else if (i==nbBasePairs-1)
534 if (isAnchored5Start())
535 { drawMagnet(g2d, p5); }
537 { drawAnchor5(g2d, p5); }
539 if (isAnchored3Start())
540 { drawMagnet(g2d, p3); }
542 { drawAnchor3(g2d, p3); }
555 Shape p = getSelectionBox();
556 g2d.setColor(BACKBONE_COLOR);
557 g2d.setStroke(_dashedStroke);
559 Point2D.Double center = getCenter();
560 g2d.setStroke(_solidStroke);
561 drawMove(g2d,center);
562 drawEditStart(g2d,this,-dx,-dy,nx,ny);
563 drawEditEnd(g2d,this,-dx,-dy,nx,ny);
568 public void translate(double x, double y) {
569 Point2D.Double pos = getPos();
570 Point2D.Double extent = getExtent();
571 setPos(pos.x+x,pos.y+y);
572 setExtent(extent.x+x,extent.y+y);
575 public RNATemplateHelix getHelix() {
580 public EdgeEndPoint getEndPoint(RelativePosition r) {
583 case RP_CONNECT_START5:
585 case RP_CONNECT_START3:
587 case RP_CONNECT_END3:
589 case RP_CONNECT_END5:
595 public boolean isIn(RelativePosition r) {
598 case RP_CONNECT_START5:
600 case RP_CONNECT_START3:
602 case RP_CONNECT_END3:
604 case RP_CONNECT_END5:
611 public void attach(GraphicalTemplateElement e, RelativePosition edgeOrig, RelativePosition edgeDest) throws ExceptionInvalidRNATemplate
613 super.attach(e,edgeOrig,edgeDest);
614 EdgeEndPoint e1 = this.getEndPoint(edgeOrig);
615 EdgeEndPoint e2 = e.getEndPoint(edgeDest);
616 boolean parity1 = this.isIn(edgeOrig);
617 boolean parity2 = e.isIn(edgeDest);
618 if ((e1!=null)&&(e2!=null)&&(parity1!=parity2))
628 public void detach(RelativePosition edge)
630 // If the underlying template element is still connected, disconnect it
631 if (getEndPoint(edge).isConnected())
633 getEndPoint(edge).disconnect();
636 // Call the parent class detach function, which will also take care to disconnect this other endpoint of this edge
640 public void setEdgePosition(RelativePosition edge, java.awt.geom.Point2D.Double pos) {
650 moveCenter(pos.x,pos.y);
653 updateAttachedUnpairedRegions();
656 public ArrayList<RelativePosition> getConnectedEdges() {
657 ArrayList<RelativePosition> result = new ArrayList<RelativePosition>();
658 result.add(RelativePosition.RP_CONNECT_START5);
659 result.add(RelativePosition.RP_CONNECT_START3);
660 result.add(RelativePosition.RP_CONNECT_END5);
661 result.add(RelativePosition.RP_CONNECT_END3);
665 public String toString()
667 return "Helix " + getCaption();
670 public RelativePosition relativePositionFromEdgeEndPointPosition(
671 EdgeEndPointPosition pos) {
674 return RelativePosition.RP_CONNECT_START5;
676 return RelativePosition.RP_CONNECT_END5;
678 return RelativePosition.RP_CONNECT_END3;
680 return RelativePosition.RP_CONNECT_START3;