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