--- /dev/null
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.analysis;
+
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Console;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.Point;
+import jalview.math.MatrixI;
+
+import java.io.PrintStream;
+
+/**
+ * Performs Principal Component Analysis on given sequences
+ */
+public class PaSiMap implements Runnable
+{
+ /*
+ * inputs
+ */
+ final private AlignmentView seqs;
+
+ final private ScoreModelI scoreModel;
+
+ final private SimilarityParamsI similarityParams;
+
+ /*
+ * outputs
+ */
+ private MatrixI pairwiseScores;
+
+ private MatrixI tridiagonal;
+
+ private MatrixI eigenMatrix;
+
+ /**
+ * Constructor given the sequences to compute for, the similarity model to
+ * use, and a set of parameters for sequence comparison
+ *
+ * @param sequences
+ * @param sm
+ * @param options
+ */
+ public PaSiMap(AlignmentView sequences, ScoreModelI sm,
+ SimilarityParamsI options)
+ {
+ this.seqs = sequences;
+ this.scoreModel = sm;
+ this.similarityParams = options;
+ }
+
+ /**
+ * Returns Eigenvalue
+ *
+ * @param i
+ * Index of diagonal within matrix
+ *
+ * @return Returns value of diagonal from matrix
+ */
+ public double getEigenvalue(int i)
+ {
+ return eigenMatrix.getD()[i];
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param l
+ * DOCUMENT ME!
+ * @param n
+ * DOCUMENT ME!
+ * @param mm
+ * DOCUMENT ME!
+ * @param factor
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Point[] getComponents(int l, int n, int mm, float factor)
+ {
+ Point[] out = new Point[getHeight()];
+
+ for (int i = 0; i < getHeight(); i++)
+ {
+ float x = (float) component(i, l) * factor;
+ float y = (float) component(i, n) * factor;
+ float z = (float) component(i, mm) * factor;
+ out[i] = new Point(x, y, z);
+ }
+
+ return out;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param n
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public double[] component(int n)
+ {
+ // n = index of eigenvector
+ double[] out = new double[getHeight()];
+
+ for (int i = 0; i < out.length; i++)
+ {
+ out[i] = component(i, n);
+ }
+
+ return out;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param row
+ * DOCUMENT ME!
+ * @param n
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ double component(int row, int n)
+ {
+ double out = 0.0;
+
+ for (int i = 0; i < pairwiseScores.width(); i++)
+ {
+ out += (pairwiseScores.getValue(row, i) * eigenMatrix.getValue(i, n));
+ }
+
+ return out / eigenMatrix.getD()[n];
+ }
+
+ /**
+ * Answers a formatted text report of the PaSiMap calculation results (matrices
+ * and eigenvalues) suitable for display
+ *
+ * @return
+ */
+ public String getDetails()
+ {
+ StringBuilder sb = new StringBuilder(1024);
+ sb.append("PaSiMap calculation using ").append(scoreModel.getName())
+ .append(" sequence similarity matrix\n========\n\n");
+ PrintStream ps = wrapOutputBuffer(sb);
+
+ /*
+ * pairwise similarity scores
+ */
+ sb.append(" --- OrigT * Orig ---- \n");
+ pairwiseScores.print(ps, "%8.2f");
+
+ /*
+ * tridiagonal matrix, with D and E vectors
+ */
+ sb.append(" ---Tridiag transform matrix ---\n");
+ sb.append(" --- D vector ---\n");
+ tridiagonal.printD(ps, "%15.4e");
+ ps.println();
+ sb.append("--- E vector ---\n");
+ tridiagonal.printE(ps, "%15.4e");
+ ps.println();
+
+ /*
+ * eigenvalues matrix, with D vector
+ */
+ sb.append(" --- New diagonalization matrix ---\n");
+ eigenMatrix.print(ps, "%8.2f");
+ sb.append(" --- Eigenvalues ---\n");
+ eigenMatrix.printD(ps, "%15.4e");
+ ps.println();
+
+ return sb.toString();
+ }
+
+ /**
+ * Performs the PaSiMap calculation
+ */
+ @Override
+ public void run()
+ {
+ try
+ {
+ /*
+ * sequence pairwise similarity scores
+ */
+ pairwiseScores = scoreModel.findSimilarities(seqs, similarityParams);
+
+ /*
+ * tridiagonal matrix
+ */
+ tridiagonal = pairwiseScores.copy();
+ tridiagonal.tred();
+
+ /*
+ * the diagonalization matrix
+ */
+ eigenMatrix = tridiagonal.copy();
+ eigenMatrix.tqli();
+ } catch (Exception q)
+ {
+ Console.error("Error computing PaSiMap: " + q.getMessage());
+ q.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns a PrintStream that wraps (appends its output to) the given
+ * StringBuilder
+ *
+ * @param sb
+ * @return
+ */
+ protected PrintStream wrapOutputBuffer(StringBuilder sb)
+ {
+ PrintStream ps = new PrintStream(System.out)
+ {
+ @Override
+ public void print(String x)
+ {
+ sb.append(x);
+ }
+
+ @Override
+ public void println()
+ {
+ sb.append("\n");
+ }
+ };
+ return ps;
+ }
+
+ /**
+ * Answers the N dimensions of the NxN PaSiMap matrix. This is the number of
+ * sequences involved in the pairwise score calculation.
+ *
+ * @return
+ */
+ public int getHeight()
+ {
+ // TODO can any of seqs[] be null?
+ return pairwiseScores.height();// seqs.getSequences().length;
+ }
+
+ /**
+ * Answers the sequence pairwise similarity scores which were the first step
+ * of the PaSiMap calculation
+ *
+ * @return
+ */
+ public MatrixI getPairwiseScores()
+ {
+ return pairwiseScores;
+ }
+
+ public void setPairwiseScores(MatrixI m)
+ {
+ pairwiseScores = m;
+ }
+
+ public MatrixI getEigenmatrix()
+ {
+ return eigenMatrix;
+ }
+
+ public void setEigenmatrix(MatrixI m)
+ {
+ eigenMatrix = m;
+ }
+
+ public MatrixI getTridiagonal()
+ {
+ return tridiagonal;
+ }
+
+ public void setTridiagonal(MatrixI tridiagonal)
+ {
+ this.tridiagonal = tridiagonal;
+ }
+}
--- /dev/null
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.gui;
+
+import jalview.analysis.scoremodels.ScoreModels;
+import jalview.api.AlignViewportI;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.bin.Console;
+import jalview.datamodel.Alignment;
+import jalview.datamodel.AlignmentI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.HiddenColumns;
+import jalview.datamodel.SequenceI;
+import jalview.gui.ImageExporter.ImageWriterI;
+import jalview.gui.JalviewColourChooser.ColourChooserListener;
+import jalview.jbgui.GPCAPanel;
+import jalview.math.RotatableMatrix.Axis;
+import jalview.util.ImageMaker;
+import jalview.util.MessageManager;
+import jalview.viewmodel.AlignmentViewport;
+import jalview.viewmodel.PaSiMapModel;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+
+import javax.swing.ButtonGroup;
+import javax.swing.JMenuItem;
+import javax.swing.JRadioButtonMenuItem;
+import javax.swing.event.InternalFrameAdapter;
+import javax.swing.event.InternalFrameEvent;
+
+/**
+ * The panel holding the Pairwise Similarity Map 3-D visualisation
+ */
+public class PaSiMapPanel extends GPCAPanel
+ implements Runnable, IProgressIndicator
+{
+ private static final int MIN_WIDTH = 470;
+
+ private static final int MIN_HEIGHT = 250;
+
+ private RotatableCanvas rc;
+
+ AlignmentPanel ap;
+
+ AlignmentViewport av;
+
+ private PaSiMapModel pasimapModel;
+
+ private int top = 0;
+
+ private IProgressIndicator progressBar;
+
+ private boolean working;
+
+ /**
+ * Constructor given sequence data, a similarity (or distance) score model
+ * name, and score calculation parameters
+ *
+ * @param alignPanel
+ * @param modelName
+ * @param params
+ */
+ public PaSiMapPanel(AlignmentPanel alignPanel, String modelName,
+ SimilarityParamsI params)
+ {
+ super();
+ this.av = alignPanel.av;
+ this.ap = alignPanel;
+ boolean nucleotide = av.getAlignment().isNucleotide();
+
+ progressBar = new ProgressBar(statusPanel, statusBar);
+
+ addInternalFrameListener(new InternalFrameAdapter()
+ {
+ @Override
+ public void internalFrameClosed(InternalFrameEvent e)
+ {
+ close_actionPerformed();
+ }
+ });
+
+ boolean selected = av.getSelectionGroup() != null
+ && av.getSelectionGroup().getSize() > 0;
+ AlignmentView seqstrings = av.getAlignmentView(selected);
+ SequenceI[] seqs;
+ if (!selected)
+ {
+ seqs = av.getAlignment().getSequencesArray();
+ }
+ else
+ {
+ seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
+ }
+
+ ScoreModelI scoreModel = ScoreModels.getInstance()
+ .getScoreModel(modelName, ap);
+ setPasimapModel(
+ new PaSiMapModel(seqstrings, seqs, nucleotide, scoreModel, params));
+ PaintRefresher.Register(this, av.getSequenceSetId());
+
+ setRotatableCanvas(new RotatableCanvas(alignPanel));
+ this.getContentPane().add(getRotatableCanvas(), BorderLayout.CENTER);
+
+ addKeyListener(getRotatableCanvas());
+ validate();
+ }
+
+ /**
+ * Ensure references to potentially very large objects (the PaSiMap matrices) are
+ * nulled when the frame is closed
+ */
+ protected void close_actionPerformed()
+ {
+ setPasimapModel(null);
+ if (this.rc != null)
+ {
+ this.rc.sequencePoints = null;
+ this.rc.setAxisEndPoints(null);
+ this.rc = null;
+ }
+ }
+
+ @Override
+ protected void bgcolour_actionPerformed()
+ {
+ String ttl = MessageManager.getString("label.select_background_colour");
+ ColourChooserListener listener = new ColourChooserListener()
+ {
+ @Override
+ public void colourSelected(Color c)
+ {
+ rc.setBgColour(c);
+ rc.repaint();
+ }
+ };
+ JalviewColourChooser.showColourChooser(this, ttl, rc.getBgColour(),
+ listener);
+ }
+
+ /**
+ * Calculates the PaSiMap and displays the results
+ */
+ @Override
+ public void run()
+ {
+ working = true;
+ long progId = System.currentTimeMillis();
+ IProgressIndicator progress = this;
+ String message = MessageManager.getString("label.pasimap_recalculating");
+ if (getParent() == null)
+ {
+ progress = ap.alignFrame;
+ message = MessageManager.getString("label.pasimap_calculating");
+ }
+ progress.setProgressBar(message, progId);
+ try
+ {
+ getPasimapModel().calculate();
+
+ xCombobox.setSelectedIndex(0);
+ yCombobox.setSelectedIndex(1);
+ zCombobox.setSelectedIndex(2);
+
+ getPasimapModel().updateRc(getRotatableCanvas());
+ // rc.invalidate();
+ setTop(getPasimapModel().getTop());
+
+ } catch (OutOfMemoryError er)
+ {
+ new OOMWarning("calculating PaSiMap", er);
+ working = false;
+ return;
+ } finally
+ {
+ progress.setProgressBar("", progId);
+ }
+
+ repaint();
+ if (getParent() == null)
+ {
+ Desktop.addInternalFrame(this,
+ MessageManager.formatMessage("label.calc_title", "PaSiMap",
+ getPasimapModel().getScoreModelName()),
+ 475, 450);
+ this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
+ }
+ working = false;
+ }
+
+ /**
+ * Updates the PaSiMap display after a change of component to use for x, y or z
+ * axis
+ */
+ @Override
+ protected void doDimensionChange()
+ {
+ if (getTop() == 0)
+ {
+ return;
+ }
+
+ int dim1 = getTop() - xCombobox.getSelectedIndex();
+ int dim2 = getTop() - yCombobox.getSelectedIndex();
+ int dim3 = getTop() - zCombobox.getSelectedIndex();
+ getPasimapModel().updateRcView(dim1, dim2, dim3);
+ getRotatableCanvas().resetView();
+ }
+
+ /**
+ * Sets the selected checkbox item index for PaSiMap dimension (1, 2, 3...) for
+ * the given axis (X/Y/Z)
+ *
+ * @param index
+ * @param axis
+ */
+ public void setSelectedDimensionIndex(int index, Axis axis)
+ {
+ switch (axis)
+ {
+ case X:
+ xCombobox.setSelectedIndex(index);
+ break;
+ case Y:
+ yCombobox.setSelectedIndex(index);
+ break;
+ case Z:
+ zCombobox.setSelectedIndex(index);
+ break;
+ default:
+ }
+ }
+
+ @Override
+ protected void outputValues_actionPerformed()
+ {
+ CutAndPasteTransfer cap = new CutAndPasteTransfer();
+ try
+ {
+ cap.setText(getPasimapModel().getDetails());
+ Desktop.addInternalFrame(cap,
+ MessageManager.getString("label.pasimap_details"), 500, 500);
+ } catch (OutOfMemoryError oom)
+ {
+ new OOMWarning("opening PaSiMap details", oom);
+ cap.dispose();
+ }
+ }
+
+ @Override
+ protected void showLabels_actionPerformed()
+ {
+ getRotatableCanvas().showLabels(showLabels.getState());
+ }
+
+ @Override
+ protected void print_actionPerformed()
+ {
+ PaSiMapPrinter printer = new PaSiMapPrinter();
+ printer.start();
+ }
+
+ /**
+ * If available, shows the data which formed the inputs for the PaSiMap as a new
+ * alignment
+ */
+ @Override
+ public void originalSeqData_actionPerformed()
+ {
+ // JAL-2647 disabled after load from project (until save to project done)
+ if (getPasimapModel().getInputData() == null)
+ {
+ Console.info(
+ "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
+ return;
+ }
+ // decide if av alignment is sufficiently different to original data to
+ // warrant a new window to be created
+ // create new alignment window with hidden regions (unhiding hidden regions
+ // yields unaligned seqs)
+ // or create a selection box around columns in alignment view
+ // test Alignment(SeqCigar[])
+ char gc = '-';
+ try
+ {
+ // we try to get the associated view's gap character
+ // but this may fail if the view was closed...
+ gc = av.getGapCharacter();
+ } catch (Exception ex)
+ {
+ }
+
+ Object[] alAndColsel = getPasimapModel().getInputData()
+ .getAlignmentAndHiddenColumns(gc);
+
+ if (alAndColsel != null && alAndColsel[0] != null)
+ {
+ // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
+
+ AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
+ AlignmentI dataset = (av != null && av.getAlignment() != null)
+ ? av.getAlignment().getDataset()
+ : null;
+ if (dataset != null)
+ {
+ al.setDataset(dataset);
+ }
+
+ if (true)
+ {
+ // make a new frame!
+ AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
+ AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
+
+ // >>>This is a fix for the moment, until a better solution is
+ // found!!<<<
+ // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
+
+ // af.addSortByOrderMenuItem(ServiceName + " Ordering",
+ // msaorder);
+
+ Desktop.addInternalFrame(af, MessageManager.formatMessage(
+ "label.original_data_for_params", new String[]
+ { this.title }), AlignFrame.DEFAULT_WIDTH,
+ AlignFrame.DEFAULT_HEIGHT);
+ }
+ }
+ /*
+ * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
+ * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
+ * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
+ * "\n"); }
+ *
+ * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
+ */
+ }
+
+ class PaSiMapPrinter extends Thread implements Printable
+ {
+ @Override
+ public void run()
+ {
+ PrinterJob printJob = PrinterJob.getPrinterJob();
+ PageFormat defaultPage = printJob.defaultPage();
+ PageFormat pf = printJob.pageDialog(defaultPage);
+
+ if (defaultPage == pf)
+ {
+ /*
+ * user cancelled
+ */
+ return;
+ }
+
+ printJob.setPrintable(this, pf);
+
+ if (printJob.printDialog())
+ {
+ try
+ {
+ printJob.print();
+ } catch (Exception PrintException)
+ {
+ PrintException.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public int print(Graphics pg, PageFormat pf, int pi)
+ throws PrinterException
+ {
+ pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
+
+ getRotatableCanvas().drawBackground(pg);
+ getRotatableCanvas().drawScene(pg);
+ if (getRotatableCanvas().drawAxes)
+ {
+ getRotatableCanvas().drawAxes(pg);
+ }
+
+ if (pi == 0)
+ {
+ return Printable.PAGE_EXISTS;
+ }
+ else
+ {
+ return Printable.NO_SUCH_PAGE;
+ }
+ }
+ }
+
+ public void makePaSiMapImage(ImageMaker.TYPE type)
+ {
+ int width = getRotatableCanvas().getWidth();
+ int height = getRotatableCanvas().getHeight();
+ ImageWriterI writer = new ImageWriterI()
+ {
+ @Override
+ public void exportImage(Graphics g) throws Exception
+ {
+ RotatableCanvas canvas = getRotatableCanvas();
+ canvas.drawBackground(g);
+ canvas.drawScene(g);
+ if (canvas.drawAxes)
+ {
+ canvas.drawAxes(g);
+ }
+ }
+ };
+ String pasimap = MessageManager.getString("label.pasimap");
+ ImageExporter exporter = new ImageExporter(writer, null, type, pasimap);
+ exporter.doExport(null, this, width, height, pasimap);
+ }
+
+ @Override
+ protected void viewMenu_menuSelected()
+ {
+ buildAssociatedViewMenu();
+ }
+
+ /**
+ * Builds the menu showing the choice of possible views (for the associated
+ * sequence data) to which the PaSiMap may be linked
+ */
+ void buildAssociatedViewMenu()
+ {
+ AlignmentPanel[] aps = PaintRefresher
+ .getAssociatedPanels(av.getSequenceSetId());
+ if (aps.length == 1 && getRotatableCanvas().av == aps[0].av)
+ {
+ associateViewsMenu.setVisible(false);
+ return;
+ }
+
+ associateViewsMenu.setVisible(true);
+
+ if ((viewMenu
+ .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
+ {
+ viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
+ }
+
+ associateViewsMenu.removeAll();
+
+ JRadioButtonMenuItem item;
+ ButtonGroup buttonGroup = new ButtonGroup();
+ int iSize = aps.length;
+
+ for (int i = 0; i < iSize; i++)
+ {
+ final AlignmentPanel panel = aps[i];
+ item = new JRadioButtonMenuItem(panel.av.getViewName(),
+ panel.av == getRotatableCanvas().av);
+ buttonGroup.add(item);
+ item.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent evt)
+ {
+ selectAssociatedView(panel);
+ }
+ });
+
+ associateViewsMenu.add(item);
+ }
+
+ final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
+ "All Views");
+
+ buttonGroup.add(itemf);
+
+ itemf.setSelected(getRotatableCanvas().isApplyToAllViews());
+ itemf.addActionListener(new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent evt)
+ {
+ getRotatableCanvas().setApplyToAllViews(itemf.isSelected());
+ }
+ });
+ associateViewsMenu.add(itemf);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
+ * )
+ */
+ @Override
+ protected void outputPoints_actionPerformed()
+ {
+ CutAndPasteTransfer cap = new CutAndPasteTransfer();
+ try
+ {
+ cap.setText(getPasimapModel().getPointsasCsv(false,
+ xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
+ zCombobox.getSelectedIndex()));
+ Desktop.addInternalFrame(cap, MessageManager
+ .formatMessage("label.points_for_params", new String[]
+ { this.getTitle() }), 500, 500);
+ } catch (OutOfMemoryError oom)
+ {
+ new OOMWarning("exporting PaSiMap points", oom);
+ cap.dispose();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
+ * .ActionEvent)
+ */
+ @Override
+ protected void outputProjPoints_actionPerformed()
+ {
+ CutAndPasteTransfer cap = new CutAndPasteTransfer();
+ try
+ {
+ cap.setText(getPasimapModel().getPointsasCsv(true,
+ xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
+ zCombobox.getSelectedIndex()));
+ Desktop.addInternalFrame(cap, MessageManager.formatMessage(
+ "label.transformed_points_for_params", new String[]
+ { this.getTitle() }), 500, 500);
+ } catch (OutOfMemoryError oom)
+ {
+ new OOMWarning("exporting transformed PaSiMap points", oom);
+ cap.dispose();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
+ */
+ @Override
+ public void setProgressBar(String message, long id)
+ {
+ progressBar.setProgressBar(message, id);
+ // if (progressBars == null)
+ // {
+ // progressBars = new Hashtable();
+ // progressBarHandlers = new Hashtable();
+ // }
+ //
+ // JPanel progressPanel;
+ // Long lId = Long.valueOf(id);
+ // GridLayout layout = (GridLayout) statusPanel.getLayout();
+ // if (progressBars.get(lId) != null)
+ // {
+ // progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
+ // statusPanel.remove(progressPanel);
+ // progressBars.remove(lId);
+ // progressPanel = null;
+ // if (message != null)
+ // {
+ // statusBar.setText(message);
+ // }
+ // if (progressBarHandlers.contains(lId))
+ // {
+ // progressBarHandlers.remove(lId);
+ // }
+ // layout.setRows(layout.getRows() - 1);
+ // }
+ // else
+ // {
+ // progressPanel = new JPanel(new BorderLayout(10, 5));
+ //
+ // JProgressBar progressBar = new JProgressBar();
+ // progressBar.setIndeterminate(true);
+ //
+ // progressPanel.add(new JLabel(message), BorderLayout.WEST);
+ // progressPanel.add(progressBar, BorderLayout.CENTER);
+ //
+ // layout.setRows(layout.getRows() + 1);
+ // statusPanel.add(progressPanel);
+ //
+ // progressBars.put(lId, progressPanel);
+ // }
+ // // update GUI
+ // // setMenusForViewport();
+ // validate();
+ }
+
+ @Override
+ public void registerHandler(final long id,
+ final IProgressIndicatorHandler handler)
+ {
+ progressBar.registerHandler(id, handler);
+ // if (progressBarHandlers == null ||
+ // !progressBars.contains(Long.valueOf(id)))
+ // {
+ // throw new
+ // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
+ // }
+ // progressBarHandlers.put(Long.valueOf(id), handler);
+ // final JPanel progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
+ // if (handler.canCancel())
+ // {
+ // JButton cancel = new JButton(
+ // MessageManager.getString("action.cancel"));
+ // final IProgressIndicator us = this;
+ // cancel.addActionListener(new ActionListener()
+ // {
+ //
+ // @Override
+ // public void actionPerformed(ActionEvent e)
+ // {
+ // handler.cancelActivity(id);
+ // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
+ // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
+ // }
+ // });
+ // progressPanel.add(cancel, BorderLayout.EAST);
+ // }
+ }
+
+ /**
+ *
+ * @return true if any progress bars are still active
+ */
+ @Override
+ public boolean operationInProgress()
+ {
+ return progressBar.operationInProgress();
+ }
+
+ @Override
+ protected void resetButton_actionPerformed()
+ {
+ int t = getTop();
+ setTop(0); // ugly - prevents dimensionChanged events from being processed
+ xCombobox.setSelectedIndex(0);
+ yCombobox.setSelectedIndex(1);
+ setTop(t);
+ zCombobox.setSelectedIndex(2);
+ }
+
+ /**
+ * Answers true if PaSiMap calculation is in progress, else false
+ *
+ * @return
+ */
+ public boolean isWorking()
+ {
+ return working;
+ }
+
+ /**
+ * Answers the selected checkbox item index for PaSiMap dimension for the X, Y or
+ * Z axis of the display
+ *
+ * @param axis
+ * @return
+ */
+ public int getSelectedDimensionIndex(Axis axis)
+ {
+ switch (axis)
+ {
+ case X:
+ return xCombobox.getSelectedIndex();
+ case Y:
+ return yCombobox.getSelectedIndex();
+ default:
+ return zCombobox.getSelectedIndex();
+ }
+ }
+
+ public void setShowLabels(boolean show)
+ {
+ showLabels.setSelected(show);
+ }
+
+ /**
+ * Sets the input data used to calculate the PaSiMap. This is provided for
+ * 'restore from project', which does not currently support this (AL-2647), so
+ * sets the value to null, and hides the menu option for "Input Data...". J
+ *
+ * @param data
+ */
+ public void setInputData(AlignmentView data)
+ {
+ getPasimapModel().setInputData(data);
+ originalSeqData.setVisible(data != null);
+ }
+
+ public AlignViewportI getAlignViewport()
+ {
+ return av;
+ }
+
+ public PaSiMapModel getPasimapModel()
+ {
+ return pasimapModel;
+ }
+
+ public void setPasimapModel(PaSiMapModel pasimapModel)
+ {
+ this.pasimapModel = pasimapModel;
+ }
+
+ public RotatableCanvas getRotatableCanvas()
+ {
+ return rc;
+ }
+
+ public void setRotatableCanvas(RotatableCanvas rc)
+ {
+ this.rc = rc;
+ }
+
+ public int getTop()
+ {
+ return top;
+ }
+
+ public void setTop(int top)
+ {
+ this.top = top;
+ }
+
+ /**
+ * set the associated view for this PaSiMap.
+ *
+ * @param panel
+ */
+ public void selectAssociatedView(AlignmentPanel panel)
+ {
+ getRotatableCanvas().setApplyToAllViews(false);
+
+ ap = panel;
+ av = panel.av;
+
+ getRotatableCanvas().av = panel.av;
+ getRotatableCanvas().ap = panel;
+ PaintRefresher.Register(PaSiMapPanel.this, panel.av.getSequenceSetId());
+ }
+}
--- /dev/null
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.viewmodel;
+
+import jalview.analysis.PaSiMap;
+import jalview.api.RotatableCanvasI;
+import jalview.api.analysis.ScoreModelI;
+import jalview.api.analysis.SimilarityParamsI;
+import jalview.datamodel.AlignmentView;
+import jalview.datamodel.Point;
+import jalview.datamodel.SequenceI;
+import jalview.datamodel.SequencePoint;
+
+import java.util.List;
+import java.util.Vector;
+
+public class PaSiMapModel
+{
+ /*
+ * inputs
+ */
+ private AlignmentView inputData;
+
+ private final SequenceI[] seqs;
+
+ private final SimilarityParamsI similarityParams;
+
+ /*
+ * options - score model, nucleotide / protein
+ */
+ private ScoreModelI scoreModel;
+
+ private boolean nucleotide = false;
+
+ /*
+ * outputs
+ */
+ private PaSiMap pasimap;
+
+ int top;
+
+ private List<SequencePoint> points;
+
+ /**
+ * Constructor given sequence data, score model and score calculation
+ * parameter options.
+ *
+ * @param seqData
+ * @param sqs
+ * @param nuc
+ * @param modelName
+ * @param params
+ */
+ public PaSiMapModel(AlignmentView seqData, SequenceI[] sqs, boolean nuc,
+ ScoreModelI modelName, SimilarityParamsI params)
+ {
+ inputData = seqData;
+ seqs = sqs;
+ nucleotide = nuc;
+ scoreModel = modelName;
+ similarityParams = params;
+ }
+
+ /**
+ * Performs the PaSiMap calculation (in the same thread) and extracts result data
+ * needed for visualisation by PaSiMapPanel
+ */
+ public void calculate()
+ {
+ pasimap = new PaSiMap(inputData, scoreModel, similarityParams);
+ pasimap.run(); // executes in same thread, wait for completion
+
+ // Now find the component coordinates
+ int ii = 0;
+
+ while ((ii < seqs.length) && (seqs[ii] != null))
+ {
+ ii++;
+ }
+
+ int height = pasimap.getHeight();
+ // top = pasimap.getM().height() - 1;
+ top = height - 1;
+
+ points = new Vector<>();
+ Point[] scores = pasimap.getComponents(top - 1, top - 2, top - 3, 100);
+
+ for (int i = 0; i < height; i++)
+ {
+ SequencePoint sp = new SequencePoint(seqs[i], scores[i]);
+ points.add(sp);
+ }
+ }
+
+ public void updateRc(RotatableCanvasI rc)
+ {
+ rc.setPoints(points, pasimap.getHeight());
+ }
+
+ public boolean isNucleotide()
+ {
+ return nucleotide;
+ }
+
+ public void setNucleotide(boolean nucleotide)
+ {
+ this.nucleotide = nucleotide;
+ }
+
+ /**
+ * Answers the index of the principal dimension of the PaSiMap
+ *
+ * @return
+ */
+ public int getTop()
+ {
+ return top;
+ }
+
+ public void setTop(int t)
+ {
+ top = t;
+ }
+
+ /**
+ * Updates the 3D coordinates for the list of points to the given dimensions.
+ * Principal dimension is getTop(). Next greatest eigenvector is getTop()-1.
+ * Note - pasimap.getComponents starts counting the spectrum from rank-2 to zero,
+ * rather than rank-1, so getComponents(dimN ...) == updateRcView(dimN+1 ..)
+ *
+ * @param dim1
+ * @param dim2
+ * @param dim3
+ */
+ public void updateRcView(int dim1, int dim2, int dim3)
+ {
+ // note: actual indices for components are dim1-1, etc (patch for JAL-1123)
+ Point[] scores = pasimap.getComponents(dim1 - 1, dim2 - 1, dim3 - 1, 100);
+
+ for (int i = 0; i < pasimap.getHeight(); i++)
+ {
+ points.get(i).coord = scores[i];
+ }
+ }
+
+ public String getDetails()
+ {
+ return pasimap.getDetails();
+ }
+
+ public AlignmentView getInputData()
+ {
+ return inputData;
+ }
+
+ public void setInputData(AlignmentView data)
+ {
+ inputData = data;
+ }
+
+ public String getPointsasCsv(boolean transformed, int xdim, int ydim,
+ int zdim)
+ {
+ StringBuffer csv = new StringBuffer();
+ csv.append("\"Sequence\"");
+ if (transformed)
+ {
+ csv.append(",");
+ csv.append(xdim);
+ csv.append(",");
+ csv.append(ydim);
+ csv.append(",");
+ csv.append(zdim);
+ }
+ else
+ {
+ for (int d = 1, dmax = pasimap.component(1).length; d <= dmax; d++)
+ {
+ csv.append("," + d);
+ }
+ }
+ csv.append("\n");
+ for (int s = 0; s < seqs.length; s++)
+ {
+ csv.append("\"" + seqs[s].getName() + "\"");
+ double fl[];
+ if (!transformed)
+ {
+ // output pasimap in correct order
+ fl = pasimap.component(s);
+ for (int d = fl.length - 1; d >= 0; d--)
+ {
+ csv.append(",");
+ csv.append(fl[d]);
+ }
+ }
+ else
+ {
+ Point p = points.get(s).coord;
+ csv.append(",").append(p.x);
+ csv.append(",").append(p.y);
+ csv.append(",").append(p.z);
+ }
+ csv.append("\n");
+ }
+ return csv.toString();
+ }
+
+ public String getScoreModelName()
+ {
+ return scoreModel == null ? "" : scoreModel.getName();
+ }
+
+ public void setScoreModel(ScoreModelI sm)
+ {
+ this.scoreModel = sm;
+ }
+
+ /**
+ * Answers the parameters configured for pairwise similarity calculations
+ *
+ * @return
+ */
+ public SimilarityParamsI getSimilarityParameters()
+ {
+ return similarityParams;
+ }
+
+ public List<SequencePoint> getSequencePoints()
+ {
+ return points;
+ }
+
+ public void setSequencePoints(List<SequencePoint> sp)
+ {
+ points = sp;
+ }
+
+ /**
+ * Answers the object holding the values of the computed PaSiMap
+ *
+ * @return
+ */
+ public PaSiMap getPasimapData()
+ {
+ return pasimap;
+ }
+
+ public void setPaSiMap(PaSiMap data)
+ {
+ pasimap = data;
+ }
+}