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