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