updated to jalview 2.1 and begun ArchiveClient/VamsasClient/VamsasStore updates.
[jalview.git] / src / jalview / appletgui / 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
20 package jalview.appletgui;
21
22 import java.util.*;
23
24 import java.awt.*;
25 import java.awt.event.*;
26
27 import jalview.datamodel.*;
28 import jalview.math.*;
29 import jalview.util.*;
30
31 public class RotatableCanvas
32     extends Panel implements MouseListener,
33     MouseMotionListener,
34     KeyListener
35 {
36   RotatableMatrix idmat = new RotatableMatrix(3, 3);
37   RotatableMatrix objmat = new RotatableMatrix(3, 3);
38   RotatableMatrix rotmat = new RotatableMatrix(3, 3);
39   String tooltip;
40   int toolx, tooly;
41
42   //RubberbandRectangle rubberband;
43
44   boolean drawAxes = true;
45
46   int omx = 0;
47   int mx = 0;
48   int omy = 0;
49   int my = 0;
50
51   Image img;
52   Graphics ig;
53
54   Dimension prefsize;
55
56   float centre[] = new float[3];
57   float width[] = new float[3];
58
59   float max[] = new float[3];
60   float min[] = new float[3];
61
62   float maxwidth;
63   float scale;
64
65   int npoint;
66
67   Vector points;
68   float[][] orig;
69   float[][] axes;
70
71   int startx;
72   int starty;
73
74   int lastx;
75   int lasty;
76
77   int rectx1;
78   int recty1;
79   int rectx2;
80   int recty2;
81
82   float scalefactor = 1;
83
84   AlignViewport av;
85   boolean showLabels = false;
86
87   public RotatableCanvas(AlignViewport av)
88   {
89     this.av = av;
90   }
91
92   public void showLabels(boolean b)
93   {
94     showLabels = b;
95     repaint();
96   }
97
98   public void setPoints(Vector points, int npoint)
99   {
100     this.points = points;
101     this.npoint = npoint;
102     PaintRefresher.Register(this, av.alignment);
103
104     prefsize = getPreferredSize();
105     orig = new float[npoint][3];
106
107     for (int i = 0; i < npoint; i++)
108     {
109       SequencePoint sp = (SequencePoint) points.elementAt(i);
110       for (int j = 0; j < 3; j++)
111       {
112         orig[i][j] = sp.coord[j];
113       }
114     }
115     //Initialize the matrices to identity
116
117     for (int i = 0; i < 3; i++)
118     {
119       for (int j = 0; j < 3; j++)
120       {
121         if (i != j)
122         {
123           idmat.addElement(i, j, 0);
124           objmat.addElement(i, j, 0);
125           rotmat.addElement(i, j, 0);
126         }
127         else
128         {
129           idmat.addElement(i, j, 0);
130           objmat.addElement(i, j, 0);
131           rotmat.addElement(i, j, 0);
132         }
133       }
134     }
135
136     axes = new float[3][3];
137     initAxes();
138
139     findCentre();
140     findWidth();
141
142     scale = findScale();
143
144     //    System.out.println("Scale factor = " + scale);
145
146     addMouseListener(this);
147     addKeyListener(this);
148     // if (getParent() != null) {
149     //   getParent().addKeyListener(this);
150     //}
151     addMouseMotionListener(this);
152
153     // Add rubberband
154     //   rubberband  = new RubberbandRectangle(this);
155     //  rubberband.setActive(true);
156     //   rubberband.addListener(this);
157   }
158
159   /* public boolean handleSequenceSelectionEvent(SequenceSelectionEvent evt) {
160      redrawneeded = true;
161      repaint();
162      return true;
163    }
164
165    public void removeNotify() {
166      controller.removeListener(this);
167      super.removeNotify();
168    }*/
169
170   public void initAxes()
171   {
172     for (int i = 0; i < 3; i++)
173     {
174       for (int j = 0; j < 3; j++)
175       {
176         if (i != j)
177         {
178           axes[i][j] = 0;
179         }
180         else
181         {
182           axes[i][j] = 1;
183         }
184       }
185     }
186   }
187
188   public void findWidth()
189   {
190     max = new float[3];
191     min = new float[3];
192
193     max[0] = (float) - 1e30;
194     max[1] = (float) - 1e30;
195     max[2] = (float) - 1e30;
196
197     min[0] = (float) 1e30;
198     min[1] = (float) 1e30;
199     min[2] = (float) 1e30;
200
201     for (int i = 0; i < 3; i++)
202     {
203       for (int j = 0; j < npoint; j++)
204       {
205         SequencePoint sp = (SequencePoint) points.elementAt(j);
206         if (sp.coord[i] >= max[i])
207         {
208           max[i] = sp.coord[i];
209         }
210         if (sp.coord[i] <= min[i])
211         {
212           min[i] = sp.coord[i];
213         }
214       }
215     }
216
217     //    System.out.println("xmax " + max[0] + " min " + min[0]);
218     //System.out.println("ymax " + max[1] + " min " + min[1]);
219     //System.out.println("zmax " + max[2] + " min " + min[2]);
220
221     width[0] = Math.abs(max[0] - min[0]);
222     width[1] = Math.abs(max[1] - min[1]);
223     width[2] = Math.abs(max[2] - min[2]);
224
225     maxwidth = width[0];
226
227     if (width[1] > width[0])
228     {
229       maxwidth = width[1];
230     }
231     if (width[2] > width[1])
232     {
233       maxwidth = width[2];
234     }
235
236     //System.out.println("Maxwidth = " + maxwidth);
237   }
238
239   public float findScale()
240   {
241     int dim, width, height;
242     if (getSize().width != 0)
243     {
244       width = getSize().width;
245       height = getSize().height;
246     }
247     else
248     {
249       width = prefsize.width;
250       height = prefsize.height;
251     }
252
253     if (width < height)
254     {
255       dim = width;
256     }
257     else
258     {
259       dim = height;
260     }
261
262     return (float) (dim * scalefactor / (2 * maxwidth));
263   }
264
265   public void findCentre()
266   {
267     //Find centre coordinate
268     findWidth();
269
270     centre[0] = (max[0] + min[0]) / 2;
271     centre[1] = (max[1] + min[1]) / 2;
272     centre[2] = (max[2] + min[2]) / 2;
273
274     //    System.out.println("Centre x " + centre[0]);
275     //System.out.println("Centre y " + centre[1]);
276     //System.out.println("Centre z " + centre[2]);
277   }
278
279   public Dimension getPreferredSize()
280   {
281     if (prefsize != null)
282     {
283       return prefsize;
284     }
285     else
286     {
287       return new Dimension(400, 400);
288     }
289   }
290
291   public Dimension getMinimumSize()
292   {
293     return getPreferredSize();
294   }
295
296   public void update(Graphics g)
297   {
298     paint(g);
299   }
300   public void paint(Graphics g)
301   {
302     if (points == null)
303     {
304       g.setFont(new Font("Verdana", Font.PLAIN, 18));
305       g.drawString("Calculating PCA....", 20, getSize().height / 2);
306     }
307     else
308     {
309
310       //Only create the image at the beginning -
311       if ( (img == null) || (prefsize.width != getSize().width) ||
312           (prefsize.height != getSize().height))
313       {
314         prefsize.width = getSize().width;
315         prefsize.height = getSize().height;
316
317         scale = findScale();
318
319         //      System.out.println("New scale = " + scale);
320         img = createImage(getSize().width, getSize().height);
321         ig = img.getGraphics();
322
323       }
324
325       drawBackground(ig, Color.black);
326       drawScene(ig);
327       if (drawAxes == true)
328       {
329         drawAxes(ig);
330       }
331
332       if(tooltip!=null)
333       {
334         ig.setColor(Color.red);
335         ig.drawString(tooltip, toolx, tooly);
336       }
337
338       g.drawImage(img, 0, 0, this);
339     }
340   }
341
342   public void drawAxes(Graphics g)
343   {
344
345     g.setColor(Color.yellow);
346     for (int i = 0; i < 3; i++)
347     {
348       g.drawLine(getSize().width / 2, getSize().height / 2,
349                  (int) (axes[i][0] * scale * max[0] + getSize().width / 2),
350                  (int) (axes[i][1] * scale * max[1] + getSize().height / 2));
351     }
352   }
353
354   public void drawBackground(Graphics g, Color col)
355   {
356     g.setColor(col);
357     g.fillRect(0, 0, prefsize.width, prefsize.height);
358   }
359
360   public void drawScene(Graphics g)
361   {
362     //boolean darker = false;
363
364     int halfwidth = getSize().width / 2;
365     int halfheight = getSize().height / 2;
366
367     for (int i = 0; i < npoint; i++)
368     {
369       SequencePoint sp = (SequencePoint) points.elementAt(i);
370       int x = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;
371       int y = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;
372       float z = sp.coord[1] - centre[2];
373
374       if (sp.sequence.getColor() == Color.black)
375       {
376         g.setColor(Color.white);
377       }
378       else
379       {
380         g.setColor(sp.sequence.getColor());
381       }
382
383       if (av.getSelectionGroup() != null)
384       {
385         if (av.getSelectionGroup().getSequences(false).contains( ( (SequencePoint) points.
386             elementAt(i)).sequence))
387         {
388           g.setColor(Color.gray);
389         }
390       }
391       if (z < 0)
392       {
393         g.setColor(g.getColor().darker());
394       }
395
396       g.fillRect(x - 3, y - 3, 6, 6);
397       if (showLabels)
398       {
399         g.setColor(Color.red);
400         g.drawString( ( (SequencePoint) points.elementAt(i)).sequence.
401                      getName(),
402                      x - 3, y - 4);
403       }
404     }
405   }
406
407   public Dimension minimumsize()
408   {
409     return prefsize;
410   }
411
412   public Dimension preferredsize()
413   {
414     return prefsize;
415   }
416
417   public void keyTyped(KeyEvent evt)
418   {}
419
420   public void keyReleased(KeyEvent evt)
421   {}
422
423   public void keyPressed(KeyEvent evt)
424   {
425     if (evt.getKeyCode() == KeyEvent.VK_UP)
426     {
427       scalefactor = (float) (scalefactor * 1.1);
428       scale = findScale();
429     }
430     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
431     {
432       scalefactor = (float) (scalefactor * 0.9);
433       scale = findScale();
434     }
435     else if (evt.getKeyChar() == 's')
436     {
437       System.err.println("DEBUG: Rectangle selection"); // log.debug
438       if (rectx2 != -1 && recty2 != -1)
439       {
440         rectSelect(rectx1, recty1, rectx2, recty2);
441
442       }
443     }
444     repaint();
445   }
446
447   public void printPoints()
448   {
449     for (int i = 0; i < npoint; i++)
450     {
451       SequencePoint sp = (SequencePoint) points.elementAt(i);
452       Format.print(System.out, "%5d ", i);
453       for (int j = 0; j < 3; j++)
454       {
455         Format.print(System.out, "%13.3f  ", sp.coord[j]);
456       }
457       System.out.println();
458     }
459   }
460
461   public void mouseClicked(MouseEvent evt)
462   {}
463
464   public void mouseEntered(MouseEvent evt)
465   {}
466
467   public void mouseExited(MouseEvent evt)
468   {}
469
470   public void mouseReleased(MouseEvent evt)
471   {}
472
473   public void mousePressed(MouseEvent evt)
474   {
475     int x = evt.getX();
476     int y = evt.getY();
477
478     mx = x;
479     my = y;
480
481     omx = mx;
482     omy = my;
483
484     startx = x;
485     starty = y;
486
487     rectx1 = x;
488     recty1 = y;
489
490     rectx2 = -1;
491     recty2 = -1;
492
493     SequenceI found = findPoint(x, y);
494
495     if (found != null)
496     {
497       if (av.getSelectionGroup() != null)
498       {
499         av.getSelectionGroup().addOrRemove(found, true);
500         av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);
501         PaintRefresher.Refresh(this, av.alignment);
502       }
503       else
504       {
505         av.setSelectionGroup(new SequenceGroup());
506         av.getSelectionGroup().addOrRemove(found, true);
507         av.getSelectionGroup().setEndRes(av.alignment.getWidth()-1);
508
509       }
510     }
511     repaint();
512   }
513
514
515   public void mouseMoved(MouseEvent evt)
516   {
517      SequenceI found = findPoint(evt.getX(), evt.getY());
518      if(found==null)
519        tooltip = null;
520      else
521      {
522        tooltip = found.getName();
523        toolx = evt.getX();
524        tooly = evt.getY();
525      }
526      repaint();
527   }
528
529   public void mouseDragged(MouseEvent evt)
530   {
531     mx = evt.getX();
532     my = evt.getY();
533
534       rotmat.setIdentity();
535
536       rotmat.rotate( (float) (my - omy), 'x');
537       rotmat.rotate( (float) (mx - omx), 'y');
538
539       for (int i = 0; i < npoint; i++)
540       {
541         SequencePoint sp = (SequencePoint) points.elementAt(i);
542         sp.coord[0] -= centre[0];
543         sp.coord[1] -= centre[1];
544         sp.coord[2] -= centre[2];
545
546         //Now apply the rotation matrix
547         sp.coord = rotmat.vectorMultiply(sp.coord);
548
549         //Now translate back again
550         sp.coord[0] += centre[0];
551         sp.coord[1] += centre[1];
552         sp.coord[2] += centre[2];
553       }
554
555       for (int i = 0; i < 3; i++)
556       {
557         axes[i] = rotmat.vectorMultiply(axes[i]);
558       }
559       omx = mx;
560       omy = my;
561
562       paint(this.getGraphics());
563   }
564
565   public void rectSelect(int x1, int y1, int x2, int y2)
566   {
567     //boolean changedSel = false;
568     for (int i = 0; i < npoint; i++)
569     {
570       SequencePoint sp = (SequencePoint) points.elementAt(i);
571       int tmp1 = (int) ( (sp.coord[0] - centre[0]) * scale +
572                         (float) getSize().width / 2.0);
573       int tmp2 = (int) ( (sp.coord[1] - centre[1]) * scale +
574                         (float) getSize().height / 2.0);
575
576       if (tmp1 > x1 && tmp1 < x2 && tmp2 > y1 && tmp2 < y2)
577       {
578         if (av != null)
579         {
580           if (!av.getSelectionGroup().getSequences(false).contains(sp.sequence))
581           {
582             av.getSelectionGroup().addSequence(sp.sequence, true);
583           }
584         }
585       }
586     }
587   }
588
589   public SequenceI findPoint(int x, int y)
590   {
591
592     int halfwidth = getSize().width / 2;
593     int halfheight = getSize().height / 2;
594
595     int found = -1;
596
597     for (int i = 0; i < npoint; i++)
598     {
599
600       SequencePoint sp = (SequencePoint) points.elementAt(i);
601       int px = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;
602       int py = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;
603
604       if (Math.abs(px - x) < 3 && Math.abs(py - y) < 3)
605       {
606         found = i;
607       }
608     }
609     if (found != -1)
610     {
611       return ( (SequencePoint) points.elementAt(found)).sequence;
612     }
613     else
614     {
615       return null;
616     }
617   }
618
619 }