b6d3a9ce8bac9b5c51f75bf85f4e6c816cdd599b
[jalview.git] / src / jalview / appletgui / PCAPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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.appletgui;
19
20 import java.util.*;
21
22 import java.awt.*;
23 import java.awt.event.*;
24
25 import jalview.analysis.*;
26 import jalview.datamodel.*;
27 import jalview.viewmodel.PCAModel;
28
29 public class PCAPanel extends EmbmenuFrame implements Runnable,
30         ActionListener, ItemListener
31 {
32   RotatableCanvas rc;
33
34   AlignViewport av;
35
36   PCAModel pcaModel;
37
38   int top = 0;
39
40   public PCAPanel(AlignViewport av)
41   {
42     try
43     {
44       jbInit();
45     } catch (Exception e)
46     {
47       e.printStackTrace();
48     }
49
50     for (int i = 1; i < 8; i++)
51     {
52       xCombobox.addItem("dim " + i);
53       yCombobox.addItem("dim " + i);
54       zCombobox.addItem("dim " + i);
55     }
56
57     this.av = av;
58     boolean selected = av.getSelectionGroup() != null
59             && av.getSelectionGroup().getSize() > 0;
60     AlignmentView seqstrings = av.getAlignmentView(selected);
61     boolean nucleotide = av.getAlignment().isNucleotide();
62     SequenceI[] seqs;
63     if (!selected)
64     {
65       seqs = av.getAlignment().getSequencesArray();
66     }
67     else
68     {
69       seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
70     }
71     SeqCigar sq[] = seqstrings.getSequences();
72     int length = sq[0].getWidth();
73
74     for (int i = 0; i < seqs.length; i++)
75     {
76       if (sq[i].getWidth() != length)
77       {
78         System.out
79                 .println("Sequences must be equal length for PCA analysis");
80         return;
81       }
82     }
83     pcaModel = new PCAModel(seqstrings, seqs, nucleotide);
84
85     rc = new RotatableCanvas(av);
86     embedMenuIfNeeded(rc);
87     add(rc, BorderLayout.CENTER);
88
89     jalview.bin.JalviewLite.addFrame(this, "Principal component analysis",
90             475, 400);
91
92     Thread worker = new Thread(this);
93     worker.start();
94   }
95
96   /**
97    * DOCUMENT ME!
98    */
99   public void run()
100   {
101     // TODO progress indicator
102     calcSettings.setEnabled(false);
103     rc.setEnabled(false);
104     try
105     {
106       nuclSetting.setState(pcaModel.isNucleotide());
107       protSetting.setState(!pcaModel.isNucleotide());
108       pcaModel.run();
109       // ////////////////
110       xCombobox.select(0);
111       yCombobox.select(1);
112       zCombobox.select(2);
113
114       pcaModel.updateRc(rc);
115       // rc.invalidate();
116       top = pcaModel.getTop();
117     } catch (OutOfMemoryError x)
118     {
119       System.err.println("Out of memory when calculating PCA.");
120       return;
121     }
122     calcSettings.setEnabled(true);
123
124     // TODO revert progress indicator
125     rc.setEnabled(true);
126     rc.repaint();
127     this.repaint();
128   }
129
130   void doDimensionChange()
131   {
132     if (top == 0)
133     {
134       return;
135     }
136
137     int dim1 = top - xCombobox.getSelectedIndex();
138     int dim2 = top - yCombobox.getSelectedIndex();
139     int dim3 = top - zCombobox.getSelectedIndex();
140     pcaModel.updateRcView(dim1, dim2, dim3);
141     rc.img = null;
142     rc.rotmat.setIdentity();
143     rc.initAxes();
144     rc.paint(rc.getGraphics());
145   }
146
147   public void actionPerformed(ActionEvent evt)
148   {
149     if (evt.getSource() == inputData)
150     {
151       showOriginalData();
152     }
153     if (evt.getSource() == resetButton)
154     {
155       xCombobox.select(0);
156       yCombobox.select(1);
157       zCombobox.select(2);
158       doDimensionChange();
159     }
160     if (evt.getSource() == values)
161     {
162       values_actionPerformed();
163     }
164   }
165
166   public void itemStateChanged(ItemEvent evt)
167   {
168     if (evt.getSource() == xCombobox)
169     {
170       xCombobox_actionPerformed();
171     }
172     else if (evt.getSource() == yCombobox)
173     {
174       yCombobox_actionPerformed();
175     }
176     else if (evt.getSource() == zCombobox)
177     {
178       zCombobox_actionPerformed();
179     }
180     else if (evt.getSource() == labels)
181     {
182       labels_itemStateChanged(evt);
183     }
184     else if (evt.getSource() == nuclSetting)
185     {
186       if (!pcaModel.isNucleotide())
187       {
188         pcaModel.setNucleotide(true);
189         new Thread(this).start();
190       }
191     }
192     else if (evt.getSource() == protSetting)
193     {
194       if (pcaModel.isNucleotide())
195       {
196         pcaModel.setNucleotide(false);
197         new Thread(this).start();
198       }
199     }
200   }
201
202   protected void xCombobox_actionPerformed()
203   {
204     doDimensionChange();
205   }
206
207   protected void yCombobox_actionPerformed()
208   {
209     doDimensionChange();
210   }
211
212   protected void zCombobox_actionPerformed()
213   {
214     doDimensionChange();
215   }
216
217   public void values_actionPerformed()
218   {
219
220     CutAndPasteTransfer cap = new CutAndPasteTransfer(false, null);
221     Frame frame = new Frame();
222     frame.add(cap);
223     jalview.bin.JalviewLite.addFrame(frame, "PCA details", 500, 500);
224
225     cap.setText(pcaModel.getDetails());
226   }
227
228   void showOriginalData()
229   {
230     // decide if av alignment is sufficiently different to original data to
231     // warrant a new window to be created
232     // create new alignmnt window with hidden regions (unhiding hidden regions
233     // yields unaligned seqs)
234     // or create a selection box around columns in alignment view
235     // test Alignment(SeqCigar[])
236     char gc = '-';
237     try
238     {
239       // we try to get the associated view's gap character
240       // but this may fail if the view was closed...
241       gc = av.getGapCharacter();
242     } catch (Exception ex)
243     {
244     }
245     ;
246     Object[] alAndColsel = pcaModel.getSeqtrings()
247             .getAlignmentAndColumnSelection(gc);
248
249     if (alAndColsel != null && alAndColsel[0] != null)
250     {
251       Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
252       AlignFrame af = new AlignFrame(al, av.applet,
253               "Original Data for PCA", false);
254
255       af.viewport.setHiddenColumns((ColumnSelection) alAndColsel[1]);
256     }
257   }
258
259   public void labels_itemStateChanged(ItemEvent itemEvent)
260   {
261     rc.showLabels(labels.getState());
262   }
263
264   Panel jPanel2 = new Panel();
265
266   Label jLabel1 = new Label();
267
268   Label jLabel2 = new Label();
269
270   Label jLabel3 = new Label();
271
272   protected Choice xCombobox = new Choice();
273
274   protected Choice yCombobox = new Choice();
275
276   protected Choice zCombobox = new Choice();
277
278   protected Button resetButton = new Button();
279
280   FlowLayout flowLayout1 = new FlowLayout();
281
282   BorderLayout borderLayout1 = new BorderLayout();
283
284   MenuBar menuBar1 = new MenuBar();
285
286   Menu menu1 = new Menu();
287
288   Menu menu2 = new Menu();
289
290   Menu calcSettings = new Menu();
291
292   protected CheckboxMenuItem labels = new CheckboxMenuItem();
293
294   protected CheckboxMenuItem protSetting = new CheckboxMenuItem();
295
296   protected CheckboxMenuItem nuclSetting = new CheckboxMenuItem();
297
298   MenuItem values = new MenuItem();
299
300   MenuItem inputData = new MenuItem();
301
302   private void jbInit() throws Exception
303   {
304     this.setLayout(borderLayout1);
305     jPanel2.setLayout(flowLayout1);
306     jLabel1.setFont(new java.awt.Font("Verdana", 0, 12));
307     jLabel1.setText("x=");
308     jLabel2.setFont(new java.awt.Font("Verdana", 0, 12));
309     jLabel2.setText("y=");
310     jLabel3.setFont(new java.awt.Font("Verdana", 0, 12));
311     jLabel3.setText("z=");
312     jPanel2.setBackground(Color.white);
313     zCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
314     zCombobox.addItemListener(this);
315     yCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
316     yCombobox.addItemListener(this);
317     xCombobox.setFont(new java.awt.Font("Verdana", 0, 12));
318     xCombobox.addItemListener(this);
319     resetButton.setFont(new java.awt.Font("Verdana", 0, 12));
320     resetButton.setLabel("Reset");
321     resetButton.addActionListener(this);
322     this.setMenuBar(menuBar1);
323     menu1.setLabel("File");
324     menu2.setLabel("View");
325     calcSettings.setLabel("Change Parameters");
326     labels.setLabel("Labels");
327     labels.addItemListener(this);
328     values.setLabel("Output Values...");
329     values.addActionListener(this);
330     inputData.setLabel("Input Data...");
331     nuclSetting.setLabel("Nucleotide matrix");
332     nuclSetting.addItemListener(this);
333     protSetting.setLabel("Protein matrix");
334     protSetting.addItemListener(this);
335     this.add(jPanel2, BorderLayout.SOUTH);
336     jPanel2.add(jLabel1, null);
337     jPanel2.add(xCombobox, null);
338     jPanel2.add(jLabel2, null);
339     jPanel2.add(yCombobox, null);
340     jPanel2.add(jLabel3, null);
341     jPanel2.add(zCombobox, null);
342     jPanel2.add(resetButton, null);
343     menuBar1.add(menu1);
344     menuBar1.add(menu2);
345     menuBar1.add(calcSettings);
346     menu2.add(labels);
347     menu1.add(values);
348     menu1.add(inputData);
349     calcSettings.add(nuclSetting);
350     calcSettings.add(protSetting);
351     inputData.addActionListener(this);
352   }
353
354 }