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