Colour text
[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.getSequenceSetId());\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 \r
142         addMouseMotionListener(this);\r
143 \r
144     }\r
145 \r
146     public void initAxes()\r
147     {\r
148         for (int i = 0; i < 3; i++)\r
149         {\r
150             for (int j = 0; j < 3; j++)\r
151             {\r
152                 if (i != j)\r
153                 {\r
154                     axes[i][j] = 0;\r
155                 }\r
156                 else\r
157                 {\r
158                     axes[i][j] = 1;\r
159                 }\r
160             }\r
161         }\r
162     }\r
163 \r
164     /**\r
165      * DOCUMENT ME!\r
166      */\r
167     public void findWidth()\r
168     {\r
169         max = new float[3];\r
170         min = new float[3];\r
171 \r
172         max[0] = (float) -1e30;\r
173         max[1] = (float) -1e30;\r
174         max[2] = (float) -1e30;\r
175 \r
176         min[0] = (float) 1e30;\r
177         min[1] = (float) 1e30;\r
178         min[2] = (float) 1e30;\r
179 \r
180         for (int i = 0; i < 3; i++)\r
181         {\r
182             for (int j = 0; j < npoint; j++)\r
183             {\r
184                 SequencePoint sp = (SequencePoint) points.elementAt(j);\r
185 \r
186                 if (sp.coord[i] >= max[i])\r
187                 {\r
188                     max[i] = sp.coord[i];\r
189                 }\r
190 \r
191                 if (sp.coord[i] <= min[i])\r
192                 {\r
193                     min[i] = sp.coord[i];\r
194                 }\r
195             }\r
196         }\r
197 \r
198         //    System.out.println("xmax " + max[0] + " min " + min[0]);\r
199         //System.out.println("ymax " + max[1] + " min " + min[1]);\r
200         //System.out.println("zmax " + max[2] + " min " + min[2]);\r
201         width[0] = Math.abs(max[0] - min[0]);\r
202         width[1] = Math.abs(max[1] - min[1]);\r
203         width[2] = Math.abs(max[2] - min[2]);\r
204 \r
205         maxwidth = width[0];\r
206 \r
207         if (width[1] > width[0])\r
208         {\r
209             maxwidth = width[1];\r
210         }\r
211 \r
212         if (width[2] > width[1])\r
213         {\r
214             maxwidth = width[2];\r
215         }\r
216 \r
217         //System.out.println("Maxwidth = " + maxwidth);\r
218     }\r
219 \r
220     /**\r
221      * DOCUMENT ME!\r
222      *\r
223      * @return DOCUMENT ME!\r
224      */\r
225     public float findScale()\r
226     {\r
227         int dim;\r
228         int width;\r
229         int height;\r
230 \r
231         if (getWidth() != 0)\r
232         {\r
233             width = getWidth();\r
234             height = getHeight();\r
235         }\r
236         else\r
237         {\r
238             width = prefsize.width;\r
239             height = prefsize.height;\r
240         }\r
241 \r
242         if (width < height)\r
243         {\r
244             dim = width;\r
245         }\r
246         else\r
247         {\r
248             dim = height;\r
249         }\r
250 \r
251         return (float) ((dim * scalefactor) / (2 * maxwidth));\r
252     }\r
253 \r
254     /**\r
255      * DOCUMENT ME!\r
256      */\r
257     public void findCentre()\r
258     {\r
259         //Find centre coordinate\r
260         findWidth();\r
261 \r
262         centre[0] = (max[0] + min[0]) / 2;\r
263         centre[1] = (max[1] + min[1]) / 2;\r
264         centre[2] = (max[2] + min[2]) / 2;\r
265 \r
266         //    System.out.println("Centre x " + centre[0]);\r
267         //System.out.println("Centre y " + centre[1]);\r
268         //System.out.println("Centre z " + centre[2]);\r
269     }\r
270 \r
271     /**\r
272      * DOCUMENT ME!\r
273      *\r
274      * @return DOCUMENT ME!\r
275      */\r
276     public Dimension getPreferredSize()\r
277     {\r
278         if (prefsize != null)\r
279         {\r
280             return prefsize;\r
281         }\r
282         else\r
283         {\r
284             return new Dimension(400, 400);\r
285         }\r
286     }\r
287 \r
288     /**\r
289      * DOCUMENT ME!\r
290      *\r
291      * @return DOCUMENT ME!\r
292      */\r
293     public Dimension getMinimumSize()\r
294     {\r
295         return getPreferredSize();\r
296     }\r
297 \r
298     /**\r
299      * DOCUMENT ME!\r
300      *\r
301      * @param g DOCUMENT ME!\r
302      */\r
303     public void paintComponent(Graphics g1)\r
304     {\r
305 \r
306       Graphics2D g = (Graphics2D) g1;\r
307 \r
308       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
309                          RenderingHints.VALUE_ANTIALIAS_ON);\r
310       if(points==null)\r
311       {\r
312         g.setFont(new Font("Verdana", Font.PLAIN, 18));\r
313         g.drawString("Calculating PCA....", 20, getHeight()/2);\r
314       }\r
315       else\r
316       {\r
317         //Only create the image at the beginning -\r
318         if ( (img == null) || (prefsize.width != getWidth()) ||\r
319             (prefsize.height != getHeight()))\r
320         {\r
321           prefsize.width = getWidth();\r
322           prefsize.height = getHeight();\r
323 \r
324           scale = findScale();\r
325 \r
326           //      System.out.println("New scale = " + scale);\r
327           img = createImage(getWidth(), getHeight());\r
328           ig = img.getGraphics();\r
329         }\r
330 \r
331 \r
332         drawBackground(ig, bgColour);\r
333         drawScene(ig);\r
334 \r
335         if (drawAxes == true)\r
336         {\r
337           drawAxes(ig);\r
338         }\r
339 \r
340         g.drawImage(img, 0, 0, this);\r
341       }\r
342     }\r
343 \r
344     /**\r
345      * DOCUMENT ME!\r
346      *\r
347      * @param g DOCUMENT ME!\r
348      */\r
349     public void drawAxes(Graphics g)\r
350     {\r
351 \r
352         g.setColor(Color.yellow);\r
353 \r
354         for (int i = 0; i < 3; i++)\r
355         {\r
356             g.drawLine(getWidth() / 2, getHeight() / 2,\r
357                 (int) ((axes[i][0] * scale * max[0]) + (getWidth() / 2)),\r
358                 (int) ((axes[i][1] * scale * max[1]) + (getHeight() / 2)));\r
359         }\r
360     }\r
361 \r
362     /**\r
363      * DOCUMENT ME!\r
364      *\r
365      * @param g DOCUMENT ME!\r
366      * @param col DOCUMENT ME!\r
367      */\r
368     public void drawBackground(Graphics g, Color col)\r
369     {\r
370         g.setColor(col);\r
371         g.fillRect(0, 0, prefsize.width, prefsize.height);\r
372     }\r
373 \r
374     /**\r
375      * DOCUMENT ME!\r
376      *\r
377      * @param g DOCUMENT ME!\r
378      */\r
379     public void drawScene(Graphics g1)\r
380     {\r
381 \r
382       Graphics2D g = (Graphics2D) g1;\r
383 \r
384       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,\r
385                          RenderingHints.VALUE_ANTIALIAS_ON);\r
386 \r
387 \r
388         int halfwidth = getWidth() / 2;\r
389         int halfheight = getHeight() / 2;\r
390 \r
391         for (int i = 0; i < npoint; i++)\r
392         {\r
393             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
394             int x = (int) ((float) (sp.coord[0] - centre[0]) * scale) +\r
395                 halfwidth;\r
396             int y = (int) ((float) (sp.coord[1] - centre[1]) * scale) +\r
397                 halfheight;\r
398             float z = sp.coord[1] - centre[2];\r
399 \r
400             if (av.getSequenceColour(sp.sequence) == Color.black)\r
401             {\r
402                 g.setColor(Color.white);\r
403             }\r
404             else\r
405             {\r
406                 g.setColor(av.getSequenceColour(sp.sequence));\r
407             }\r
408 \r
409             if (av.getSelectionGroup() != null)\r
410             {\r
411                 if (av.getSelectionGroup().getSequences(false).contains(\r
412                             ((SequencePoint) points.elementAt(i)).sequence))\r
413                 {\r
414                     g.setColor(Color.gray);\r
415                 }\r
416             }\r
417 \r
418             if (z < 0)\r
419             {\r
420                 g.setColor(g.getColor().darker());\r
421             }\r
422 \r
423             g.fillRect(x - 3, y - 3, 6, 6);\r
424             if(showLabels)\r
425             {\r
426               g.setColor(Color.red);\r
427               g.drawString( ( (SequencePoint) points.elementAt(i)).sequence.\r
428                            getName(),\r
429                            x - 3, y - 4);\r
430             }\r
431         }\r
432 \r
433         //    //Now the rectangle\r
434         //    if (rectx2 != -1 && recty2 != -1) {\r
435         //      g.setColor(Color.white);\r
436         //\r
437         //      g.drawRect(rectx1,recty1,rectx2-rectx1,recty2-recty1);\r
438         //    }\r
439     }\r
440 \r
441     /**\r
442      * DOCUMENT ME!\r
443      *\r
444      * @return DOCUMENT ME!\r
445      */\r
446     public Dimension minimumsize()\r
447     {\r
448         return prefsize;\r
449     }\r
450 \r
451     /**\r
452      * DOCUMENT ME!\r
453      *\r
454      * @return DOCUMENT ME!\r
455      */\r
456     public Dimension preferredsize()\r
457     {\r
458         return prefsize;\r
459     }\r
460 \r
461     /**\r
462      * DOCUMENT ME!\r
463      *\r
464      * @param evt DOCUMENT ME!\r
465      */\r
466     public void keyTyped(KeyEvent evt)\r
467     {\r
468     }\r
469 \r
470     /**\r
471      * DOCUMENT ME!\r
472      *\r
473      * @param evt DOCUMENT ME!\r
474      */\r
475     public void keyReleased(KeyEvent evt)\r
476     {\r
477     }\r
478 \r
479     /**\r
480      * DOCUMENT ME!\r
481      *\r
482      * @param evt DOCUMENT ME!\r
483      */\r
484     public void keyPressed(KeyEvent evt)\r
485     {\r
486         if (evt.getKeyCode() == KeyEvent.VK_UP)\r
487         {\r
488             scalefactor = (float) (scalefactor * 1.1);\r
489             scale = findScale();\r
490         }\r
491         else if (evt.getKeyCode() == KeyEvent.VK_DOWN)\r
492         {\r
493             scalefactor = (float) (scalefactor * 0.9);\r
494             scale = findScale();\r
495         }\r
496         else if (evt.getKeyChar() == 's')\r
497         {\r
498             System.err.println("DEBUG: Rectangle selection"); // log.debug\r
499 \r
500             if ((rectx2 != -1) && (recty2 != -1))\r
501             {\r
502                 rectSelect(rectx1, recty1, rectx2, recty2);\r
503             }\r
504         }\r
505 \r
506         repaint();\r
507     }\r
508 \r
509 \r
510     /**\r
511      * DOCUMENT ME!\r
512      *\r
513      * @param evt DOCUMENT ME!\r
514      */\r
515     public void mouseClicked(MouseEvent evt)\r
516     {\r
517     }\r
518 \r
519     /**\r
520      * DOCUMENT ME!\r
521      *\r
522      * @param evt DOCUMENT ME!\r
523      */\r
524     public void mouseEntered(MouseEvent evt)\r
525     {\r
526     }\r
527 \r
528     /**\r
529      * DOCUMENT ME!\r
530      *\r
531      * @param evt DOCUMENT ME!\r
532      */\r
533     public void mouseExited(MouseEvent evt)\r
534     {\r
535     }\r
536 \r
537     /**\r
538      * DOCUMENT ME!\r
539      *\r
540      * @param evt DOCUMENT ME!\r
541      */\r
542     public void mouseReleased(MouseEvent evt)\r
543     {\r
544     }\r
545 \r
546     /**\r
547      * DOCUMENT ME!\r
548      *\r
549      * @param evt DOCUMENT ME!\r
550      */\r
551     public void mousePressed(MouseEvent evt)\r
552     {\r
553         int x = evt.getX();\r
554         int y = evt.getY();\r
555 \r
556         mx = x;\r
557         my = y;\r
558 \r
559         omx = mx;\r
560         omy = my;\r
561 \r
562         startx = x;\r
563         starty = y;\r
564 \r
565         rectx1 = x;\r
566         recty1 = y;\r
567 \r
568         rectx2 = -1;\r
569         recty2 = -1;\r
570 \r
571         SequenceI found = findPoint(x, y);\r
572 \r
573         if (found != null)\r
574         {\r
575             if (av.getSelectionGroup() != null)\r
576             {\r
577                 av.getSelectionGroup().addOrRemove(found, true);\r
578                 PaintRefresher.Refresh(this, av.getSequenceSetId());\r
579             }\r
580             else\r
581             {\r
582                 av.setSelectionGroup(new SequenceGroup());\r
583                 av.getSelectionGroup().addOrRemove(found, true);\r
584                 av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);\r
585             }\r
586         }\r
587 \r
588         repaint();\r
589     }\r
590 \r
591     // private void fireSequenceSelectionEvent(Selection sel) {\r
592     //   controller.handleSequenceSelectionEvent(new SequenceSelectionEvent(this,sel));\r
593     //}\r
594     public void mouseMoved(MouseEvent evt)\r
595     {\r
596         SequenceI found = findPoint(evt.getX(), evt.getY());\r
597 \r
598         if (found != null)\r
599         {\r
600             this.setToolTipText(found.getName());\r
601         }\r
602         else\r
603         {\r
604             this.setToolTipText(null);\r
605         }\r
606     }\r
607 \r
608     /**\r
609      * DOCUMENT ME!\r
610      *\r
611      * @param evt DOCUMENT ME!\r
612      */\r
613     public void mouseDragged(MouseEvent evt)\r
614     {\r
615         mx = evt.getX();\r
616         my = evt.getY();\r
617 \r
618         //Check if this is a rectangle drawing drag\r
619         if ((evt.getModifiers() & InputEvent.BUTTON2_MASK) != 0)\r
620         {\r
621             //      rectx2 = evt.getX();\r
622             //      recty2 = evt.getY();\r
623         }\r
624         else\r
625         {\r
626             rotmat.setIdentity();\r
627 \r
628             rotmat.rotate((float) (my - omy), 'x');\r
629             rotmat.rotate((float) (mx - omx), 'y');\r
630 \r
631             for (int i = 0; i < npoint; i++)\r
632             {\r
633                 SequencePoint sp = (SequencePoint) points.elementAt(i);\r
634                 sp.coord[0] -= centre[0];\r
635                 sp.coord[1] -= centre[1];\r
636                 sp.coord[2] -= centre[2];\r
637 \r
638                 //Now apply the rotation matrix\r
639                 sp.coord = rotmat.vectorMultiply(sp.coord);\r
640 \r
641                 //Now translate back again\r
642                 sp.coord[0] += centre[0];\r
643                 sp.coord[1] += centre[1];\r
644                 sp.coord[2] += centre[2];\r
645             }\r
646 \r
647             for (int i = 0; i < 3; i++)\r
648             {\r
649                 axes[i] = rotmat.vectorMultiply(axes[i]);\r
650             }\r
651 \r
652             omx = mx;\r
653             omy = my;\r
654 \r
655             paint(this.getGraphics());\r
656         }\r
657     }\r
658 \r
659     /**\r
660      * DOCUMENT ME!\r
661      *\r
662      * @param x1 DOCUMENT ME!\r
663      * @param y1 DOCUMENT ME!\r
664      * @param x2 DOCUMENT ME!\r
665      * @param y2 DOCUMENT ME!\r
666      */\r
667     public void rectSelect(int x1, int y1, int x2, int y2)\r
668     {\r
669         for (int i = 0; i < npoint; i++)\r
670         {\r
671             SequencePoint sp = (SequencePoint) points.elementAt(i);\r
672             int tmp1 = (int) (((sp.coord[0] - centre[0]) * scale) +\r
673                 ((float) getWidth() / 2.0));\r
674             int tmp2 = (int) (((sp.coord[1] - centre[1]) * scale) +\r
675                 ((float) getHeight() / 2.0));\r
676 \r
677             if ((tmp1 > x1) && (tmp1 < x2) && (tmp2 > y1) && (tmp2 < y2))\r
678             {\r
679                 if (av != null)\r
680                 {\r
681                     if (!av.getSelectionGroup().getSequences(false).contains(sp.sequence))\r
682                     {\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