From 8720298d1b7d5fa13b95e0769b9370d21673d56d Mon Sep 17 00:00:00 2001 From: MorellThomas Date: Sun, 9 Jul 2023 18:41:58 +0200 Subject: [PATCH] Copied PCA files to PaSiMap analogues and changed all names --- resources/lang/Messages.properties | 4 + resources/lang/Messages_es.properties | 3 + src/jalview/analysis/PaSiMap.java | 302 ++++++++++++ src/jalview/gui/CalculationChooser.java | 6 +- src/jalview/gui/PaSiMapPanel.java | 772 +++++++++++++++++++++++++++++++ src/jalview/viewmodel/PaSiMapModel.java | 271 +++++++++++ 6 files changed, 1355 insertions(+), 3 deletions(-) create mode 100755 src/jalview/analysis/PaSiMap.java create mode 100644 src/jalview/gui/PaSiMapPanel.java create mode 100644 src/jalview/viewmodel/PaSiMapModel.java diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 980e9bc..a6eee7e 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -443,6 +443,7 @@ label.selection_output_command = Selection output - {0} label.annotation_for_displayid =

Annotation for {0}

label.pdb_sequence_mapping = PDB - Sequence Mapping label.pca_details = PCA details +label.pasimap_details = PaSiMap details label.redundancy_threshold_selection = Redundancy threshold selection label.user_defined_colours = User defined colours label.jalviewLite_release = JalviewLite - Release {0} @@ -1004,6 +1005,8 @@ label.add_new_sbrs_service = Add a new Simple Bioinformatics Rest Service label.edit_sbrs_entry = Edit Simple Bioinformatics Rest Service entry label.pca_recalculating = Recalculating PCA label.pca_calculating = Calculating PCA +label.pasimap_recalculating = Recalculating PaSiMap +label.pasimap_calculating = Calculating PaSiMap label.select_foreground_colour = Choose foreground colour label.select_colour_for_text = Select Colour for Text label.adjust_foreground_text_colour_threshold = Adjust Foreground Text Colour Threshold @@ -1330,6 +1333,7 @@ label.annotation_description = Annotation Description label.edit_annotation_name_description = Edit Annotation Name/Description label.alignment = alignment label.pca = PCA +label.pasimap = PaSiMap label.create_image_of = Create {0} image of {1} label.click_to_edit = Click to edit, right-click for menu label.backupfiles_confirm_delete = Confirm delete diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 547d95b..74b3674 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -922,6 +922,8 @@ label.add_new_sbrs_service = A label.edit_sbrs_entry = Editar entrada SBRS label.pca_recalculating = Recalculando ACP label.pca_calculating = Calculando ACP +label.pasimap_recalculating = Recalculando PaSiMap +label.pasimap_calculating = Calculando PaSiMap label.select_foreground_colour = Escoger color del primer plano label.select_colour_for_text = Seleccione el color del texto label.adjust_foreground_text_colour_threshold = Ajustar el umbral del color del texto en primer plano @@ -1320,6 +1322,7 @@ label.annotation_description = Descripci label.edit_annotation_name_description = Editar el nombre/descripción de la anotación label.alignment = alineamiento label.pca = ACP +label.pasimap = PaSiMap label.create_image_of = Crear imagen {0} de {1} label.click_to_edit = Haga clic para editar, clic en el botón derecho para ver el menú label.backupfiles_confirm_delete = Confirmar borrar diff --git a/src/jalview/analysis/PaSiMap.java b/src/jalview/analysis/PaSiMap.java new file mode 100755 index 0000000..7714be4 --- /dev/null +++ b/src/jalview/analysis/PaSiMap.java @@ -0,0 +1,302 @@ +/* + * 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 . + * 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; + } +} diff --git a/src/jalview/gui/CalculationChooser.java b/src/jalview/gui/CalculationChooser.java index 0f6c257..9e596e2 100644 --- a/src/jalview/gui/CalculationChooser.java +++ b/src/jalview/gui/CalculationChooser.java @@ -115,8 +115,8 @@ public class CalculationChooser extends JPanel */ private PCAPanel pcaPanel; - //&! change to PaSiMapPanel - private PCAPanel pasimapPanel; + //&! + private PaSiMapPanel pasimapPanel; /** * Constructor @@ -657,7 +657,7 @@ public class CalculationChooser extends JPanel * construct the panel and kick off its calculation thread */ //&! change to PaSiMapPanel - pasimapPanel = new PCAPanel(af.alignPanel, modelName, params); + pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName, params); new Thread(pasimapPanel).start(); } diff --git a/src/jalview/gui/PaSiMapPanel.java b/src/jalview/gui/PaSiMapPanel.java new file mode 100644 index 0000000..e394781 --- /dev/null +++ b/src/jalview/gui/PaSiMapPanel.java @@ -0,0 +1,772 @@ +/* + * 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 . + * 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()); + } +} diff --git a/src/jalview/viewmodel/PaSiMapModel.java b/src/jalview/viewmodel/PaSiMapModel.java new file mode 100644 index 0000000..94485a3 --- /dev/null +++ b/src/jalview/viewmodel/PaSiMapModel.java @@ -0,0 +1,271 @@ +/* + * 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 . + * 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 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 getSequencePoints() + { + return points; + } + + public void setSequencePoints(List 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; + } +} -- 1.7.10.2