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