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