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