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