From 2feac6dabe64b144783f8538fab352e987eadcb6 Mon Sep 17 00:00:00 2001 From: kiramt Date: Wed, 29 Mar 2017 10:53:39 +0100 Subject: [PATCH] JAL-2388 New rendering code, extensions to support hiding hidden cols --- resources/lang/Messages.properties | 1 + resources/lang/Messages_es.properties | 3 +- src/jalview/appletgui/OverviewPanel.java | 6 +- src/jalview/datamodel/AlignmentColsCollection.java | 51 ++++ src/jalview/datamodel/AlignmentRowsCollection.java | 62 +++++ src/jalview/datamodel/HiddenSequences.java | 16 ++ src/jalview/gui/OverviewCanvas.java | 126 ++-------- src/jalview/gui/OverviewPanel.java | 77 +++++- src/jalview/gui/OverviewRenderer.java | 180 ++++++++++++++ src/jalview/viewmodel/OverviewDimensions.java | 262 +++----------------- .../viewmodel/OverviewDimensionsAllVisible.java | 165 ++++++++++++ .../viewmodel/OverviewDimensionsWithHidden.java | 239 ++++++++++++++++++ src/jalview/viewmodel/ViewportRanges.java | 17 ++ test/jalview/viewmodel/OverviewDimensionsTest.java | 16 +- 14 files changed, 868 insertions(+), 353 deletions(-) create mode 100644 src/jalview/datamodel/AlignmentColsCollection.java create mode 100644 src/jalview/datamodel/AlignmentRowsCollection.java create mode 100644 src/jalview/gui/OverviewRenderer.java create mode 100644 src/jalview/viewmodel/OverviewDimensionsAllVisible.java create mode 100644 src/jalview/viewmodel/OverviewDimensionsWithHidden.java diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index d6d3034..c70d1be 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1296,3 +1296,4 @@ warn.name_cannot_be_duplicate = User-defined URL names must be unique and cannot label.invalid_name = Invalid Name ! label.output_seq_details = Output Sequence Details to list all database references label.urllinks = Links +label.togglehidden = Toggle hidden columns on/off \ No newline at end of file diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 6b8761e..f98a45a 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1288,4 +1288,5 @@ label.edit_sequence_url_link = Editar link de secuencia URL warn.name_cannot_be_duplicate = Los nombres URL definidos por el usuario deben ser únicos y no pueden ser ids de MIRIAM label.invalid_name = Nombre inválido ! label.output_seq_details = Seleccionar Detalles de la secuencia para ver todas -label.urllinks = Enlaces \ No newline at end of file +label.urllinks = Enlaces +label.togglehidden = Toggle hidden columns on/off \ No newline at end of file diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index d64c72f..de192fa 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -20,7 +20,7 @@ */ package jalview.appletgui; -import jalview.viewmodel.OverviewDimensions; +import jalview.viewmodel.OverviewDimensionsWithHidden; import java.awt.BorderLayout; import java.awt.Dimension; @@ -34,7 +34,7 @@ import java.awt.event.MouseMotionListener; public class OverviewPanel extends Panel implements Runnable, MouseMotionListener, MouseListener { - private OverviewDimensions od; + private OverviewDimensionsWithHidden od; private OverviewCanvas oviewCanvas; @@ -50,7 +50,7 @@ public class OverviewPanel extends Panel implements Runnable, this.ap = alPanel; setLayout(null); - od = new OverviewDimensions(av.getRanges(), + od = new OverviewDimensionsWithHidden(av.getRanges(), (av.isShowAnnotation() && av.getSequenceConsensusHash() != null)); oviewCanvas = new OverviewCanvas(od, av); diff --git a/src/jalview/datamodel/AlignmentColsCollection.java b/src/jalview/datamodel/AlignmentColsCollection.java new file mode 100644 index 0000000..3a9f598 --- /dev/null +++ b/src/jalview/datamodel/AlignmentColsCollection.java @@ -0,0 +1,51 @@ +/* + * 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.datamodel; + +import java.util.Iterator; + +public class AlignmentColsCollection implements Iterable +{ + int start; + int end; + ColumnSelection hidden; + + public AlignmentColsCollection(int s, int e, ColumnSelection colsel) + { + start = s; + end = e; + hidden = colsel; + } + + @Override + public Iterator iterator() + { + return new AllColsIterator(start,end,hidden); + } + + /** + * Answers if the column at the the current position is hidden. + */ + public boolean isHidden(int c) + { + return !hidden.isVisible(c); + } +} diff --git a/src/jalview/datamodel/AlignmentRowsCollection.java b/src/jalview/datamodel/AlignmentRowsCollection.java new file mode 100644 index 0000000..e22d8fc --- /dev/null +++ b/src/jalview/datamodel/AlignmentRowsCollection.java @@ -0,0 +1,62 @@ +/* + * 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.datamodel; + +import java.util.Iterator; + +public class AlignmentRowsCollection implements Iterable +{ + int start; + + int end; + + AlignmentI alignment; + + HiddenSequences hidden; + + public AlignmentRowsCollection(int s, int e, AlignmentI al) + { + start = s; + end = e; + alignment = al; + hidden = al.getHiddenSequences(); + } + + @Override + public Iterator iterator() + { + return new AllRowsIterator(start, end, alignment); + } + + /** + * Answers if the sequence at the position is hidden. + */ + public boolean isHidden(int seq) + { + return hidden.isHidden(seq); + } + + public SequenceI getSequence(int seq) + { + return alignment.getSequenceAtAbsoluteIndex(seq); + } +} + diff --git a/src/jalview/datamodel/HiddenSequences.java b/src/jalview/datamodel/HiddenSequences.java index 6950c28..1daaf43 100755 --- a/src/jalview/datamodel/HiddenSequences.java +++ b/src/jalview/datamodel/HiddenSequences.java @@ -403,4 +403,20 @@ public class HiddenSequences return false; } + + /** + * Answers if a sequence is hidden + * + * @param seq + * (absolute) index to test + * @return true if sequence at index seq is hidden + */ + public boolean isHidden(int seq) + { + if (hiddenSequences != null) + { + return (hiddenSequences[seq] != null); + } + return false; + } } diff --git a/src/jalview/gui/OverviewCanvas.java b/src/jalview/gui/OverviewCanvas.java index 3ced9ee..e4c2056 100644 --- a/src/jalview/gui/OverviewCanvas.java +++ b/src/jalview/gui/OverviewCanvas.java @@ -21,8 +21,6 @@ package jalview.gui; import jalview.api.AlignViewportI; -import jalview.datamodel.SequenceI; -import jalview.renderer.AnnotationRenderer; import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.viewmodel.OverviewDimensions; @@ -54,8 +52,6 @@ public class OverviewCanvas extends JComponent private jalview.renderer.seqfeatures.FeatureRenderer fr; - private final AnnotationRenderer renderer = new AnnotationRenderer(); - OverviewDimensions od; AlignViewportI av; @@ -72,6 +68,11 @@ public class OverviewCanvas extends JComponent fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); } + public void resetOviewDims(OverviewDimensions overviewDims) + { + od = overviewDims; + } + /* * Signals to drawing code that the associated alignment viewport * has changed and a redraw will be required @@ -101,47 +102,25 @@ public class OverviewCanvas extends JComponent { fr.transferSettings(transferRenderer); } + FeatureColourFinder finder = new FeatureColourFinder(fr); // why do we need to set preferred size again? was set in // updateOverviewImage setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); - miniMe = new BufferedImage(od.getWidth(), od.getHeight(), - BufferedImage.TYPE_INT_RGB); + OverviewRenderer or = new OverviewRenderer(sr, finder, od); + miniMe = or.draw(od.getRows(av.getRanges(), av.getAlignment()), + od.getColumns(av.getRanges(), av.getColumnSelection())); Graphics mg = miniMe.getGraphics(); - mg.setColor(Color.orange); - mg.fillRect(0, 0, od.getWidth(), miniMe.getHeight()); - - // calculate sampleCol and sampleRow - // alignment width is max number of residues/bases - // alignment height is number of sequences - int alwidth = av.getAlignment().getWidth(); - int alheight = av.getAlignment().getAbsoluteHeight(); - - // sampleCol or sampleRow is the width/height allocated to each residue - // in particular, sometimes we may need more than one row/col of the - // BufferedImage allocated - // sampleCol is how much of a residue to assign to each pixel - // sampleRow is how many sequences to assign to each pixel - float sampleCol = alwidth / (float) od.getWidth(); - float sampleRow = alheight / (float) od.getSequencesHeight(); - - buildImage(sampleRow, sampleCol); if (showAnnotation) { - renderer.updateFromAlignViewport(av); - for (int col = 0; col < od.getWidth() && !restart; col++) - { - mg.translate(col, od.getSequencesHeight()); - renderer.drawGraph(mg, av.getAlignmentConservationAnnotation(), - av.getAlignmentConservationAnnotation().annotations, - (int) (sampleCol) + 1, od.getGraphHeight(), - (int) (col * sampleCol), (int) (col * sampleCol) + 1); - mg.translate(-col, -od.getSequencesHeight()); - - } + mg.translate(0, od.getSequencesHeight()); + or.drawGraph(mg, av.getAlignmentConservationAnnotation(), + av.getCharWidth(), od.getGraphHeight(), + od.getColumns(av.getRanges(), av.getColumnSelection())); + mg.translate(0, -od.getSequencesHeight()); } System.gc(); @@ -188,81 +167,4 @@ public class OverviewCanvas extends JComponent od.drawBox(g); } - /* - * Build the overview panel image - */ - private void buildImage(float sampleRow, float sampleCol) - { - int lastcol = -1; - int lastrow = -1; - int rgbcolor = Color.white.getRGB(); - - SequenceI seq = null; - FeatureColourFinder finder = new FeatureColourFinder(fr); - - final boolean hasHiddenCols = av.hasHiddenColumns(); - boolean hiddenRow = false; - // get hidden row and hidden column map once at beginning. - // clone featureRenderer settings to avoid race conditions... if state is - // updated just need to refresh again - for (int row = 0; row < od.getSequencesHeight() && !restart; row++) - { - boolean doCopy = true; - int currentrow = (int) (row * sampleRow); - if (currentrow != lastrow) - { - doCopy = false; - - lastrow = currentrow; - - // get the sequence which would be at alignment index 'lastrow' if no - // rows were hidden, and determine whether it is hidden or not - hiddenRow = av.getAlignment().isHidden(lastrow); - seq = av.getAlignment().getSequenceAtAbsoluteIndex(lastrow); - } - - for (int col = 0; col < od.getWidth() && !restart; col++) - { - if (doCopy) - { - rgbcolor = miniMe.getRGB(col, row - 1); - } - else if ((int) (col * sampleCol) != lastcol - || (int) (row * sampleRow) != lastrow) - { - lastcol = (int) (col * sampleCol); - rgbcolor = getColumnColourFromSequence(seq, hiddenRow, - hasHiddenCols, lastcol, finder); - } - // else we just use the color we already have , so don't need to set it - - miniMe.setRGB(col, row, rgbcolor); - } - } - } - - /* - * Find the colour of a sequence at a specified column position - */ - private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq, - boolean hiddenRow, boolean hasHiddenCols, int lastcol, - FeatureColourFinder finder) - { - Color color = Color.white; - - if ((seq != null) && (seq.getLength() > lastcol)) - { - color = sr.getResidueColour(seq, lastcol, finder); - } - - if (hiddenRow - || (hasHiddenCols && !av.getColumnSelection() - .isVisible(lastcol))) - { - color = color.darker().darker(); - } - - return color.getRGB(); - } - } diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 68b4dc5..8a416bd 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -20,17 +20,26 @@ */ package jalview.gui; +import jalview.util.MessageManager; +import jalview.util.Platform; import jalview.viewmodel.OverviewDimensions; +import jalview.viewmodel.OverviewDimensionsAllVisible; +import jalview.viewmodel.OverviewDimensionsWithHidden; import java.awt.BorderLayout; import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; /** * Panel displaying an overview of the full alignment, with an interactive box @@ -49,6 +58,8 @@ public class OverviewPanel extends JPanel implements Runnable private AlignmentPanel ap; + private boolean showHidden = true; + /** * Creates a new OverviewPanel object. * @@ -60,7 +71,7 @@ public class OverviewPanel extends JPanel implements Runnable this.av = alPanel.av; this.ap = alPanel; - od = new OverviewDimensions(av.getRanges(), + od = new OverviewDimensionsWithHidden(av.getRanges(), (av.isShowAnnotation() && av .getAlignmentConservationAnnotation() != null)); @@ -86,7 +97,8 @@ public class OverviewPanel extends JPanel implements Runnable @Override public void mouseDragged(MouseEvent evt) { - if (!av.getWrapAlignment()) + if (!SwingUtilities.isRightMouseButton(evt) + && !av.getWrapAlignment()) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av @@ -101,7 +113,14 @@ public class OverviewPanel extends JPanel implements Runnable @Override public void mousePressed(MouseEvent evt) { - if (!av.getWrapAlignment()) + if (SwingUtilities.isRightMouseButton(evt)) + { + if (!Platform.isAMac()) + { + showPopupMenu(evt); + } + } + else if (!av.getWrapAlignment()) { od.updateViewportFromMouse(evt.getX(), evt.getY(), av .getAlignment().getHiddenSequences(), av @@ -109,8 +128,60 @@ public class OverviewPanel extends JPanel implements Runnable ap.setScrollValues(od.getScrollCol(), od.getScrollRow()); } } + + @Override + public void mouseClicked(MouseEvent evt) + { + if (SwingUtilities.isRightMouseButton(evt)) + { + showPopupMenu(evt); + } + } }); + + updateOverviewImage(); + } + + /* + * Displays the popup menu and acts on user input + */ + private void showPopupMenu(MouseEvent e) + { + JPopupMenu popup = new JPopupMenu(); + ActionListener menuListener = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent event) + { + // switch on/off the hidden columns view + toggleHiddenColumns(); + } + }; + JMenuItem item; + popup.add(item = new JMenuItem(MessageManager + .getString("label.togglehidden"))); + item.addActionListener(menuListener); + popup.show(this, e.getX(), e.getY()); + } + + private void toggleHiddenColumns() + { + if (showHidden) + { + showHidden = false; + od = new OverviewDimensionsAllVisible(av.getRanges(), + (av.isShowAnnotation() && av + .getAlignmentConservationAnnotation() != null)); + } + else + { + showHidden = true; + od = new OverviewDimensionsWithHidden(av.getRanges(), + (av.isShowAnnotation() && av + .getAlignmentConservationAnnotation() != null)); + } + oviewCanvas.resetOviewDims(od); updateOverviewImage(); } diff --git a/src/jalview/gui/OverviewRenderer.java b/src/jalview/gui/OverviewRenderer.java new file mode 100644 index 0000000..87937ba --- /dev/null +++ b/src/jalview/gui/OverviewRenderer.java @@ -0,0 +1,180 @@ +/* + * 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.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentColsCollection; +import jalview.datamodel.AlignmentRowsCollection; +import jalview.datamodel.Annotation; +import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; +import jalview.viewmodel.OverviewDimensions; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +public class OverviewRenderer +{ + private FeatureColourFinder finder; + + private SequenceRenderer sr; + + // image to render on + private BufferedImage miniMe; + + // raw number of pixels to allocate to each column + private float pixelsPerCol; + + // raw number of pixels to allocate to each row + private float pixelsPerSeq; + + public OverviewRenderer(SequenceRenderer seqRenderer, + FeatureColourFinder colfinder, OverviewDimensions od) + { + sr = seqRenderer; + finder = colfinder; + + pixelsPerCol = od.getPixelsPerCol(); + pixelsPerSeq = od.getPixelsPerSeq(); + miniMe = new BufferedImage(od.getWidth(), od.getHeight(), + BufferedImage.TYPE_INT_RGB); + } + + /** + * Draw alignment rows and columns onto an image + * + * @param rit + * Iterator over rows to be drawn + * @param cit + * Iterator over columns to be drawn + * @return image containing the drawing + */ + public BufferedImage draw(AlignmentRowsCollection rows, + AlignmentColsCollection cols) + { + int rgbcolor = Color.white.getRGB(); + int seqIndex = 0; + int pixelRow = 0; + for (int alignmentRow : rows) + { + // get details of this alignment row + boolean hidden = rows.isHidden(alignmentRow); + SequenceI seq = rows.getSequence(alignmentRow); + + // calculate where this row extends to in pixels + int endRow = Math.min(Math.round((seqIndex + 1) * pixelsPerSeq) - 1, + miniMe.getHeight() - 1); + + int colIndex = 0; + int pixelCol = 0; + for (int alignmentCol : cols) + { + // calculate where this column extends to in pixels + int endCol = Math.min( + Math.round((colIndex + 1) * pixelsPerCol) - 1, + miniMe.getWidth() - 1); + + // determine the colour based on the sequence and column position + rgbcolor = getColumnColourFromSequence(seq, + hidden || cols.isHidden(alignmentCol), alignmentCol, finder); + + // fill in the appropriate number of pixels + for (int row = pixelRow; row <= endRow; ++row) + { + for (int col = pixelCol; col <= endCol; ++col) + { + miniMe.setRGB(col, row, rgbcolor); + } + } + + pixelCol = endCol + 1; + colIndex++; + } + pixelRow = endRow + 1; + seqIndex++; + } + return miniMe; + } + + /* + * Find the colour of a sequence at a specified column position + */ + private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq, + boolean isHidden, int lastcol, FeatureColourFinder finder) + { + Color color = Color.white; + + if ((seq != null) && (seq.getLength() > lastcol)) + { + color = sr.getResidueColour(seq, lastcol, finder); + } + + if (isHidden) + { + color = color.darker().darker(); + } + + return color.getRGB(); + } + + public void drawGraph(Graphics g, AlignmentAnnotation _aa, int charWidth, + int y, AlignmentColsCollection cols) + { + Annotation[] aa_annotations = _aa.annotations; + g.setColor(Color.white); + g.fillRect(0, 0, miniMe.getWidth(), y); + // g.setColor(new Color(0, 0, 180)); + + int height; + + int colIndex = 0; + int pixelCol = 0; + for (int alignmentCol : cols) + { + int endCol = Math.min(Math.round((colIndex + 1) * pixelsPerCol) - 1, + miniMe.getWidth() - 1); + + if (alignmentCol < aa_annotations.length + && aa_annotations[alignmentCol] != null) + { + if (aa_annotations[alignmentCol].colour == null) + { + g.setColor(Color.black); + } + else + { + g.setColor(aa_annotations[alignmentCol].colour); + } + + height = (int) ((aa_annotations[alignmentCol].value / _aa.graphMax) * y); + if (height > y) + { + height = y; + } + + g.fillRect(pixelCol, y - height, endCol - pixelCol + 1, height); + } + pixelCol = endCol + 1; + colIndex++; + } + } +} diff --git a/src/jalview/viewmodel/OverviewDimensions.java b/src/jalview/viewmodel/OverviewDimensions.java index 43680b5..50e1210 100644 --- a/src/jalview/viewmodel/OverviewDimensions.java +++ b/src/jalview/viewmodel/OverviewDimensions.java @@ -1,79 +1,31 @@ -/* - * 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.datamodel.AlignmentColsCollection; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentRowsCollection; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenSequences; import java.awt.Graphics; -public class OverviewDimensions +public abstract class OverviewDimensions { - // Default width and height values - private static final int DEFAULT_GRAPH_HEIGHT = 20; - - private static final int MAX_WIDTH = 400; - - private static final int MIN_WIDTH = 120; - - private static final int MIN_SEQ_HEIGHT = 40; - - private static final int MAX_SEQ_HEIGHT = 300; - - // width of the overview panel - private int width; - - // height of sequences part of the overview panel - private int sequencesHeight; - - // height of the graphs part of the overview panel - private int graphHeight = DEFAULT_GRAPH_HEIGHT; - - // dimensions of box outlining current extent of view in alignment panel - // location of left side of box - private int boxX = -1; - - // location of bottom of box - private int boxY = -1; - // width of box - private int boxWidth = -1; - - // height of box - private int boxHeight = -1; - - // scroll position in viewport corresponding to boxX - private int scrollCol = -1; - - // scroll position in viewport corresponding to boxY - private int scrollRow = -1; + private static final int DEFAULT_GRAPH_HEIGHT = 20; + protected static final int MAX_WIDTH = 400; + protected static final int MIN_WIDTH = 120; + protected static final int MIN_SEQ_HEIGHT = 40; + protected static final int MAX_SEQ_HEIGHT = 300; + protected int width; + protected int sequencesHeight; + protected int graphHeight = DEFAULT_GRAPH_HEIGHT; + protected int boxX = -1; + protected int boxY = -1; + protected int boxWidth = -1; + protected int boxHeight = -1; + protected int scrollCol = -1; + protected int scrollRow = -1; - /** - * Create an OverviewDimensions object - * - * @param ranges - * positional properties of the viewport - * @param showAnnotationPanel - * true if the annotation panel is to be shown, false otherwise - */ public OverviewDimensions(ViewportRanges ranges, boolean showAnnotationPanel) { @@ -111,159 +63,6 @@ public class OverviewDimensions } /** - * Check box dimensions and scroll positions and correct if necessary - * - * @param mousex - * x position in overview panel - * @param mousey - * y position in overview panel - * @param hiddenSeqs - * hidden sequences - * @param hiddenCols - * hidden columns - * @param ranges - * viewport position properties - */ - public void updateViewportFromMouse(int mousex, int mousey, - HiddenSequences hiddenSeqs, ColumnSelection hiddenCols, - ViewportRanges ranges) - { - int x = mousex; - int y = mousey; - - int alwidth = ranges.getAbsoluteAlignmentWidth(); - int alheight = ranges.getAbsoluteAlignmentHeight(); - - if (x < 0) - { - x = 0; - } - - if (y < 0) - { - y = 0; - } - - // - // Convert x value to residue position - // - - // need to determine where scrollCol should be, given x - // to do this also need to know width of viewport, and some hidden column - // correction - - // convert x to residues - this is an absolute position - int xAsRes = Math.round((float) x * alwidth / width); - - // get viewport width in residues - int vpwidth = ranges.getEndRes() - ranges.getStartRes() + 1; - - // get where x should be when accounting for hidden cols - // if x is in a hidden col region, shift to left - but we still need - // absolute position - // so convert back after getting visible region position - int visXAsRes = hiddenCols.findColumnPosition(xAsRes); - - // check in case we went off the edge of the alignment - int visAlignWidth = hiddenCols.findColumnPosition(alwidth - 1); - if (visXAsRes + vpwidth - 1 > visAlignWidth) - { - // went past the end of the alignment, adjust backwards - - // if last position was before the end of the alignment, need to update - if ((scrollCol + vpwidth - 1) < visAlignWidth) - { - visXAsRes = hiddenCols.findColumnPosition(hiddenCols - .subtractVisibleColumns(vpwidth - 1, alwidth - 1)); - } - else - { - visXAsRes = scrollCol; - } - } - - // - // Convert y value to sequence position - // - - // convert y to residues - int yAsSeq = Math.round((float) y * alheight / sequencesHeight); - - // get viewport height in sequences - // add 1 because height includes both endSeq and startSeq - int vpheight = ranges.getEndSeq() - ranges.getStartSeq() + 1; - - // get where y should be when accounting for hidden rows - // if y is in a hidden row region, shift up - but we still need absolute - // position, - // so convert back after getting visible region position - yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs - .findIndexWithoutHiddenSeqs(yAsSeq)); - - // check in case we went off the edge of the alignment - int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight); - int visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq); - if (visYAsRes + vpheight - 1 > visAlignHeight) - { - // went past the end of the alignment, adjust backwards - if ((scrollRow + vpheight - 1) < visAlignHeight) - { - visYAsRes = hiddenSeqs.findIndexWithoutHiddenSeqs(hiddenSeqs - .subtractVisibleRows(vpheight - 1, alheight - 1)); - } - else - { - visYAsRes = scrollRow; - } - } - - // update scroll values - scrollCol = visXAsRes; - scrollRow = visYAsRes; - - } - - /** - * Update the overview panel box when the associated alignment panel is - * changed - * - * @param hiddenSeqs - * hidden sequences - * @param hiddenCols - * hidden columns - * @param ranges - * viewport position properties - */ - public void setBoxPosition(HiddenSequences hiddenSeqs, - ColumnSelection hiddenCols, ViewportRanges ranges) - { - int alwidth = ranges.getAbsoluteAlignmentWidth(); - int alheight = ranges.getAbsoluteAlignmentHeight(); - - // work with absolute values of startRes and endRes - int startRes = hiddenCols.adjustForHiddenColumns(ranges.getStartRes()); - int endRes = hiddenCols.adjustForHiddenColumns(ranges.getEndRes()); - - // work with absolute values of startSeq and endSeq - int startSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getStartSeq()); - int endSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getEndSeq()); - - // boxX, boxY is the x,y location equivalent to startRes, startSeq - boxX = Math.round((float) startRes * width / alwidth); - boxY = Math.round((float) startSeq * sequencesHeight / alheight); - - // boxWidth is the width in residues translated to pixels - // since the box includes both the start and end residues, add 1 to the - // difference - boxWidth = Math - .round((float) (endRes - startRes + 1) * width / alwidth); - // boxHeight is the height in sequences translated to pixels - boxHeight = Math.round((float) (endSeq - startSeq + 1) - * sequencesHeight - / alheight); - } - - /** * Draw the overview panel's viewport box on a graphics object * * @param g @@ -285,27 +84,21 @@ public class OverviewDimensions return scrollRow; } - // TODO should be removed, when unit test has mock Graphics object available - // to check boxX/boxY public int getBoxX() { return boxX; } - // TODO should be removed, when unit test has mock Graphics object available - // to check boxX/boxY public int getBoxY() { return boxY; } - // TODO should be removed, when unit test has mock Graphics object available public int getBoxWidth() { return boxWidth; } - // TODO should be removed, when unit test has mock Graphics object available public int getBoxHeight() { return boxHeight; @@ -340,4 +133,21 @@ public class OverviewDimensions { return graphHeight; } -} + + public abstract void updateViewportFromMouse(int mousex, int mousey, + HiddenSequences hiddenSeqs, ColumnSelection hiddenCols, + ViewportRanges ranges); + + public abstract void setBoxPosition(HiddenSequences hiddenSeqs, + ColumnSelection hiddenCols, ViewportRanges ranges); + + public abstract AlignmentColsCollection getColumns( + ViewportRanges ranges, ColumnSelection hiddenCols); + + public abstract AlignmentRowsCollection getRows( + ViewportRanges ranges, AlignmentI al); + + public abstract float getPixelsPerCol(); + + public abstract float getPixelsPerSeq(); +} \ No newline at end of file diff --git a/src/jalview/viewmodel/OverviewDimensionsAllVisible.java b/src/jalview/viewmodel/OverviewDimensionsAllVisible.java new file mode 100644 index 0000000..21f0be6 --- /dev/null +++ b/src/jalview/viewmodel/OverviewDimensionsAllVisible.java @@ -0,0 +1,165 @@ +package jalview.viewmodel; + +import jalview.datamodel.AlignmentColsCollection; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentRowsCollection; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenSequences; + +public class OverviewDimensionsAllVisible extends OverviewDimensions +{ + private float pixelsPerCol; + + private float pixelsPerSeq; + + public OverviewDimensionsAllVisible(ViewportRanges ranges, + boolean showAnnotationPanel) + { + super(ranges, showAnnotationPanel); + + int alwidth = ranges.getVisibleAlignmentWidth(); + int alheight = ranges.getVisibleAlignmentHeight(); + + pixelsPerCol = (float) width / alwidth; + pixelsPerSeq = (float) sequencesHeight / alheight; + } + + @Override + public void updateViewportFromMouse(int mousex, int mousey, + HiddenSequences hiddenSeqs, ColumnSelection hiddenCols, + ViewportRanges ranges) + { + int x = mousex; + int y = mousey; + + int alwidth = ranges.getVisibleAlignmentWidth(); + int alheight = ranges.getVisibleAlignmentHeight(); + + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + // + // Convert x value to residue position + // + + // need to determine where scrollCol should be, given x + // to do this also need to know width of viewport, and some hidden column + // correction + + // convert x to residues - this is an absolute position + int xAsRes = Math.round((float) x * alwidth / width); + + // get viewport width in residues + int vpwidth = ranges.getEndRes() - ranges.getStartRes() + 1; + + if (xAsRes + vpwidth > alwidth) + { + // went past the end of the alignment, adjust backwards + + // if last position was before the end of the alignment, need to update + if ((scrollCol + vpwidth - 1) < alwidth) + { + xAsRes = alwidth - vpwidth; + } + else + { + xAsRes = scrollCol; + } + } + + + // + // Convert y value to sequence position + // + + // convert y to residues + int yAsSeq = Math.round((float) y * alheight / sequencesHeight); + + // get viewport height in sequences + // add 1 because height includes both endSeq and startSeq + int vpheight = ranges.getEndSeq() - ranges.getStartSeq() + 1; + + if (yAsSeq + vpheight > alheight) + { + // went past the end of the alignment, adjust backwards + if ((scrollRow + vpheight - 1) < alheight) + { + yAsSeq = alheight - vpheight; + } + else + { + yAsSeq = scrollRow; + } + } + + // update scroll values + scrollCol = xAsRes; + scrollRow = yAsSeq; + + } + + @Override + public void setBoxPosition(HiddenSequences hiddenSeqs, + ColumnSelection hiddenCols, ViewportRanges ranges) + { + int alwidth = ranges.getVisibleAlignmentWidth(); + int alheight = ranges.getVisibleAlignmentHeight(); + + // work with visible values of startRes and endRes + int startRes = ranges.getStartRes(); + int endRes = ranges.getEndRes(); + + // work with visible values of startSeq and endSeq + int startSeq = ranges.getStartSeq(); + int endSeq = ranges.getEndSeq(); + + // boxX, boxY is the x,y location equivalent to startRes, startSeq + boxX = Math.round((float) startRes * width / alwidth); + boxY = Math.round((float) startSeq * sequencesHeight / alheight); + + // boxWidth is the width in residues translated to pixels + // since the box includes both the start and end residues, add 1 to the + // difference + boxWidth = Math + .round((float) (endRes - startRes + 1) * width / alwidth); + // boxHeight is the height in sequences translated to pixels + boxHeight = Math.round((float) (endSeq - startSeq + 1) + * sequencesHeight / alheight); + + } + + @Override + public AlignmentColsCollection getColumns(ViewportRanges ranges, + ColumnSelection hiddenCols) + { + return new AlignmentColsCollection(0, + ranges.getVisibleAlignmentWidth() - 1, hiddenCols); + } + + @Override + public AlignmentRowsCollection getRows(ViewportRanges ranges, + AlignmentI al) + { + return new AlignmentRowsCollection(0, + ranges.getVisibleAlignmentHeight() - 1, al); + } + + @Override + public float getPixelsPerCol() + { + return pixelsPerCol; + } + + @Override + public float getPixelsPerSeq() + { + return pixelsPerSeq; + } +} diff --git a/src/jalview/viewmodel/OverviewDimensionsWithHidden.java b/src/jalview/viewmodel/OverviewDimensionsWithHidden.java new file mode 100644 index 0000000..575167a --- /dev/null +++ b/src/jalview/viewmodel/OverviewDimensionsWithHidden.java @@ -0,0 +1,239 @@ +/* + * 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.datamodel.AlignmentColsCollection; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.AlignmentRowsCollection; +import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenSequences; + +public class OverviewDimensionsWithHidden extends OverviewDimensions +{ + private float pixelsPerCol; + + private float pixelsPerSeq; + + /** + * Create an OverviewDimensions object + * + * @param ranges + * positional properties of the viewport + * @param showAnnotationPanel + * true if the annotation panel is to be shown, false otherwise + */ + public OverviewDimensionsWithHidden(ViewportRanges ranges, + boolean showAnnotationPanel) + { + super(ranges, showAnnotationPanel); + + int alwidth = ranges.getAbsoluteAlignmentWidth(); + int alheight = ranges.getAbsoluteAlignmentHeight(); + + pixelsPerCol = (float) width / alwidth; + pixelsPerSeq = (float) sequencesHeight / alheight; + } + + /** + * Check box dimensions and scroll positions and correct if necessary + * + * @param mousex + * x position in overview panel + * @param mousey + * y position in overview panel + * @param hiddenSeqs + * hidden sequences + * @param hiddenCols + * hidden columns + * @param ranges + * viewport position properties + */ + @Override + public void updateViewportFromMouse(int mousex, int mousey, + HiddenSequences hiddenSeqs, ColumnSelection hiddenCols, + ViewportRanges ranges) + { + int x = mousex; + int y = mousey; + + int alwidth = ranges.getAbsoluteAlignmentWidth(); + int alheight = ranges.getAbsoluteAlignmentHeight(); + + if (x < 0) + { + x = 0; + } + + if (y < 0) + { + y = 0; + } + + // + // Convert x value to residue position + // + + // need to determine where scrollCol should be, given x + // to do this also need to know width of viewport, and some hidden column + // correction + + // convert x to residues - this is an absolute position + int xAsRes = Math.round((float) x * alwidth / width); + + // get viewport width in residues + int vpwidth = ranges.getEndRes() - ranges.getStartRes() + 1; + + // get where x should be when accounting for hidden cols + // if x is in a hidden col region, shift to left - but we still need + // absolute position + // so convert back after getting visible region position + int visXAsRes = hiddenCols.findColumnPosition(xAsRes); + + // check in case we went off the edge of the alignment + int visAlignWidth = hiddenCols.findColumnPosition(alwidth - 1); + if (visXAsRes + vpwidth - 1 > visAlignWidth) + { + // went past the end of the alignment, adjust backwards + + // if last position was before the end of the alignment, need to update + if ((scrollCol + vpwidth - 1) < visAlignWidth) + { + visXAsRes = hiddenCols.findColumnPosition(hiddenCols + .subtractVisibleColumns(vpwidth - 1, alwidth - 1)); + } + else + { + visXAsRes = scrollCol; + } + } + + // + // Convert y value to sequence position + // + + // convert y to residues + int yAsSeq = Math.round((float) y * alheight / sequencesHeight); + + // get viewport height in sequences + // add 1 because height includes both endSeq and startSeq + int vpheight = ranges.getEndSeq() - ranges.getStartSeq() + 1; + + // get where y should be when accounting for hidden rows + // if y is in a hidden row region, shift up - but we still need absolute + // position, + // so convert back after getting visible region position + yAsSeq = hiddenSeqs.adjustForHiddenSeqs(hiddenSeqs + .findIndexWithoutHiddenSeqs(yAsSeq)); + + // check in case we went off the edge of the alignment + int visAlignHeight = hiddenSeqs.findIndexWithoutHiddenSeqs(alheight); + int visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(yAsSeq); + if (visYAsSeq + vpheight - 1 > visAlignHeight) + { + // went past the end of the alignment, adjust backwards + if ((scrollRow + vpheight - 1) < visAlignHeight) + { + visYAsSeq = hiddenSeqs.findIndexWithoutHiddenSeqs(hiddenSeqs + .subtractVisibleRows(vpheight - 1, alheight - 1)); + } + else + { + visYAsSeq = scrollRow; + } + } + + // update scroll values + scrollCol = visXAsRes; + scrollRow = visYAsSeq; + + } + + /** + * Update the overview panel box when the associated alignment panel is + * changed + * + * @param hiddenSeqs + * hidden sequences + * @param hiddenCols + * hidden columns + * @param ranges + * viewport position properties + */ + @Override + public void setBoxPosition(HiddenSequences hiddenSeqs, + ColumnSelection hiddenCols, ViewportRanges ranges) + { + int alwidth = ranges.getAbsoluteAlignmentWidth(); + int alheight = ranges.getAbsoluteAlignmentHeight(); + + // work with absolute values of startRes and endRes + int startRes = hiddenCols.adjustForHiddenColumns(ranges.getStartRes()); + int endRes = hiddenCols.adjustForHiddenColumns(ranges.getEndRes()); + + // work with absolute values of startSeq and endSeq + int startSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getStartSeq()); + int endSeq = hiddenSeqs.adjustForHiddenSeqs(ranges.getEndSeq()); + + // boxX, boxY is the x,y location equivalent to startRes, startSeq + boxX = Math.round((float) startRes * width / alwidth); + boxY = Math.round((float) startSeq * sequencesHeight / alheight); + + // boxWidth is the width in residues translated to pixels + // since the box includes both the start and end residues, add 1 to the + // difference + boxWidth = Math + .round((float) (endRes - startRes + 1) * width / alwidth); + // boxHeight is the height in sequences translated to pixels + boxHeight = Math.round((float) (endSeq - startSeq + 1) + * sequencesHeight + / alheight); + } + + @Override + public AlignmentColsCollection getColumns(ViewportRanges ranges, + ColumnSelection hiddenCols) + { + return new AlignmentColsCollection(0, + ranges.getAbsoluteAlignmentWidth() - 1, + hiddenCols); + } + + @Override + public AlignmentRowsCollection getRows(ViewportRanges ranges, + AlignmentI al) + { + return new AlignmentRowsCollection(0, + ranges.getAbsoluteAlignmentHeight() - 1, + al); + } + + @Override + public float getPixelsPerCol() + { + return pixelsPerCol; + } + + @Override + public float getPixelsPerSeq() + { + return pixelsPerSeq; + } +} diff --git a/src/jalview/viewmodel/ViewportRanges.java b/src/jalview/viewmodel/ViewportRanges.java index c91d2d9..18703b4 100644 --- a/src/jalview/viewmodel/ViewportRanges.java +++ b/src/jalview/viewmodel/ViewportRanges.java @@ -79,6 +79,23 @@ public class ViewportRanges extends ViewportProperties } /** + * Get alignment width in cols, excluding hidden cols + */ + public int getVisibleAlignmentWidth() + { + // TODO need access to hidden columns here + return al.getWidth(); // - hidden columns + } + + /** + * Get alignment height in rows, excluding hidden rows + */ + public int getVisibleAlignmentHeight() + { + return al.getHeight(); + } + + /** * Set first residue visible in the viewport * * @param res diff --git a/test/jalview/viewmodel/OverviewDimensionsTest.java b/test/jalview/viewmodel/OverviewDimensionsTest.java index 398fec3..dc414fb 100644 --- a/test/jalview/viewmodel/OverviewDimensionsTest.java +++ b/test/jalview/viewmodel/OverviewDimensionsTest.java @@ -42,7 +42,7 @@ import org.testng.annotations.Test; public class OverviewDimensionsTest { AlignmentI al; - OverviewDimensions od; + OverviewDimensionsWithHidden od; // cached widths and heights int boxWidth; @@ -86,7 +86,7 @@ public class OverviewDimensionsTest ColumnSelection hiddenCols = new ColumnSelection(); - od = new OverviewDimensions(vpranges, true); + od = new OverviewDimensionsWithHidden(vpranges, true); // Initial box sizing - default path through code od.setBoxPosition(al.getHiddenSequences(), hiddenCols, vpranges); @@ -136,7 +136,7 @@ public class OverviewDimensionsTest Alignment al1 = new Alignment(seqs1); ViewportRanges props = new ViewportRanges(al1); - OverviewDimensions od = new OverviewDimensions(props, true); + OverviewDimensions od = new OverviewDimensionsWithHidden(props, true); int scaledHeight = 267; assertEquals(od.getGraphHeight(), defaultGraphHeight); assertEquals(od.getSequencesHeight(), scaledHeight); @@ -148,7 +148,7 @@ public class OverviewDimensionsTest Alignment al2 = new Alignment(seqs2); props = new ViewportRanges(al2); - od = new OverviewDimensions(props, true); + od = new OverviewDimensionsWithHidden(props, true); int scaledWidth = 300; assertEquals(od.getGraphHeight(), defaultGraphHeight); assertEquals(od.getSequencesHeight(), maxSeqHeight); @@ -161,7 +161,7 @@ public class OverviewDimensionsTest Alignment al3 = new Alignment(seqs3); props = new ViewportRanges(al3); - od = new OverviewDimensions(props, true); + od = new OverviewDimensionsWithHidden(props, true); assertEquals(od.getGraphHeight(), defaultGraphHeight); assertEquals(od.getSequencesHeight(), minSeqHeight); assertEquals(od.getWidth(), maxWidth); @@ -173,7 +173,7 @@ public class OverviewDimensionsTest Alignment al4 = new Alignment(seqs4); props = new ViewportRanges(al4); - od = new OverviewDimensions(props, true); + od = new OverviewDimensionsWithHidden(props, true); assertEquals(od.getGraphHeight(), defaultGraphHeight); assertEquals(od.getSequencesHeight(), maxSeqHeight); assertEquals(od.getWidth(), minWidth); @@ -182,7 +182,7 @@ public class OverviewDimensionsTest Alignment al5 = new Alignment(seqs4); props = new ViewportRanges(al5); - od = new OverviewDimensions(props, false); + od = new OverviewDimensionsWithHidden(props, false); assertEquals(od.getGraphHeight(), 0); assertEquals(od.getSequencesHeight(), maxSeqHeight); assertEquals(od.getWidth(), minWidth); @@ -988,7 +988,7 @@ public class OverviewDimensionsTest /* * Mouse click as position x,y in overview window */ - private void mouseClick(OverviewDimensions od, int x, int y) + private void mouseClick(OverviewDimensionsWithHidden od, int x, int y) { od.updateViewportFromMouse(x, y, al.getHiddenSequences(), hiddenCols, vpranges); -- 1.7.10.2