Wrap alignment with annotations
[jalview.git] / src / jalview / appletgui / RotatableCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 import java.awt.event.*;\r
26 \r
27 import jalview.datamodel.*;\r
28 import jalview.math.*;\r
29 import jalview.util.*;\r
30 \r
31 public class RotatableCanvas\r
32     extends Panel implements MouseListener,\r
33     MouseMotionListener,\r
34     KeyListener\r
35 {\r
36   RotatableMatrix idmat = new RotatableMatrix(3, 3);\r
37   RotatableMatrix objmat = new RotatableMatrix(3, 3);\r
38   RotatableMatrix rotmat = new RotatableMatrix(3, 3);\r
39 \r
40   //RubberbandRectangle rubberband;\r
41 \r
42   boolean drawAxes = true;\r
43 \r
44   int omx = 0;\r
45   int mx = 0;\r
46   int omy = 0;\r
47   int my = 0;\r
48 \r
49   Image img;\r
50   Graphics ig;\r
51 \r
52   Dimension prefsize;\r
53 \r
54   float centre[] = new float[3];\r
55   float width[] = new float[3];\r
56 \r
57   float max[] = new float[3];\r
58   float min[] = new float[3];\r
59 \r
60   float maxwidth;\r
61   float scale;\r
62 \r
63   int npoint;\r
64 \r
65   Vector points;\r
66   float[][] orig;\r
67   float[][] axes;\r
68 \r
69   int startx;\r
70   int starty;\r
71 \r
72   int lastx;\r
73   int lasty;\r
74 \r
75   int rectx1;\r
76   int recty1;\r
77   int rectx2;\r
78   int recty2;\r
79 \r
80   float scalefactor = 1;\r
81 \r
82   AlignViewport av;\r
83   boolean showLabels = false;\r
84 \r
85   public RotatableCanvas(AlignViewport av)\r
86   {\r
87     this.av = av;\r
88   }\r
89 \r
90   public void showLabels(boolean b)\r
91   {\r
92     showLabels = b;\r
93     repaint();\r
94   }\r
95 \r
96   public void setPoints(Vector points, int npoint)\r
97   {\r
98     this.points = points;\r
99     this.npoint = npoint;\r
100     this.av = av;\r
101     PaintRefresher.Register(this, av.alignment);\r
102 \r
103     prefsize = getPreferredSize();\r
104     orig = new float[npoint][3];\r
105 \r
106     for (int i = 0; i < npoint; i++)\r
107     {\r
108       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
109       for (int j = 0; j < 3; j++)\r
110       {\r
111         orig[i][j] = sp.coord[j];\r
112       }\r
113     }\r
114     //Initialize the matrices to identity\r
115 \r
116     for (int i = 0; i < 3; i++)\r
117     {\r
118       for (int j = 0; j < 3; j++)\r
119       {\r
120         if (i != j)\r
121         {\r
122           idmat.addElement(i, j, 0);\r
123           objmat.addElement(i, j, 0);\r
124           rotmat.addElement(i, j, 0);\r
125         }\r
126         else\r
127         {\r
128           idmat.addElement(i, j, 0);\r
129           objmat.addElement(i, j, 0);\r
130           rotmat.addElement(i, j, 0);\r
131         }\r
132       }\r
133     }\r
134 \r
135     axes = new float[3][3];\r
136     initAxes();\r
137 \r
138     findCentre();\r
139     findWidth();\r
140 \r
141     scale = findScale();\r
142 \r
143     //    System.out.println("Scale factor = " + scale);\r
144 \r
145     addMouseListener(this);\r
146     addKeyListener(this);\r
147     // if (getParent() != null) {\r
148     //   getParent().addKeyListener(this);\r
149     //}\r
150     addMouseMotionListener(this);\r
151 \r
152     // Add rubberband\r
153     //   rubberband  = new RubberbandRectangle(this);\r
154     //  rubberband.setActive(true);\r
155     //   rubberband.addListener(this);\r
156   }\r
157 \r
158   /* public boolean handleSequenceSelectionEvent(SequenceSelectionEvent evt) {\r
159      redrawneeded = true;\r
160      repaint();\r
161      return true;\r
162    }\r
163 \r
164    public void removeNotify() {\r
165      controller.removeListener(this);\r
166      super.removeNotify();\r
167    }*/\r
168 \r
169   public void initAxes()\r
170   {\r
171     for (int i = 0; i < 3; i++)\r
172     {\r
173       for (int j = 0; j < 3; j++)\r
174       {\r
175         if (i != j)\r
176         {\r
177           axes[i][j] = 0;\r
178         }\r
179         else\r
180         {\r
181           axes[i][j] = 1;\r
182         }\r
183       }\r
184     }\r
185   }\r
186 \r
187   public void findWidth()\r
188   {\r
189     max = new float[3];\r
190     min = new float[3];\r
191 \r
192     max[0] = (float) - 1e30;\r
193     max[1] = (float) - 1e30;\r
194     max[2] = (float) - 1e30;\r
195 \r
196     min[0] = (float) 1e30;\r
197     min[1] = (float) 1e30;\r
198     min[2] = (float) 1e30;\r
199 \r
200     for (int i = 0; i < 3; i++)\r
201     {\r
202       for (int j = 0; j < npoint; j++)\r
203       {\r
204         SequencePoint sp = (SequencePoint) points.elementAt(j);\r
205         if (sp.coord[i] >= max[i])\r
206         {\r
207           max[i] = sp.coord[i];\r
208         }\r
209         if (sp.coord[i] <= min[i])\r
210         {\r
211           min[i] = sp.coord[i];\r
212         }\r
213       }\r
214     }\r
215 \r
216     //    System.out.println("xmax " + max[0] + " min " + min[0]);\r
217     //System.out.println("ymax " + max[1] + " min " + min[1]);\r
218     //System.out.println("zmax " + max[2] + " min " + min[2]);\r
219 \r
220     width[0] = Math.abs(max[0] - min[0]);\r
221     width[1] = Math.abs(max[1] - min[1]);\r
222     width[2] = Math.abs(max[2] - min[2]);\r
223 \r
224     maxwidth = width[0];\r
225 \r
226     if (width[1] > width[0])\r
227     {\r
228       maxwidth = width[1];\r
229     }\r
230     if (width[2] > width[1])\r
231     {\r
232       maxwidth = width[2];\r
233     }\r
234 \r
235     //System.out.println("Maxwidth = " + maxwidth);\r
236   }\r
237 \r
238   public float findScale()\r
239   {\r
240     int dim, width, height;\r
241     if (getSize().width != 0)\r
242     {\r
243       width = getSize().width;\r
244       height = getSize().height;\r
245     }\r
246     else\r
247     {\r
248       width = prefsize.width;\r
249       height = prefsize.height;\r
250     }\r
251 \r
252     if (width < height)\r
253     {\r
254       dim = width;\r
255     }\r
256     else\r
257     {\r
258       dim = height;\r
259     }\r
260 \r
261     return (float) (dim * scalefactor / (2 * maxwidth));\r
262   }\r
263 \r
264   public void findCentre()\r
265   {\r
266     //Find centre coordinate\r
267     findWidth();\r
268 \r
269     centre[0] = (max[0] + min[0]) / 2;\r
270     centre[1] = (max[1] + min[1]) / 2;\r
271     centre[2] = (max[2] + min[2]) / 2;\r
272 \r
273     //    System.out.println("Centre x " + centre[0]);\r
274     //System.out.println("Centre y " + centre[1]);\r
275     //System.out.println("Centre z " + centre[2]);\r
276   }\r
277 \r
278   public Dimension getPreferredSize()\r
279   {\r
280     if (prefsize != null)\r
281     {\r
282       return prefsize;\r
283     }\r
284     else\r
285     {\r
286       return new Dimension(400, 400);\r
287     }\r
288   }\r
289 \r
290   public Dimension getMinimumSize()\r
291   {\r
292     return getPreferredSize();\r
293   }\r
294 \r
295   public void paint(Graphics g)\r
296   {\r
297     if (points == null)\r
298     {\r
299       g.setFont(new Font("Verdana", Font.PLAIN, 18));\r
300       g.drawString("Calculating PCA....", 20, getSize().height / 2);\r
301     }\r
302     else\r
303     {\r
304 \r
305       //Only create the image at the beginning -\r
306       if ( (img == null) || (prefsize.width != getSize().width) ||\r
307           (prefsize.height != getSize().height))\r
308       {\r
309         prefsize.width = getSize().width;\r
310         prefsize.height = getSize().height;\r
311 \r
312         scale = findScale();\r
313 \r
314         //      System.out.println("New scale = " + scale);\r
315         img = createImage(getSize().width, getSize().height);\r
316         ig = img.getGraphics();\r
317 \r
318       }\r
319 \r
320       drawBackground(ig, Color.black);\r
321       drawScene(ig);\r
322       if (drawAxes == true)\r
323       {\r
324         drawAxes(ig);\r
325       }\r
326 \r
327       g.drawImage(img, 0, 0, this);\r
328     }\r
329   }\r
330 \r
331   public void drawAxes(Graphics g)\r
332   {\r
333 \r
334     g.setColor(Color.yellow);\r
335     for (int i = 0; i < 3; i++)\r
336     {\r
337       g.drawLine(getSize().width / 2, getSize().height / 2,\r
338                  (int) (axes[i][0] * scale * max[0] + getSize().width / 2),\r
339                  (int) (axes[i][1] * scale * max[1] + getSize().height / 2));\r
340     }\r
341   }\r
342 \r
343   public void drawBackground(Graphics g, Color col)\r
344   {\r
345     g.setColor(col);\r
346     g.fillRect(0, 0, prefsize.width, prefsize.height);\r
347   }\r
348 \r
349   public void drawScene(Graphics g)\r
350   {\r
351     boolean darker = false;\r
352 \r
353     int halfwidth = getSize().width / 2;\r
354     int halfheight = getSize().height / 2;\r
355 \r
356     for (int i = 0; i < npoint; i++)\r
357     {\r
358       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
359       int x = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;\r
360       int y = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;\r
361       float z = sp.coord[1] - centre[2];\r
362 \r
363       if (sp.sequence.getColor() == Color.black)\r
364       {\r
365         g.setColor(Color.white);\r
366       }\r
367       else\r
368       {\r
369         g.setColor(sp.sequence.getColor());\r
370       }\r
371 \r
372       if (av.getSelectionGroup() != null)\r
373       {\r
374         if (av.getSelectionGroup().sequences.contains( ( (SequencePoint) points.\r
375             elementAt(i)).sequence))\r
376         {\r
377           g.setColor(Color.gray);\r
378         }\r
379       }\r
380       if (z < 0)\r
381       {\r
382         g.setColor(g.getColor().darker());\r
383       }\r
384 \r
385       g.fillRect(x - 3, y - 3, 6, 6);\r
386       if (showLabels)\r
387       {\r
388         g.setColor(Color.red);\r
389         g.drawString( ( (SequencePoint) points.elementAt(i)).sequence.\r
390                      getName(),\r
391                      x - 3, y - 4);\r
392       }\r
393 \r
394     }\r
395 //    //Now the rectangle\r
396 //    if (rectx2 != -1 && recty2 != -1) {\r
397 //      g.setColor(Color.white);\r
398 //\r
399 //      g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1);\r
400 //    }\r
401   }\r
402 \r
403   public Dimension minimumsize()\r
404   {\r
405     return prefsize;\r
406   }\r
407 \r
408   public Dimension preferredsize()\r
409   {\r
410     return prefsize;\r
411   }\r
412 \r
413   public void keyTyped(KeyEvent evt)\r
414   {}\r
415 \r
416   public void keyReleased(KeyEvent evt)\r
417   {}\r
418 \r
419   public void keyPressed(KeyEvent evt)\r
420   {\r
421     requestFocus();\r
422     if (evt.getKeyCode() == KeyEvent.VK_UP)\r
423     {\r
424       scalefactor = (float) (scalefactor * 1.1);\r
425       scale = findScale();\r
426     }\r
427     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)\r
428     {\r
429       scalefactor = (float) (scalefactor * 0.9);\r
430       scale = findScale();\r
431     }\r
432     else if (evt.getKeyChar() == 's')\r
433     {\r
434       System.err.println("DEBUG: Rectangle selection"); // log.debug\r
435       if (rectx2 != -1 && recty2 != -1)\r
436       {\r
437         rectSelect(rectx1, recty1, rectx2, recty2);\r
438 \r
439       }\r
440     }\r
441     repaint();\r
442   }\r
443 \r
444   public void printPoints()\r
445   {\r
446     for (int i = 0; i < npoint; i++)\r
447     {\r
448       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
449       Format.print(System.out, "%5d ", i);\r
450       for (int j = 0; j < 3; j++)\r
451       {\r
452         Format.print(System.out, "%13.3f  ", sp.coord[j]);\r
453       }\r
454       System.out.println();\r
455     }\r
456   }\r
457 \r
458   public void mouseClicked(MouseEvent evt)\r
459   {}\r
460 \r
461   public void mouseEntered(MouseEvent evt)\r
462   {}\r
463 \r
464   public void mouseExited(MouseEvent evt)\r
465   {}\r
466 \r
467   public void mouseReleased(MouseEvent evt)\r
468   {}\r
469 \r
470   public void mousePressed(MouseEvent evt)\r
471   {\r
472     int x = evt.getX();\r
473     int y = evt.getY();\r
474 \r
475     mx = x;\r
476     my = y;\r
477 \r
478     omx = mx;\r
479     omy = my;\r
480 \r
481     startx = x;\r
482     starty = y;\r
483 \r
484     rectx1 = x;\r
485     recty1 = y;\r
486 \r
487     rectx2 = -1;\r
488     recty2 = -1;\r
489 \r
490     SequenceI found = findPoint(x, y);\r
491 \r
492     if (found != null)\r
493     {\r
494       if (av.getSelectionGroup() != null)\r
495       {\r
496         av.getSelectionGroup().addOrRemove(found, true);\r
497         av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);\r
498         PaintRefresher.Refresh(this, av.alignment);\r
499       }\r
500       else\r
501       {\r
502         av.setSelectionGroup(new SequenceGroup());\r
503         av.getSelectionGroup().addOrRemove(found, true);\r
504         av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);\r
505 \r
506       }\r
507     }\r
508     repaint();\r
509   }\r
510 \r
511   // private void fireSequenceSelectionEvent(Selection sel) {\r
512   //   controller.handleSequenceSelectionEvent(new SequenceSelectionEvent(this,sel));\r
513   //}\r
514 \r
515   public void mouseMoved(MouseEvent evt)\r
516   {\r
517     // SequenceI found = findPoint(evt.getX(), evt.getY());\r
518   }\r
519 \r
520   public void mouseDragged(MouseEvent evt)\r
521   {\r
522     mx = evt.getX();\r
523     my = evt.getY();\r
524     //Check if this is a rectangle drawing drag\r
525     if ( (evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)\r
526     {\r
527 //      rectx2 = evt.getX();\r
528 //      recty2 = evt.getY();\r
529     }\r
530     else\r
531     {\r
532       rotmat.setIdentity();\r
533 \r
534       rotmat.rotate( (float) (my - omy), 'x');\r
535       rotmat.rotate( (float) (mx - omx), 'y');\r
536 \r
537       for (int i = 0; i < npoint; i++)\r
538       {\r
539         SequencePoint sp = (SequencePoint) points.elementAt(i);\r
540         sp.coord[0] -= centre[0];\r
541         sp.coord[1] -= centre[1];\r
542         sp.coord[2] -= centre[2];\r
543 \r
544         //Now apply the rotation matrix\r
545         sp.coord = rotmat.vectorMultiply(sp.coord);\r
546 \r
547         //Now translate back again\r
548         sp.coord[0] += centre[0];\r
549         sp.coord[1] += centre[1];\r
550         sp.coord[2] += centre[2];\r
551       }\r
552 \r
553       for (int i = 0; i < 3; i++)\r
554       {\r
555         axes[i] = rotmat.vectorMultiply(axes[i]);\r
556       }\r
557       omx = mx;\r
558       omy = my;\r
559 \r
560       paint(this.getGraphics());\r
561     }\r
562 \r
563   }\r
564 \r
565   public void rectSelect(int x1, int y1, int x2, int y2)\r
566   {\r
567     boolean changedSel = false;\r
568     for (int i = 0; i < npoint; i++)\r
569     {\r
570       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
571       int tmp1 = (int) ( (sp.coord[0] - centre[0]) * scale +\r
572                         (float) getSize().width / 2.0);\r
573       int tmp2 = (int) ( (sp.coord[1] - centre[1]) * scale +\r
574                         (float) getSize().height / 2.0);\r
575 \r
576       if (tmp1 > x1 && tmp1 < x2 && tmp2 > y1 && tmp2 < y2)\r
577       {\r
578         if (av != null)\r
579         {\r
580           if (!av.getSelectionGroup().sequences.contains(sp.sequence))\r
581           {\r
582             av.getSelectionGroup().addSequence(sp.sequence, true);\r
583           }\r
584         }\r
585       }\r
586     }\r
587   }\r
588 \r
589   public SequenceI findPoint(int x, int y)\r
590   {\r
591 \r
592     int halfwidth = getSize().width / 2;\r
593     int halfheight = getSize().height / 2;\r
594 \r
595     int found = -1;\r
596 \r
597     for (int i = 0; i < npoint; i++)\r
598     {\r
599 \r
600       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
601       int px = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;\r
602       int py = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;\r
603 \r
604       if (Math.abs(px - x) < 3 && Math.abs(py - y) < 3)\r
605       {\r
606         found = i;\r
607       }\r
608     }\r
609     if (found != -1)\r
610     {\r
611       return ( (SequencePoint) points.elementAt(found)).sequence;\r
612     }\r
613     else\r
614     {\r
615       return null;\r
616     }\r
617   }\r
618   /*  public boolean handleRubberbandEvent(RubberbandEvent evt) {\r
619       System.out.println("Rubberband handler called in RotatableCanvas with " +\r
620                          evt.getBounds());\r
621 \r
622       Rubberband rb = (Rubberband)evt.getSource();\r
623 \r
624       // Clear the current selection (instance variable)\r
625       //if ((rb.getModifiers() & Event.SHIFT_MASK) == 0) {\r
626       //   clearSelection();\r
627       //}\r
628 \r
629       if (rb.getComponent() == this) {\r
630         Rectangle bounds = evt.getBounds();\r
631    rectSelect(bounds.x,bounds.y,bounds.x+bounds.width,bounds.y+bounds.height);\r
632       }\r
633 \r
634       redrawneeded = true;\r
635       paint(this.getGraphics());\r
636 \r
637       return true;\r
638     }*/\r
639 \r
640 }\r