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