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