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