recovery of identical dataset sequence object on undo (rather than creation of a...
[jalview.git] / src / jalview / appletgui / RotatableCanvas.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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 \r
301   public void paint(Graphics g)\r
302   {\r
303     if (points == null)\r
304     {\r
305       g.setFont(new Font("Verdana", Font.PLAIN, 18));\r
306       g.drawString("Calculating PCA....", 20, getSize().height / 2);\r
307     }\r
308     else\r
309     {\r
310 \r
311       //Only create the image at the beginning -\r
312       if ( (img == null) || (prefsize.width != getSize().width) ||\r
313           (prefsize.height != getSize().height))\r
314       {\r
315         prefsize.width = getSize().width;\r
316         prefsize.height = getSize().height;\r
317 \r
318         scale = findScale();\r
319 \r
320         //      System.out.println("New scale = " + scale);\r
321         img = createImage(getSize().width, getSize().height);\r
322         ig = img.getGraphics();\r
323 \r
324       }\r
325 \r
326       drawBackground(ig, Color.black);\r
327       drawScene(ig);\r
328       if (drawAxes == true)\r
329       {\r
330         drawAxes(ig);\r
331       }\r
332 \r
333       if (tooltip != null)\r
334       {\r
335         ig.setColor(Color.red);\r
336         ig.drawString(tooltip, toolx, tooly);\r
337       }\r
338 \r
339       g.drawImage(img, 0, 0, this);\r
340     }\r
341   }\r
342 \r
343   public void drawAxes(Graphics g)\r
344   {\r
345 \r
346     g.setColor(Color.yellow);\r
347     for (int i = 0; i < 3; i++)\r
348     {\r
349       g.drawLine(getSize().width / 2, getSize().height / 2,\r
350                  (int) (axes[i][0] * scale * max[0] + getSize().width / 2),\r
351                  (int) (axes[i][1] * scale * max[1] + getSize().height / 2));\r
352     }\r
353   }\r
354 \r
355   public void drawBackground(Graphics g, Color col)\r
356   {\r
357     g.setColor(col);\r
358     g.fillRect(0, 0, prefsize.width, prefsize.height);\r
359   }\r
360 \r
361   public void drawScene(Graphics g)\r
362   {\r
363     //boolean darker = false;\r
364 \r
365     int halfwidth = getSize().width / 2;\r
366     int halfheight = getSize().height / 2;\r
367 \r
368     for (int i = 0; i < npoint; i++)\r
369     {\r
370       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
371       int x = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;\r
372       int y = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;\r
373       float z = sp.coord[1] - centre[2];\r
374 \r
375       if (av.getSequenceColour(sp.sequence) == Color.black)\r
376       {\r
377         g.setColor(Color.white);\r
378       }\r
379       else\r
380       {\r
381         g.setColor(av.getSequenceColour(sp.sequence));\r
382       }\r
383 \r
384       if (av.getSelectionGroup() != null)\r
385       {\r
386         if (av.getSelectionGroup().getSequences(null).contains( ( (\r
387             SequencePoint) points.\r
388             elementAt(i)).sequence))\r
389         {\r
390           g.setColor(Color.gray);\r
391         }\r
392       }\r
393       if (z < 0)\r
394       {\r
395         g.setColor(g.getColor().darker());\r
396       }\r
397 \r
398       g.fillRect(x - 3, y - 3, 6, 6);\r
399       if (showLabels)\r
400       {\r
401         g.setColor(Color.red);\r
402         g.drawString( ( (SequencePoint) points.elementAt(i)).sequence.\r
403                      getName(),\r
404                      x - 3, y - 4);\r
405       }\r
406     }\r
407   }\r
408 \r
409   public Dimension minimumsize()\r
410   {\r
411     return prefsize;\r
412   }\r
413 \r
414   public Dimension preferredsize()\r
415   {\r
416     return prefsize;\r
417   }\r
418 \r
419   public void keyTyped(KeyEvent evt)\r
420   {}\r
421 \r
422   public void keyReleased(KeyEvent evt)\r
423   {}\r
424 \r
425   public void keyPressed(KeyEvent evt)\r
426   {\r
427     if (evt.getKeyCode() == KeyEvent.VK_UP)\r
428     {\r
429       scalefactor = (float) (scalefactor * 1.1);\r
430       scale = findScale();\r
431     }\r
432     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)\r
433     {\r
434       scalefactor = (float) (scalefactor * 0.9);\r
435       scale = findScale();\r
436     }\r
437     else if (evt.getKeyChar() == 's')\r
438     {\r
439       System.err.println("DEBUG: Rectangle selection"); // log.debug\r
440       if (rectx2 != -1 && recty2 != -1)\r
441       {\r
442         rectSelect(rectx1, recty1, rectx2, recty2);\r
443 \r
444       }\r
445     }\r
446     repaint();\r
447   }\r
448 \r
449   public void printPoints()\r
450   {\r
451     for (int i = 0; i < npoint; i++)\r
452     {\r
453       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
454       Format.print(System.out, "%5d ", i);\r
455       for (int j = 0; j < 3; j++)\r
456       {\r
457         Format.print(System.out, "%13.3f  ", sp.coord[j]);\r
458       }\r
459       System.out.println();\r
460     }\r
461   }\r
462 \r
463   public void mouseClicked(MouseEvent evt)\r
464   {}\r
465 \r
466   public void mouseEntered(MouseEvent evt)\r
467   {}\r
468 \r
469   public void mouseExited(MouseEvent evt)\r
470   {}\r
471 \r
472   public void mouseReleased(MouseEvent evt)\r
473   {}\r
474 \r
475   public void mousePressed(MouseEvent evt)\r
476   {\r
477     int x = evt.getX();\r
478     int y = evt.getY();\r
479 \r
480     mx = x;\r
481     my = y;\r
482 \r
483     omx = mx;\r
484     omy = my;\r
485 \r
486     startx = x;\r
487     starty = y;\r
488 \r
489     rectx1 = x;\r
490     recty1 = y;\r
491 \r
492     rectx2 = -1;\r
493     recty2 = -1;\r
494 \r
495     SequenceI found = findPoint(x, y);\r
496 \r
497     if (found != null)\r
498     {\r
499       if (av.getSelectionGroup() != null)\r
500       {\r
501         av.getSelectionGroup().addOrRemove(found, true);\r
502         av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);\r
503         PaintRefresher.Refresh(this, av.getSequenceSetId());\r
504       }\r
505       else\r
506       {\r
507         av.setSelectionGroup(new SequenceGroup());\r
508         av.getSelectionGroup().addOrRemove(found, true);\r
509         av.getSelectionGroup().setEndRes(av.alignment.getWidth() - 1);\r
510 \r
511       }\r
512     }\r
513     repaint();\r
514   }\r
515 \r
516   public void mouseMoved(MouseEvent evt)\r
517   {\r
518     SequenceI found = findPoint(evt.getX(), evt.getY());\r
519     if (found == null)\r
520     {\r
521       tooltip = null;\r
522     }\r
523     else\r
524     {\r
525       tooltip = found.getName();\r
526       toolx = evt.getX();\r
527       tooly = evt.getY();\r
528     }\r
529     repaint();\r
530   }\r
531 \r
532   public void mouseDragged(MouseEvent evt)\r
533   {\r
534     mx = evt.getX();\r
535     my = evt.getY();\r
536 \r
537     rotmat.setIdentity();\r
538 \r
539     rotmat.rotate( (float) (my - omy), 'x');\r
540     rotmat.rotate( (float) (mx - omx), 'y');\r
541 \r
542     for (int i = 0; i < npoint; i++)\r
543     {\r
544       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
545       sp.coord[0] -= centre[0];\r
546       sp.coord[1] -= centre[1];\r
547       sp.coord[2] -= centre[2];\r
548 \r
549       //Now apply the rotation matrix\r
550       sp.coord = rotmat.vectorMultiply(sp.coord);\r
551 \r
552       //Now translate back again\r
553       sp.coord[0] += centre[0];\r
554       sp.coord[1] += centre[1];\r
555       sp.coord[2] += centre[2];\r
556     }\r
557 \r
558     for (int i = 0; i < 3; i++)\r
559     {\r
560       axes[i] = rotmat.vectorMultiply(axes[i]);\r
561     }\r
562     omx = mx;\r
563     omy = my;\r
564 \r
565     paint(this.getGraphics());\r
566   }\r
567 \r
568   public void rectSelect(int x1, int y1, int x2, int y2)\r
569   {\r
570     //boolean changedSel = false;\r
571     for (int i = 0; i < npoint; i++)\r
572     {\r
573       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
574       int tmp1 = (int) ( (sp.coord[0] - centre[0]) * scale +\r
575                         (float) getSize().width / 2.0);\r
576       int tmp2 = (int) ( (sp.coord[1] - centre[1]) * scale +\r
577                         (float) getSize().height / 2.0);\r
578 \r
579       if (tmp1 > x1 && tmp1 < x2 && tmp2 > y1 && tmp2 < y2)\r
580       {\r
581         if (av != null)\r
582         {\r
583           if (!av.getSelectionGroup().getSequences(null).contains(sp.sequence))\r
584           {\r
585             av.getSelectionGroup().addSequence(sp.sequence, true);\r
586           }\r
587         }\r
588       }\r
589     }\r
590   }\r
591 \r
592   public SequenceI findPoint(int x, int y)\r
593   {\r
594 \r
595     int halfwidth = getSize().width / 2;\r
596     int halfheight = getSize().height / 2;\r
597 \r
598     int found = -1;\r
599 \r
600     for (int i = 0; i < npoint; i++)\r
601     {\r
602 \r
603       SequencePoint sp = (SequencePoint) points.elementAt(i);\r
604       int px = (int) ( (float) (sp.coord[0] - centre[0]) * scale) + halfwidth;\r
605       int py = (int) ( (float) (sp.coord[1] - centre[1]) * scale) + halfheight;\r
606 \r
607       if (Math.abs(px - x) < 3 && Math.abs(py - y) < 3)\r
608       {\r
609         found = i;\r
610       }\r
611     }\r
612     if (found != -1)\r
613     {\r
614       return ( (SequencePoint) points.elementAt(found)).sequence;\r
615     }\r
616     else\r
617     {\r
618       return null;\r
619     }\r
620   }\r
621 \r
622 }\r