Formatting
[jalview.git] / src / jalview / gui / PCAPanel.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 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 java.awt.print.*;\r
26 import javax.swing.*;\r
27 \r
28 import jalview.analysis.*;\r
29 import jalview.datamodel.*;\r
30 import jalview.jbgui.*;\r
31 \r
32 /**\r
33  * DOCUMENT ME!\r
34  *\r
35  * @author $author$\r
36  * @version $Revision$\r
37  */\r
38 public class PCAPanel\r
39     extends GPCAPanel implements Runnable\r
40 {\r
41   PCA pca;\r
42   int top;\r
43   RotatableCanvas rc;\r
44   AlignmentPanel ap;\r
45   AlignViewport av;\r
46   AlignmentView seqstrings;\r
47   SequenceI[] seqs;\r
48 \r
49   /**\r
50    * Creates a new PCAPanel object.\r
51    *\r
52    * @param av DOCUMENT ME!\r
53    * @param s DOCUMENT ME!\r
54    */\r
55   public PCAPanel(AlignmentPanel ap)\r
56   {\r
57     this.av = ap.av;\r
58     this.ap = ap;\r
59 \r
60     boolean sameLength = true;\r
61 \r
62     seqstrings = av.getAlignmentView(av.getSelectionGroup() != null);\r
63     if (av.getSelectionGroup() == null)\r
64     {\r
65       seqs = av.alignment.getSequencesArray();\r
66     }\r
67     else\r
68     {\r
69       seqs = av.getSelectionGroup().getSequencesInOrder(av.alignment);\r
70     }\r
71     SeqCigar sq[] = seqstrings.getSequences();\r
72     int length = sq[0].getWidth();\r
73 \r
74     for (int i = 0; i < seqs.length; i++)\r
75     {\r
76       if (sq[i].getWidth() != length)\r
77       {\r
78         sameLength = false;\r
79         break;\r
80       }\r
81     }\r
82 \r
83     if (!sameLength)\r
84     {\r
85       JOptionPane.showMessageDialog(Desktop.desktop,\r
86                                     "The sequences must be aligned before calculating PCA.\n" +\r
87                                     "Try using the Pad function in the edit menu,\n" +\r
88                                     "or one of the multiple sequence alignment web services.",\r
89                                     "Sequences not aligned",\r
90                                     JOptionPane.WARNING_MESSAGE);\r
91 \r
92       return;\r
93     }\r
94 \r
95     Desktop.addInternalFrame(this, "Principal component analysis",\r
96                              400, 400);\r
97 \r
98     PaintRefresher.Register(this, av.getSequenceSetId());\r
99 \r
100     rc = new RotatableCanvas(ap);\r
101     this.getContentPane().add(rc, BorderLayout.CENTER);\r
102     Thread worker = new Thread(this);\r
103     worker.start();\r
104   }\r
105 \r
106   public void bgcolour_actionPerformed(ActionEvent e)\r
107   {\r
108     Color col = JColorChooser.showDialog(this, "Select Background Colour",\r
109                                          rc.bgColour);\r
110 \r
111     if (col != null)\r
112     {\r
113       rc.bgColour = col;\r
114     }\r
115     rc.repaint();\r
116   }\r
117 \r
118   /**\r
119    * DOCUMENT ME!\r
120    */\r
121   public void run()\r
122   {\r
123     try\r
124     {\r
125       pca = new PCA(seqstrings.getSequenceStrings(' '));\r
126       pca.run();\r
127 \r
128       // Now find the component coordinates\r
129       int ii = 0;\r
130 \r
131       while ( (ii < seqs.length) && (seqs[ii] != null))\r
132       {\r
133         ii++;\r
134       }\r
135 \r
136       double[][] comps = new double[ii][ii];\r
137 \r
138       for (int i = 0; i < ii; i++)\r
139       {\r
140         if (pca.getEigenvalue(i) > 1e-4)\r
141         {\r
142           comps[i] = pca.component(i);\r
143         }\r
144       }\r
145 \r
146       //////////////////\r
147       xCombobox.setSelectedIndex(0);\r
148       yCombobox.setSelectedIndex(1);\r
149       zCombobox.setSelectedIndex(2);\r
150 \r
151       top = pca.getM().rows - 1;\r
152 \r
153       Vector points = new Vector();\r
154       float[][] scores = pca.getComponents(top - 1, top - 2, top - 3, 100);\r
155 \r
156       for (int i = 0; i < pca.getM().rows; i++)\r
157       {\r
158         SequencePoint sp = new SequencePoint(seqs[i], scores[i]);\r
159         points.addElement(sp);\r
160       }\r
161 \r
162       rc.setPoints(points, pca.getM().rows);\r
163       rc.repaint();\r
164 \r
165       addKeyListener(rc);\r
166 \r
167     }\r
168     catch (OutOfMemoryError er)\r
169     {\r
170       JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
171                                             "Out of memory calculating PCA!!"\r
172                                             +\r
173                                             "\nSee help files for increasing Java Virtual Machine memory."\r
174                                             , "Out of memory",\r
175                                             JOptionPane.WARNING_MESSAGE);\r
176       System.out.println("PCAPanel: " + er);\r
177       System.gc();\r
178 \r
179     }\r
180 \r
181   }\r
182 \r
183   /**\r
184    * DOCUMENT ME!\r
185    */\r
186   void doDimensionChange()\r
187   {\r
188     if (top == 0)\r
189     {\r
190       return;\r
191     }\r
192 \r
193     int dim1 = top - xCombobox.getSelectedIndex();\r
194     int dim2 = top - yCombobox.getSelectedIndex();\r
195     int dim3 = top - zCombobox.getSelectedIndex();\r
196 \r
197     float[][] scores = pca.getComponents(dim1, dim2, dim3, 100);\r
198 \r
199     for (int i = 0; i < pca.getM().rows; i++)\r
200     {\r
201       ( (SequencePoint) rc.points.elementAt(i)).coord = scores[i];\r
202     }\r
203 \r
204     rc.img = null;\r
205     rc.rotmat.setIdentity();\r
206     rc.initAxes();\r
207     rc.paint(rc.getGraphics());\r
208   }\r
209 \r
210   /**\r
211    * DOCUMENT ME!\r
212    *\r
213    * @param e DOCUMENT ME!\r
214    */\r
215   protected void xCombobox_actionPerformed(ActionEvent e)\r
216   {\r
217     doDimensionChange();\r
218   }\r
219 \r
220   /**\r
221    * DOCUMENT ME!\r
222    *\r
223    * @param e DOCUMENT ME!\r
224    */\r
225   protected void yCombobox_actionPerformed(ActionEvent e)\r
226   {\r
227     doDimensionChange();\r
228   }\r
229 \r
230   /**\r
231    * DOCUMENT ME!\r
232    *\r
233    * @param e DOCUMENT ME!\r
234    */\r
235   protected void zCombobox_actionPerformed(ActionEvent e)\r
236   {\r
237     doDimensionChange();\r
238   }\r
239 \r
240   public void outputValues_actionPerformed(ActionEvent e)\r
241   {\r
242     CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
243     Desktop.addInternalFrame(cap, "PCA details", 500,\r
244                              500);\r
245 \r
246     cap.setText(pca.getDetails());\r
247   }\r
248 \r
249   public void showLabels_actionPerformed(ActionEvent e)\r
250   {\r
251     rc.showLabels(showLabels.getState());\r
252   }\r
253 \r
254   public void print_actionPerformed(ActionEvent e)\r
255   {\r
256     PCAPrinter printer = new PCAPrinter();\r
257     printer.start();\r
258   }\r
259 \r
260   public void originalSeqData_actionPerformed(ActionEvent e)\r
261   {\r
262     // this was cut'n'pasted from the equivalent TreePanel method - we should make this an abstract function of all jalview analysis windows\r
263     if (seqstrings == null)\r
264     {\r
265       jalview.bin.Cache.log.info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");\r
266       return;\r
267     }\r
268     // decide if av alignment is sufficiently different to original data to warrant a new window to be created\r
269     // create new alignmnt window with hidden regions (unhiding hidden regions yields unaligned seqs)\r
270     // or create a selection box around columns in alignment view\r
271     // test Alignment(SeqCigar[])\r
272     Object[] alAndColsel = seqstrings.getAlignmentAndColumnSelection(av.\r
273         getGapCharacter());\r
274 \r
275     if (alAndColsel != null && alAndColsel[0] != null)\r
276     {\r
277       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);\r
278 \r
279       Alignment al = new Alignment( (SequenceI[]) alAndColsel[0]);\r
280       Alignment dataset = av.getAlignment().getDataset();\r
281       if (dataset != null)\r
282       {\r
283         al.setDataset(dataset);\r
284       }\r
285 \r
286       if (true)\r
287       {\r
288         // make a new frame!\r
289         AlignFrame af = new AlignFrame(al, (ColumnSelection) alAndColsel[1],\r
290                                        AlignFrame.DEFAULT_WIDTH,\r
291                                        AlignFrame.DEFAULT_HEIGHT\r
292             );\r
293 \r
294         //>>>This is a fix for the moment, until a better solution is found!!<<<\r
295         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());\r
296 \r
297         //           af.addSortByOrderMenuItem(ServiceName + " Ordering",\r
298         //                                     msaorder);\r
299 \r
300         Desktop.addInternalFrame(af, "Original Data for " + this.title,\r
301                                  AlignFrame.DEFAULT_WIDTH,\r
302                                  AlignFrame.DEFAULT_HEIGHT);\r
303       }\r
304     }\r
305     /*      CutAndPasteTransfer cap = new CutAndPasteTransfer();\r
306            for (int i = 0; i < seqs.length; i++)\r
307            {\r
308              cap.appendText(new jalview.util.Format("%-" + 15 + "s").form(\r
309       seqs[i].getName()));\r
310              cap.appendText(" " + seqstrings[i] + "\n");\r
311 \r
312            }\r
313 \r
314            Desktop.addInternalFrame(cap, "Original Data",\r
315                400, 400);\r
316      */\r
317   }\r
318 \r
319   class PCAPrinter\r
320       extends Thread implements Printable\r
321   {\r
322     public void run()\r
323     {\r
324       PrinterJob printJob = PrinterJob.getPrinterJob();\r
325       PageFormat pf = printJob.pageDialog(printJob.defaultPage());\r
326 \r
327       printJob.setPrintable(this, pf);\r
328 \r
329       if (printJob.printDialog())\r
330       {\r
331         try\r
332         {\r
333           printJob.print();\r
334         }\r
335         catch (Exception PrintException)\r
336         {\r
337           PrintException.printStackTrace();\r
338         }\r
339       }\r
340     }\r
341 \r
342     public int print(Graphics pg, PageFormat pf, int pi)\r
343         throws PrinterException\r
344     {\r
345       pg.translate( (int) pf.getImageableX(), (int) pf.getImageableY());\r
346 \r
347       rc.drawBackground(pg, rc.bgColour);\r
348       rc.drawScene(pg);\r
349       if (rc.drawAxes == true)\r
350       {\r
351         rc.drawAxes(pg);\r
352       }\r
353 \r
354       if (pi == 0)\r
355       {\r
356         return Printable.PAGE_EXISTS;\r
357       }\r
358       else\r
359       {\r
360         return Printable.NO_SUCH_PAGE;\r
361       }\r
362     }\r
363   }\r
364 \r
365   /**\r
366    * DOCUMENT ME!\r
367    *\r
368    * @param e DOCUMENT ME!\r
369    */\r
370   public void eps_actionPerformed(ActionEvent e)\r
371   {\r
372     makePCAImage(jalview.util.ImageMaker.EPS);\r
373   }\r
374 \r
375   /**\r
376    * DOCUMENT ME!\r
377    *\r
378    * @param e DOCUMENT ME!\r
379    */\r
380   public void png_actionPerformed(ActionEvent e)\r
381   {\r
382     makePCAImage(jalview.util.ImageMaker.PNG);\r
383   }\r
384 \r
385   void makePCAImage(int type)\r
386   {\r
387     int width = rc.getWidth();\r
388     int height = rc.getHeight();\r
389 \r
390     jalview.util.ImageMaker im;\r
391 \r
392     if (type == jalview.util.ImageMaker.PNG)\r
393     {\r
394       im = new jalview.util.ImageMaker(this,\r
395                                        jalview.util.ImageMaker.PNG,\r
396                                        "Make PNG image from PCA",\r
397                                        width, height,\r
398                                        null, null);\r
399     }\r
400     else\r
401     {\r
402       im = new jalview.util.ImageMaker(this,\r
403                                        jalview.util.ImageMaker.EPS,\r
404                                        "Make EPS file from PCA",\r
405                                        width, height,\r
406                                        null, this.getTitle());\r
407     }\r
408 \r
409     if (im.getGraphics() != null)\r
410     {\r
411       rc.drawBackground(im.getGraphics(), Color.black);\r
412       rc.drawScene(im.getGraphics());\r
413       if (rc.drawAxes == true)\r
414       {\r
415         rc.drawAxes(im.getGraphics());\r
416       }\r
417       im.writeImage();\r
418     }\r
419   }\r
420 \r
421   public void viewMenu_menuSelected()\r
422   {\r
423     buildAssociatedViewMenu();\r
424   }\r
425 \r
426   void buildAssociatedViewMenu()\r
427   {\r
428     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av.\r
429         getSequenceSetId());\r
430     if (aps.length == 1 && rc.av == aps[0].av)\r
431     {\r
432       associateViewsMenu.setVisible(false);\r
433       return;\r
434     }\r
435 \r
436     associateViewsMenu.setVisible(true);\r
437 \r
438     if ( (viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))\r
439     {\r
440       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);\r
441     }\r
442 \r
443     associateViewsMenu.removeAll();\r
444 \r
445     JRadioButtonMenuItem item;\r
446     ButtonGroup buttonGroup = new ButtonGroup();\r
447     int i, iSize = aps.length;\r
448     final PCAPanel thisPCAPanel = this;\r
449     for (i = 0; i < iSize; i++)\r
450     {\r
451       final AlignmentPanel ap = aps[i];\r
452       item = new JRadioButtonMenuItem(ap.av.viewName, ap.av == rc.av);\r
453       buttonGroup.add(item);\r
454       item.addActionListener(new ActionListener()\r
455       {\r
456         public void actionPerformed(ActionEvent evt)\r
457         {\r
458           rc.applyToAllViews = false;\r
459           rc.av = ap.av;\r
460           rc.ap = ap;\r
461           PaintRefresher.Register(thisPCAPanel, ap.av.getSequenceSetId());\r
462         }\r
463       });\r
464 \r
465       associateViewsMenu.add(item);\r
466     }\r
467 \r
468     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");\r
469 \r
470     buttonGroup.add(itemf);\r
471 \r
472     itemf.setSelected(rc.applyToAllViews);\r
473     itemf.addActionListener(new ActionListener()\r
474     {\r
475       public void actionPerformed(ActionEvent evt)\r
476       {\r
477         rc.applyToAllViews = itemf.isSelected();\r
478       }\r
479     });\r
480     associateViewsMenu.add(itemf);\r
481 \r
482   }\r
483 \r
484 }\r