1 package fr.orsay.lri.varna.applications.templateEditor;
2 import java.awt.Graphics2D;
3 import java.awt.Polygon;
5 import java.awt.geom.CubicCurve2D;
6 import java.awt.geom.GeneralPath;
7 import java.awt.geom.Point2D;
8 import java.awt.geom.Point2D.Double;
9 import java.util.ArrayList;
11 import fr.orsay.lri.varna.applications.templateEditor.GraphicalTemplateElement.RelativePosition;
12 import fr.orsay.lri.varna.exceptions.ExceptionEdgeEndpointAlreadyConnected;
13 import fr.orsay.lri.varna.exceptions.ExceptionInvalidRNATemplate;
14 import fr.orsay.lri.varna.models.geom.CubicBezierCurve;
15 import fr.orsay.lri.varna.models.templates.RNATemplate;
16 import fr.orsay.lri.varna.models.templates.RNATemplate.EdgeEndPointPosition;
17 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement;
18 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateHelix;
19 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateUnpairedSequence;
20 import fr.orsay.lri.varna.models.templates.RNATemplate.RNATemplateElement.EdgeEndPoint;
22 public class UnpairedRegion extends GraphicalTemplateElement{
23 private RNATemplateUnpairedSequence _e;
24 public static final double DEFAULT_VECTOR_LENGTH = 35;
25 public static final double DEFAULT_VECTOR_DISTANCE = 35;
27 private Point2D.Double[] sequenceBasesCoords = null;
29 public UnpairedRegion(double x, double y, RNATemplate tmp)
31 _e = tmp.new RNATemplateUnpairedSequence("");
32 _e.setVertex5(new Point2D.Double(x,y));
33 _e.setVertex3(new Point2D.Double(x+DEFAULT_VECTOR_DISTANCE,y));
34 _e.setInTangentVectorLength(DEFAULT_VECTOR_LENGTH);
35 _e.setInTangentVectorAngle(-Math.PI/2.0);
36 _e.setOutTangentVectorLength(DEFAULT_VECTOR_LENGTH);
37 _e.setOutTangentVectorAngle(-Math.PI/2.0);
43 * Build an UnpairedRegion object from a RNATemplateUnpairedSequence
44 * object. The RNATemplateUnpairedSequence must be connected to
45 * an helix on both sides.
47 public UnpairedRegion(RNATemplateUnpairedSequence templateSequence)
49 _e = templateSequence;
52 public Point2D.Double getEdge5()
54 RelativePosition r = RelativePosition.RP_CONNECT_START5;
55 Couple<RelativePosition,GraphicalTemplateElement> c = getAttachedElement(r);
56 return (isAnchored5()? c.second.getEdgePosition(c.first): _e.getVertex5());
59 public Point2D.Double getEdge3()
61 RelativePosition r = RelativePosition.RP_CONNECT_END3;
62 Couple<RelativePosition,GraphicalTemplateElement> c = getAttachedElement(r);
63 return (isAnchored3()? c.second.getEdgePosition(c.first): _e.getVertex3());
66 public Point2D.Double getCenter()
68 Point2D.Double p1 = getEdge5();
69 Point2D.Double p2 = getEdge3();
70 return new Point2D.Double((p1.x+p2.x)/2.,(p1.y+p2.y)/2.);
74 public void setEdge5(Point2D.Double d)
80 public void setEdge3(Point2D.Double d)
86 public void setCenter(Point2D.Double d)
88 Point2D.Double p1 = getEdge5();
89 Point2D.Double p2 = getEdge3();
90 double dx = p1.x-p2.x;
91 double dy = p1.y-p2.y;
92 _e.setVertex3(new Point2D.Double(d.x-dx/2.,d.y-dy/2.));
93 _e.setVertex5(new Point2D.Double(d.x+dx/2.,d.y+dy/2.));
98 public boolean isAnchored5()
100 return (_e.getIn().getOtherElement()!=null);
103 public boolean isAnchored3()
105 return (_e.getOut().getOtherElement()!=null);
109 public static Shape bezToShape(CubicBezierCurve c)
111 GeneralPath p = new GeneralPath();
113 double[] tab = new double[nb];
114 for (int i=0;i<nb;i++)
116 tab[i] = (c.getApproxCurveLength()*(double)i)/(double)nb;
118 Point2D.Double[] points = c.uniformParam(tab);
119 System.out.println(points.length);
120 p.moveTo((float)points[0].x,(float)points[0].y);
121 for (int i=1;i<nb;i++)
123 Point2D.Double a = points[i];
124 System.out.println(a);
125 p.lineTo((float)a.x,(float)a.y);
127 p.lineTo((float)c.getP3().x,(float)c.getP3().y);
133 public Shape getCurve()
135 Point2D.Double p5 = getEdge5();
136 Point2D.Double p3 = getEdge3();
137 Point2D.Double t5 = getControl5();
138 Point2D.Double t3 = getControl3();
139 return new CubicCurve2D.Double(p5.x,p5.y,t5.x,t5.y,t3.x,t3.y,p3.x,p3.y);
140 //CubicBezierCurve c = new CubicBezierCurve( p5, t5, t3, p3, 30);
141 //return bezToShape(c);
145 private int estimateNumberOfBases()
147 Point2D.Double p5 = getEdge5();
148 Point2D.Double p3 = getEdge3();
149 Point2D.Double t5 = getControl5();
150 Point2D.Double t3 = getControl3();
151 CubicBezierCurve c = new CubicBezierCurve( p5, t5, t3, p3, 30);
152 // Extremities don't count as unpaired bases because they are part of the connected helix.
153 return Math.max((int)Math.round(c.getApproxCurveLength()/Helix.LOOP_DISTANCE)-1, 1);
156 private void updateLength()
158 this._e.setLength(estimateNumberOfBases());
164 * Mark the coordinates as invalid, ie. need to be calculated again if we want to draw them.
166 private void invalidateCoords() {
167 sequenceBasesCoords = null;
171 * Calculate the positions of the unpaired bases.
173 private void calculeCoords() {
174 //System.out.println("calculate coords");
176 Point2D.Double p5 = getEdge5();
177 Point2D.Double p3 = getEdge3();
178 Point2D.Double t5 = getControl5();
179 Point2D.Double t3 = getControl3();
181 // Draw bases on curve:
182 int n = _e.getLength();
183 // We choose to approximate the Bezier curve by 10*n straight lines.
184 CubicBezierCurve bezier = new CubicBezierCurve(p5, t5, t3, p3, 10*n);
185 double curveLength = bezier.getApproxCurveLength();
186 double delta_t = curveLength / (n+1);
187 double[] t = new double[n];
188 for (int k=0; k<n; k++) {
189 t[k] = (k+1) * delta_t;
191 sequenceBasesCoords = bezier.uniformParam(t);
194 public void draw(Graphics2D g2d, boolean selected) {
195 Point2D.Double p5 = getEdge5();
196 Point2D.Double p3 = getEdge3();
197 Point2D.Double t5 = getControl5();
198 Point2D.Double t3 = getControl3();
201 g2d.setStroke(_dashedStroke);
202 g2d.setColor(BACKBONE_COLOR);
203 g2d.draw(getBoundingPolygon());
204 g2d.setStroke(_solidStroke);
207 double d5x = (t5.x-p5.x)/(t5.distance(p5));
208 double d5y = (t5.y-p5.y)/(t5.distance(p5));
209 double d3x = (t3.x-p3.x)/(t3.distance(p3));
210 double d3y = (t3.y-p3.y)/(t3.distance(p3));
212 Point2D.Double tp5 = new Point2D.Double(t5.x-shift*d5x,t5.y-shift*d5y);
213 Point2D.Double tp3 = new Point2D.Double(t3.x-shift*d3x,t3.y-shift*d3y);
215 drawArrow(g2d, p5, tp5, UNPAIRED_ARROW_WIDTH);
216 drawArrow(g2d, p3, tp3, UNPAIRED_ARROW_WIDTH);
218 g2d.setColor(BACKBONE_COLOR);
219 g2d.setStroke(_solidStroke);
220 g2d.draw(getCurve());
222 if (sequenceBasesCoords == null) {
225 for (int k=0; k<sequenceBasesCoords.length; k++) {
226 drawBase(g2d, sequenceBasesCoords[k]);
230 {drawAnchor5(g2d,p5); }
232 { drawMagnet(g2d,p5);}
234 {drawAnchor3(g2d,p3); }
236 { drawMagnet(g2d,p3);}
238 if (!isAnchored5() && !isAnchored3())
239 drawMove(g2d, getCenter());
243 public Point2D.Double getControl5()
245 Point2D.Double p5 = getEdge5();
246 double angle = _e.getInTangentVectorAngle();
247 return new Point2D.Double(p5.x+Math.cos(angle)*_e.getInTangentVectorLength(),
248 p5.y+Math.sin(angle)*_e.getInTangentVectorLength());
251 public Point2D.Double getControl3()
253 Point2D.Double p3 = getEdge3();
254 double angle = _e.getOutTangentVectorAngle();
255 return new Point2D.Double(p3.x+Math.cos(angle)*_e.getOutTangentVectorLength(),
256 p3.y+Math.sin(angle)*_e.getOutTangentVectorLength());
259 public static final double MAX_UNPAIRED_CONTROL_DISTANCE = 10.0;
260 public static final double UNPAIRED_ARROW_WIDTH = 6.0;
264 public Polygon getBoundingPolygon() {
265 Point2D.Double p5 = getEdge5();
266 Point2D.Double p3 = getEdge3();
267 Point2D.Double t5 = getControl5();
268 Point2D.Double t3 = getControl3();
270 double minx = Math.min(p5.x,Math.min(p3.x,Math.min(t5.x,t3.x)));
271 double maxx = Math.max(p5.x,Math.max(p3.x,Math.max(t5.x,t3.x)));
272 double miny = Math.min(p5.y,Math.min(p3.y,Math.min(t5.y,t3.y)));
273 double maxy = Math.max(p5.y,Math.max(p3.y,Math.max(t5.y,t3.y)));
278 int[] x = {(int)minx,(int)maxx,(int)maxx,(int)minx};
279 int[] y = {(int)miny,(int)miny,(int)maxy,(int)maxy};
280 return new Polygon(x,y,4);
283 public RelativePosition getClosestEdge(double x, double y) {
284 Point2D.Double p = new Point2D.Double(x,y);
285 Point2D.Double p5 = getEdge5();
286 Point2D.Double p3 = getEdge3();
287 Point2D.Double t5 = getControl5();
288 Point2D.Double t3 = getControl3();
289 Point2D.Double ct = getCenter();
290 ArrayList<Couple<java.lang.Double,RelativePosition>> v = new ArrayList<Couple<java.lang.Double,RelativePosition>>();
291 v.add(new Couple<java.lang.Double,RelativePosition>(p.distance(p5),RelativePosition.RP_CONNECT_START5));
292 v.add(new Couple<java.lang.Double,RelativePosition>(p.distance(p3),RelativePosition.RP_CONNECT_END3));
293 v.add(new Couple<java.lang.Double,RelativePosition>(p.distance(t5),RelativePosition.RP_EDIT_TANGENT_5));
294 v.add(new Couple<java.lang.Double,RelativePosition>(p.distance(t3),RelativePosition.RP_EDIT_TANGENT_3));
295 v.add(new Couple<java.lang.Double,RelativePosition>(p.distance(ct),RelativePosition.RP_INNER_MOVE));
296 double dist = java.lang.Double.MAX_VALUE;
297 RelativePosition r = RelativePosition.RP_OUTER;
299 for (Couple<java.lang.Double,RelativePosition> c : v)
310 public RelativePosition getConnectedEdge(RelativePosition edge) {
313 case RP_CONNECT_START5:
314 return RelativePosition.RP_CONNECT_END3;
315 case RP_CONNECT_END3:
316 return RelativePosition.RP_CONNECT_START5;
318 return RelativePosition.RP_OUTER;
322 public Point2D.Double getEdgePosition(RelativePosition edge)
328 case RP_CONNECT_START5:
330 case RP_CONNECT_END3:
332 case RP_EDIT_TANGENT_5:
333 return getControl5();
334 case RP_EDIT_TANGENT_3:
335 return getControl3();
341 double v2a(Point2D.Double p)
343 return (double)Math.atan2(p.y, p.x);
346 public void updateControl5(Point2D.Double p)
348 Point2D.Double p5 = getEdge5();
349 _e.setInTangentVectorLength(p5.distance(p));
350 Point2D.Double x = new Point2D.Double(p.x-p5.x,p.y-p5.y);
351 _e.setInTangentVectorAngle(v2a(x));
355 public void updateControl3(Point2D.Double p)
357 Point2D.Double p3 = getEdge3();
358 _e.setOutTangentVectorLength(p3.distance(p));
359 Point2D.Double x = new Point2D.Double(p.x-p3.x,p.y-p3.y);
360 _e.setOutTangentVectorAngle(v2a(x));
364 public void translate(double x, double y) {
365 _e.getVertex5().x += x;
366 _e.getVertex5().y += y;
367 _e.getVertex3().x += x;
368 _e.getVertex3().y += y;
372 public RelativePosition getRelativePosition(double x, double y) {
373 RelativePosition rp = getClosestEdge(x, y);
374 double d = getEdgePosition(rp).distance(new Point2D.Double(x,y));
375 if (d<MAX_UNPAIRED_CONTROL_DISTANCE)
377 if (getCurve().contains(new Point2D.Double(x,y)))
378 return RelativePosition.RP_INNER_GENERAL;
379 return RelativePosition.RP_OUTER;
382 public Shape getArea()
388 public void attach(GraphicalTemplateElement e, RelativePosition edgeOrig, RelativePosition edgeDest) throws ExceptionInvalidRNATemplate
390 super.attach(e,edgeOrig,edgeDest);
391 if (e instanceof Helix)
393 EdgeEndPoint e1 = this.getEndPoint(edgeOrig);
394 EdgeEndPoint e2 = e.getEndPoint(edgeDest);
395 boolean parity1 = this.isIn(edgeOrig);
396 boolean parity2 = e.isIn(edgeDest);
397 if ((e1!=null)&&(e2!=null)&&(parity1!=parity2))
407 public EdgeEndPoint getEndPoint(RelativePosition r) {
410 case RP_CONNECT_START5:
412 case RP_CONNECT_END3:
418 public boolean isIn(RelativePosition r) {
421 case RP_CONNECT_START5:
423 case RP_CONNECT_END3:
429 public void detach(RelativePosition edge)
431 // If the underlying template element is still connected, disconnect it
432 if (getEndPoint(edge).isConnected())
434 Couple<GraphicalTemplateElement.RelativePosition, GraphicalTemplateElement> c = getAttachedElement(edge);
435 getEndPoint(edge).disconnect();
438 // Call the parent class detach function, which will also take care to disconnect this other endpoint of this edge
442 public void setEdgePosition(RelativePosition edge, Point2D.Double pos) {
445 case RP_CONNECT_START5:
451 case RP_CONNECT_END3:
454 case RP_EDIT_TANGENT_5:
457 case RP_EDIT_TANGENT_3:
463 public ArrayList<RelativePosition> getConnectedEdges() {
464 ArrayList<RelativePosition> result = new ArrayList<RelativePosition>();
465 result.add(RelativePosition.RP_CONNECT_START5);
466 result.add(RelativePosition.RP_CONNECT_END3);
470 public RNATemplateElement getTemplateElement() {
475 public RelativePosition relativePositionFromEdgeEndPointPosition(
476 EdgeEndPointPosition pos) {
479 return RelativePosition.RP_CONNECT_START5;
481 return RelativePosition.RP_CONNECT_END3;