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