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