header updated
[jalview.git] / src / jalview / gui / RotatableCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2006 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 package jalview.gui;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import jalview.math.*;\r
24 \r
25 import java.awt.*;\r
26 import java.awt.event.*;\r
27 \r
28 import java.util.*;\r
29 \r
30 import javax.swing.*;\r
31 \r
32 \r
33 /**\r
34  * DOCUMENT ME!\r
35  *\r
36  * @author $author$\r
37  * @version $Revision$\r
38  */\r
39 public class RotatableCanvas extends JPanel implements MouseListener,\r
40     MouseMotionListener, KeyListener\r
41 {\r
42     RotatableMatrix idmat = new RotatableMatrix(3, 3);\r
43     RotatableMatrix objmat = new RotatableMatrix(3, 3);\r
44     RotatableMatrix rotmat = new RotatableMatrix(3, 3);\r
45 \r
46     //RubberbandRectangle rubberband;\r
47     boolean drawAxes = true;\r
48     int omx = 0;\r
49     int mx = 0;\r
50     int omy = 0;\r
51     int my = 0;\r
52     Image img;\r
53     Graphics ig;\r
54     Dimension prefsize;\r
55     float[] centre = new float[3];\r
56     float[] width = new float[3];\r
57     float[] max = new float[3];\r
58     float[] min = new float[3];\r
59     float maxwidth;\r
60     float scale;\r
61     int npoint;\r
62     Vector points;\r
63     float[][] orig;\r
64     float[][] axes;\r
65     int startx;\r
66     int starty;\r
67     int lastx;\r
68     int lasty;\r
69     int rectx1;\r
70     int recty1;\r
71     int rectx2;\r
72     int recty2;\r
73     float scalefactor = 1;\r
74     AlignViewport av;\r
75     boolean showLabels = false;\r
76     Color bgColour = Color.black;\r
77 \r
78     //  Controller    controller;\r
79     public RotatableCanvas(AlignViewport av)\r
80     {\r
81       this.av = av;\r
82     }\r
83 \r
84     public void showLabels(boolean b)\r
85     {\r
86       showLabels = b;\r
87       repaint();\r
88     }\r
89 \r
90     public void setPoints(Vector points, int npoint)\r
91     {\r
92         this.points = points;\r
93         this.npoint = npoint;\r
94         ToolTipManager.sharedInstance().registerComponent(this);\r
95         ToolTipManager.sharedInstance().setInitialDelay(0);\r
96         ToolTipManager.sharedInstance().setDismissDelay(10000);\r
97         PaintRefresher.Register(this, av.alignment);\r
98 \r
99         prefsize = getPreferredSize();\r
100         orig = new float[npoint][3];\r
101 \r
102         for (int i = 0; i < npoint; i++)\r
103         {\r
104             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
105 \r
106             for (int j = 0; j < 3; j++)\r
107             {\r
108                 orig[i][j] = sp.coord[j];\r
109             }\r
110         }\r
111 \r
112         //Initialize the matrices to identity\r
113         for (int i = 0; i < 3; i++)\r
114         {\r
115             for (int j = 0; j < 3; j++)\r
116             {\r
117                 if (i != j)\r
118                 {\r
119                     idmat.addElement(i, j, 0);\r
120                     objmat.addElement(i, j, 0);\r
121                     rotmat.addElement(i, j, 0);\r
122                 }\r
123                 else\r
124                 {\r
125                     idmat.addElement(i, j, 0);\r
126                     objmat.addElement(i, j, 0);\r
127                     rotmat.addElement(i, j, 0);\r
128                 }\r
129             }\r
130         }\r
131 \r
132         axes = new float[3][3];\r
133         initAxes();\r
134 \r
135         findCentre();\r
136         findWidth();\r
137 \r
138         scale = findScale();\r
139 \r
140         addMouseListener(this);\r
141         addKeyListener(this);\r
142 \r
143         addMouseMotionListener(this);\r
144 \r
145     }\r
146 \r
147     public void initAxes()\r
148     {\r
149         for (int i = 0; i < 3; i++)\r
150         {\r
151             for (int j = 0; j < 3; j++)\r
152             {\r
153                 if (i != j)\r
154                 {\r
155                     axes[i][j] = 0;\r
156                 }\r
157                 else\r
158                 {\r
159                     axes[i][j] = 1;\r
160                 }\r
161             }\r
162         }\r
163     }\r
164 \r
165     /**\r
166      * DOCUMENT ME!\r
167      */\r
168     public void findWidth()\r
169     {\r
170         max = new float[3];\r
171         min = new float[3];\r
172 \r
173         max[0] = (float) -1e30;\r
174         max[1] = (float) -1e30;\r
175         max[2] = (float) -1e30;\r
176 \r
177         min[0] = (float) 1e30;\r
178         min[1] = (float) 1e30;\r
179         min[2] = (float) 1e30;\r
180 \r
181         for (int i = 0; i < 3; i++)\r
182         {\r
183             for (int j = 0; j < npoint; j++)\r
184             {\r
185                 SequencePoint sp = (SequencePoint) points.elementAt(j);\r
186 \r
187                 if (sp.coord[i] >= max[i])\r
188                 {\r
189                     max[i] = sp.coord[i];\r
190                 }\r
191 \r
192                 if (sp.coord[i] <= min[i])\r
193                 {\r
194                     min[i] = sp.coord[i];\r
195                 }\r
196             }\r
197         }\r
198 \r
199         //    System.out.println("xmax " + max[0] + " min " + min[0]);\r
200         //System.out.println("ymax " + max[1] + " min " + min[1]);\r
201         //System.out.println("zmax " + max[2] + " min " + min[2]);\r
202         width[0] = Math.abs(max[0] - min[0]);\r
203         width[1] = Math.abs(max[1] - min[1]);\r
204         width[2] = Math.abs(max[2] - min[2]);\r
205 \r
206         maxwidth = width[0];\r
207 \r
208         if (width[1] > width[0])\r
209         {\r
210             maxwidth = width[1];\r
211         }\r
212 \r
213         if (width[2] > width[1])\r
214         {\r
215             maxwidth = width[2];\r
216         }\r
217 \r
218         //System.out.println("Maxwidth = " + maxwidth);\r
219     }\r
220 \r
221     /**\r
222      * DOCUMENT ME!\r
223      *\r
224      * @return DOCUMENT ME!\r
225      */\r
226     public float findScale()\r
227     {\r
228         int dim;\r
229         int width;\r
230         int height;\r
231 \r
232         if (getWidth() != 0)\r
233         {\r
234             width = getWidth();\r
235             height = getHeight();\r
236         }\r
237         else\r
238         {\r
239             width = prefsize.width;\r
240             height = prefsize.height;\r
241         }\r
242 \r
243         if (width < height)\r
244         {\r
245             dim = width;\r
246         }\r
247         else\r
248         {\r
249             dim = height;\r
250         }\r
251 \r
252         return (float) ((dim * scalefactor) / (2 * maxwidth));\r
253     }\r
254 \r
255     /**\r
256      * DOCUMENT ME!\r
257      */\r
258     public void findCentre()\r
259     {\r
260         //Find centre coordinate\r
261         findWidth();\r
262 \r
263         centre[0] = (max[0] + min[0]) / 2;\r
264         centre[1] = (max[1] + min[1]) / 2;\r
265         centre[2] = (max[2] + min[2]) / 2;\r
266 \r
267         //    System.out.println("Centre x " + centre[0]);\r
268         //System.out.println("Centre y " + centre[1]);\r
269         //System.out.println("Centre z " + centre[2]);\r
270     }\r
271 \r
272     /**\r
273      * DOCUMENT ME!\r
274      *\r
275      * @return DOCUMENT ME!\r
276      */\r
277     public Dimension getPreferredSize()\r
278     {\r
279         if (prefsize != null)\r
280         {\r
281             return prefsize;\r
282         }\r
283         else\r
284         {\r
285             return new Dimension(400, 400);\r
286         }\r
287     }\r
288 \r
289     /**\r
290      * DOCUMENT ME!\r
291      *\r
292      * @return DOCUMENT ME!\r
293      */\r
294     public Dimension getMinimumSize()\r
295     {\r
296         return getPreferredSize();\r
297     }\r
298 \r
299     /**\r
300      * DOCUMENT ME!\r
301      *\r
302      * @param g DOCUMENT ME!\r
303      */\r
304     public void paintComponent(Graphics g1)\r
305     {\r
306 \r
307       Graphics2D g = (Graphics2D) g1;\r
308 \r
309       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
310                          RenderingHints.VALUE_ANTIALIAS_ON);\r
311       if(points==null)\r
312       {\r
313         g.setFont(new Font("Verdana", Font.PLAIN, 18));\r
314         g.drawString("Calculating PCA....", 20, getHeight()/2);\r
315       }\r
316       else\r
317       {\r
318         //Only create the image at the beginning -\r
319         if ( (img == null) || (prefsize.width != getWidth()) ||\r
320             (prefsize.height != getHeight()))\r
321         {\r
322           prefsize.width = getWidth();\r
323           prefsize.height = getHeight();\r
324 \r
325           scale = findScale();\r
326 \r
327           //      System.out.println("New scale = " + scale);\r
328           img = createImage(getWidth(), getHeight());\r
329           ig = img.getGraphics();\r
330         }\r
331 \r
332 \r
333         drawBackground(ig, bgColour);\r
334         drawScene(ig);\r
335 \r
336         if (drawAxes == true)\r
337         {\r
338           drawAxes(ig);\r
339         }\r
340 \r
341         g.drawImage(img, 0, 0, this);\r
342       }\r
343     }\r
344 \r
345     /**\r
346      * DOCUMENT ME!\r
347      *\r
348      * @param g DOCUMENT ME!\r
349      */\r
350     public void drawAxes(Graphics g)\r
351     {\r
352 \r
353         g.setColor(Color.yellow);\r
354 \r
355         for (int i = 0; i < 3; i++)\r
356         {\r
357             g.drawLine(getWidth() / 2, getHeight() / 2,\r
358                 (int) ((axes[i][0] * scale * max[0]) + (getWidth() / 2)),\r
359                 (int) ((axes[i][1] * scale * max[1]) + (getHeight() / 2)));\r
360         }\r
361     }\r
362 \r
363     /**\r
364      * DOCUMENT ME!\r
365      *\r
366      * @param g DOCUMENT ME!\r
367      * @param col DOCUMENT ME!\r
368      */\r
369     public void drawBackground(Graphics g, Color col)\r
370     {\r
371         g.setColor(col);\r
372         g.fillRect(0, 0, prefsize.width, prefsize.height);\r
373     }\r
374 \r
375     /**\r
376      * DOCUMENT ME!\r
377      *\r
378      * @param g DOCUMENT ME!\r
379      */\r
380     public void drawScene(Graphics g1)\r
381     {\r
382 \r
383       Graphics2D g = (Graphics2D) g1;\r
384 \r
385       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
386                          RenderingHints.VALUE_ANTIALIAS_ON);\r
387 \r
388 \r
389         int halfwidth = getWidth() / 2;\r
390         int halfheight = getHeight() / 2;\r
391 \r
392         for (int i = 0; i < npoint; i++)\r
393         {\r
394             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
395             int x = (int) ((float) (sp.coord[0] - centre[0]) * scale) +\r
396                 halfwidth;\r
397             int y = (int) ((float) (sp.coord[1] - centre[1]) * scale) +\r
398                 halfheight;\r
399             float z = sp.coord[1] - centre[2];\r
400 \r
401             if (sp.sequence.getColor() == Color.black)\r
402             {\r
403                 g.setColor(Color.white);\r
404             }\r
405             else\r
406             {\r
407                 g.setColor(sp.sequence.getColor());\r
408             }\r
409 \r
410             if (av.getSelectionGroup() != null)\r
411             {\r
412                 if (av.getSelectionGroup().getSequences(false).contains(\r
413                             ((SequencePoint) points.elementAt(i)).sequence))\r
414                 {\r
415                     g.setColor(Color.gray);\r
416                 }\r
417             }\r
418 \r
419             if (z < 0)\r
420             {\r
421                 g.setColor(g.getColor().darker());\r
422             }\r
423 \r
424             g.fillRect(x - 3, y - 3, 6, 6);\r
425             if(showLabels)\r
426             {\r
427               g.setColor(Color.red);\r
428               g.drawString( ( (SequencePoint) points.elementAt(i)).sequence.\r
429                            getName(),\r
430                            x - 3, y - 4);\r
431             }\r
432         }\r
433 \r
434         //    //Now the rectangle\r
435         //    if (rectx2 != -1 && recty2 != -1) {\r
436         //      g.setColor(Color.white);\r
437         //\r
438         //      g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1);\r
439         //    }\r
440     }\r
441 \r
442     /**\r
443      * DOCUMENT ME!\r
444      *\r
445      * @return DOCUMENT ME!\r
446      */\r
447     public Dimension minimumsize()\r
448     {\r
449         return prefsize;\r
450     }\r
451 \r
452     /**\r
453      * DOCUMENT ME!\r
454      *\r
455      * @return DOCUMENT ME!\r
456      */\r
457     public Dimension preferredsize()\r
458     {\r
459         return prefsize;\r
460     }\r
461 \r
462     /**\r
463      * DOCUMENT ME!\r
464      *\r
465      * @param evt DOCUMENT ME!\r
466      */\r
467     public void keyTyped(KeyEvent evt)\r
468     {\r
469     }\r
470 \r
471     /**\r
472      * DOCUMENT ME!\r
473      *\r
474      * @param evt DOCUMENT ME!\r
475      */\r
476     public void keyReleased(KeyEvent evt)\r
477     {\r
478     }\r
479 \r
480     /**\r
481      * DOCUMENT ME!\r
482      *\r
483      * @param evt DOCUMENT ME!\r
484      */\r
485     public void keyPressed(KeyEvent evt)\r
486     {\r
487         if (evt.getKeyCode() == KeyEvent.VK_UP)\r
488         {\r
489             scalefactor = (float) (scalefactor * 1.1);\r
490             scale = findScale();\r
491         }\r
492         else if (evt.getKeyCode() == KeyEvent.VK_DOWN)\r
493         {\r
494             scalefactor = (float) (scalefactor * 0.9);\r
495             scale = findScale();\r
496         }\r
497         else if (evt.getKeyChar() == 's')\r
498         {\r
499             System.err.println("DEBUG: Rectangle selection"); // log.debug\r
500 \r
501             if ((rectx2 != -1) && (recty2 != -1))\r
502             {\r
503                 rectSelect(rectx1, recty1, rectx2, recty2);\r
504             }\r
505         }\r
506 \r
507         repaint();\r
508     }\r
509 \r
510 \r
511     /**\r
512      * DOCUMENT ME!\r
513      *\r
514      * @param evt DOCUMENT ME!\r
515      */\r
516     public void mouseClicked(MouseEvent evt)\r
517     {\r
518     }\r
519 \r
520     /**\r
521      * DOCUMENT ME!\r
522      *\r
523      * @param evt DOCUMENT ME!\r
524      */\r
525     public void mouseEntered(MouseEvent evt)\r
526     {\r
527     }\r
528 \r
529     /**\r
530      * DOCUMENT ME!\r
531      *\r
532      * @param evt DOCUMENT ME!\r
533      */\r
534     public void mouseExited(MouseEvent evt)\r
535     {\r
536     }\r
537 \r
538     /**\r
539      * DOCUMENT ME!\r
540      *\r
541      * @param evt DOCUMENT ME!\r
542      */\r
543     public void mouseReleased(MouseEvent evt)\r
544     {\r
545     }\r
546 \r
547     /**\r
548      * DOCUMENT ME!\r
549      *\r
550      * @param evt DOCUMENT ME!\r
551      */\r
552     public void mousePressed(MouseEvent evt)\r
553     {\r
554         int x = evt.getX();\r
555         int y = evt.getY();\r
556 \r
557         mx = x;\r
558         my = y;\r
559 \r
560         omx = mx;\r
561         omy = my;\r
562 \r
563         startx = x;\r
564         starty = y;\r
565 \r
566         rectx1 = x;\r
567         recty1 = y;\r
568 \r
569         rectx2 = -1;\r
570         recty2 = -1;\r
571 \r
572         SequenceI found = findPoint(x, y);\r
573 \r
574         if (found != null)\r
575         {\r
576             if (av.getSelectionGroup() != null)\r
577             {\r
578                 av.getSelectionGroup().addOrRemove(found, true);\r
579                 PaintRefresher.Refresh(this, av.alignment);\r
580             }\r
581             else\r
582             {\r
583                 av.setSelectionGroup(new SequenceGroup());\r
584                 av.getSelectionGroup().addOrRemove(found, true);\r
585                 av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);\r
586             }\r
587         }\r
588 \r
589         repaint();\r
590     }\r
591 \r
592     // private void fireSequenceSelectionEvent(Selection sel) {\r
593     //   controller.handleSequenceSelectionEvent(new SequenceSelectionEvent(this,sel));\r
594     //}\r
595     public void mouseMoved(MouseEvent evt)\r
596     {\r
597         SequenceI found = findPoint(evt.getX(), evt.getY());\r
598 \r
599         if (found != null)\r
600         {\r
601             this.setToolTipText(found.getName());\r
602         }\r
603         else\r
604         {\r
605             this.setToolTipText(null);\r
606         }\r
607     }\r
608 \r
609     /**\r
610      * DOCUMENT ME!\r
611      *\r
612      * @param evt DOCUMENT ME!\r
613      */\r
614     public void mouseDragged(MouseEvent evt)\r
615     {\r
616         mx = evt.getX();\r
617         my = evt.getY();\r
618 \r
619         //Check if this is a rectangle drawing drag\r
620         if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)\r
621         {\r
622             //      rectx2 = evt.getX();\r
623             //      recty2 = evt.getY();\r
624         }\r
625         else\r
626         {\r
627             rotmat.setIdentity();\r
628 \r
629             rotmat.rotate((float) (my - omy), 'x');\r
630             rotmat.rotate((float) (mx - omx), 'y');\r
631 \r
632             for (int i = 0; i < npoint; i++)\r
633             {\r
634                 SequencePoint sp = (SequencePoint) points.elementAt(i);\r
635                 sp.coord[0] -= centre[0];\r
636                 sp.coord[1] -= centre[1];\r
637                 sp.coord[2] -= centre[2];\r
638 \r
639                 //Now apply the rotation matrix\r
640                 sp.coord = rotmat.vectorMultiply(sp.coord);\r
641 \r
642                 //Now translate back again\r
643                 sp.coord[0] += centre[0];\r
644                 sp.coord[1] += centre[1];\r
645                 sp.coord[2] += centre[2];\r
646             }\r
647 \r
648             for (int i = 0; i < 3; i++)\r
649             {\r
650                 axes[i] = rotmat.vectorMultiply(axes[i]);\r
651             }\r
652 \r
653             omx = mx;\r
654             omy = my;\r
655 \r
656             paint(this.getGraphics());\r
657         }\r
658     }\r
659 \r
660     /**\r
661      * DOCUMENT ME!\r
662      *\r
663      * @param x1 DOCUMENT ME!\r
664      * @param y1 DOCUMENT ME!\r
665      * @param x2 DOCUMENT ME!\r
666      * @param y2 DOCUMENT ME!\r
667      */\r
668     public void rectSelect(int x1, int y1, int x2, int y2)\r
669     {\r
670         for (int i = 0; i < npoint; i++)\r
671         {\r
672             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
673             int tmp1 = (int) (((sp.coord[0] - centre[0]) * scale) +\r
674                 ((float) getWidth() / 2.0));\r
675             int tmp2 = (int) (((sp.coord[1] - centre[1]) * scale) +\r
676                 ((float) getHeight() / 2.0));\r
677 \r
678             if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))\r
679             {\r
680                 if (av != null)\r
681                 {\r
682                     if (!av.getSelectionGroup().getSequences(false).contains(sp.sequence))\r
683                     {\r
684                         av.getSelectionGroup().addSequence(sp.sequence, true);\r
685                     }\r
686                 }\r
687             }\r
688         }\r
689 \r
690         // if (changedSel) {\r
691         //    fireSequenceSelectionEvent(av.getSelection());\r
692         // }\r
693     }\r
694 \r
695     /**\r
696      * DOCUMENT ME!\r
697      *\r
698      * @param x DOCUMENT ME!\r
699      * @param y DOCUMENT ME!\r
700      *\r
701      * @return DOCUMENT ME!\r
702      */\r
703     public SequenceI findPoint(int x, int y)\r
704     {\r
705         int halfwidth = getWidth() / 2;\r
706         int halfheight = getHeight() / 2;\r
707 \r
708         int found = -1;\r
709 \r
710         for (int i = 0; i < npoint; i++)\r
711         {\r
712             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
713             int px = (int) ((float) (sp.coord[0] - centre[0]) * scale) +\r
714                 halfwidth;\r
715             int py = (int) ((float) (sp.coord[1] - centre[1]) * scale) +\r
716                 halfheight;\r
717 \r
718             if ((Math.abs(px - x) < 3) && (Math.abs(py - y) < 3))\r
719             {\r
720                 found = i;\r
721             }\r
722         }\r
723 \r
724         if (found != -1)\r
725         {\r
726             return ((SequencePoint) points.elementAt(found)).sequence;\r
727         }\r
728         else\r
729         {\r
730             return null;\r
731         }\r
732     }\r
733 \r
734     /*  public boolean handleRubberbandEvent(RubberbandEvent evt) {\r
735         System.out.println("Rubberband handler called in RotatableCanvas with " +\r
736                            evt.getBounds());\r
737 \r
738         Rubberband rb = (Rubberband)evt.getSource();\r
739 \r
740         // Clear the current selection (instance variable)\r
741         //if ((rb.getModifiers() & Event.SHIFT_MASK) == 0) {\r
742         //   clearSelection();\r
743         //}\r
744 \r
745         if (rb.getComponent() == this) {\r
746           Rectangle bounds = evt.getBounds();\r
747      rectSelect(bounds.x,bounds.y,bounds.x+bounds.width,bounds.y+bounds.height);\r
748         }\r
749 \r
750         redrawneeded = true;\r
751         paint(this.getGraphics());\r
752 \r
753         return true;\r
754       }*/\r
755 }\r