*/
package jalview.api;
+import java.util.BitSet;
+
public interface AlignmentColsCollectionI extends Iterable<Integer>
{
/**
* @return true if there is at least 1 hidden column
*/
public boolean hasHidden();
+
+ /**
+ * Get the visible-column bitset, possibly containing hidden columns (which
+ * may or may not be hidden in the overview).
+ *
+ * @return a BitSet
+ */
+ public BitSet getOverviewBitSet();
+
+ /**
+ * Get the hidden-column bitset, (which may or may not be hidden in the
+ * overview).
+ *
+ * @return
+ */
+ BitSet getHiddenBitSet();
}
or = new OverviewRenderer(panel.ap, fr, od, av.getAlignment(),
av.getResidueShading(), new OverviewResColourFinder());
offscreen = nullFrame.createImage(od.getWidth(), od.getHeight());
- or.draw(od.getRows(av.getAlignment()),
- od.getColumns(av.getAlignment()));
+ or.drawMiniMe();
}
@Override
public void finalizeDraw(BufferedImage miniMe)
{
- Graphics mg = miniMe.getGraphics();
-
- // checks for conservation annotation to make sure overview works for DNA
- // too
- if (showAnnotation)
- {
- mg.translate(0, od.getSequencesHeight());
- or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
- od.getGraphHeight(), od.getColumns(av.getAlignment()));
- mg.translate(0, -od.getSequencesHeight());
- }
-
if (restart)
{
restart = false;
}
else
{
+ this.miniMe = miniMe;
+ // checks for conservation annotation to make sure overview works for DNA
+ // too
+ if (showAnnotation)
+ {
+ or.drawGraph(av.getAlignmentConservationAnnotation());
+ }
updaterunning = false;
+ repaint();
}
}
@Override
public void run()
{
+ setBoxPosition();
canvas.draw(av.isShowSequenceFeatures(),
(av.isShowAnnotation()
&& av.getAlignmentConservationAnnotation() != null),
ap.seqPanel.seqCanvas.getFeatureRenderer());
- setBoxPosition();
}
/**
synchronized (groups)
{
- temp.clear();
int gSize = groups.size();
if (gSize == 0)
{
return noGroups;
}
+ temp.clear();
for (int i = 0; i < gSize; i++)
{
SequenceGroup sg = groups.get(i);
}
}
+ @Override
+ public void resetColors()
+ {
+ for (int i = getHeight(); --i >= 0;)
+ {
+ sequences.get(i).resetColors();
+ }
+ // if (dataset != null)
+ // {
+ // dataset.resetColors();
+ // }
+ }
+
}
public HiddenColumns propagateInsertions(SequenceI profileseq,
AlignmentView input);
+ /**
+ * Remove all color already assigned to sequences, causing them to be be
+ * recalculated.
+ */
+ void resetColors();
+
}
import jalview.api.AlignmentColsCollectionI;
+import java.util.BitSet;
import java.util.Iterator;
public class AllColsCollection implements AlignmentColsCollectionI
{
return hidden.hasHiddenColumns();
}
+
+ private BitSet bsVisible;
+
+ @Override
+ public BitSet getHiddenBitSet()
+ {
+ return hidden.getBitset();
+ }
+
+ /**
+ * return ALL columns, not just the truly visible ones
+ */
+ @Override
+ public BitSet getOverviewBitSet()
+ {
+ if (bsVisible == null)
+ {
+ bsVisible = new BitSet(end + 1);
+ bsVisible.set(0, end + 1);
+ }
+ return bsVisible;
+ }
}
*/
private List<int[]> hiddenColumns = new ArrayList<>();
+ private BitSet hiddenBitSet;
+
+ public BitSet getBitset()
+ {
+ if (hiddenBitSet == null)
+ {
+ hiddenBitSet = new BitSet();
+ for (int[] range : hiddenColumns)
+ {
+ hiddenBitSet.set(range[0], range[1] + 1);
+ }
+ }
+ return hiddenBitSet;
+ }
/**
* Constructor
*/
prevHiddenCount);
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
insertRangeAtOverlap(i, start, end, region);
added = true;
}
+ hiddenBitSet = null;
return added;
}
}
numColumns += region[1] - oldend;
hiddenColumns.subList(i + 1, endi + 1).clear();
+ hiddenBitSet = null;
}
/**
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
}
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
} finally
{
+ hiddenBitSet = null;
LOCK.readLock().unlock();
}
}
for (int firstSet = tohide
.nextSetBit(start), lastSet = start; firstSet >= start
&& lastSet <= end; firstSet = tohide
- .nextSetBit(lastSet))
+ .nextSetBit(lastSet))
{
lastSet = tohide.nextClearBit(firstSet);
if (lastSet <= end)
cursor = new HiddenColumnsCursor(hiddenColumns);
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
}
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
{
LOCK.writeLock().lock();
- BitSet hiddenBitSet = new BitSet();
- for (int[] range : hiddenColumns)
- {
- hiddenBitSet.set(range[0], range[1] + 1);
- }
- hiddenBitSet.andNot(updates);
+ getBitset().andNot(updates);
hiddenColumns.clear();
hideColumns(hiddenBitSet);
} finally
{
+ hiddenBitSet = null;
LOCK.writeLock().unlock();
}
}
import jalview.util.MapList;
import jalview.util.StringUtils;
-import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@Override
public void sequenceChanged()
{
+ argb = null;
changeCount++;
}
return 0;
}
+ private int[] argb;
+
@Override
- public Color getColor(int i)
+ public int getColor(int i)
{
- return null;
+ return argb == null ? 0 : argb[i];
}
@Override
- public Color setColor(int i, Color c)
+ public int setColor(int i, int rgb)
{
- return c;
+ if (argb == null)
+ {
+ argb = new int[this.sequence.length];
+ }
+ return (argb[i] = rgb);
}
@Override
public void resetColors()
{
+ argb = null;
}
}
import jalview.util.MapList;
import jalview.ws.params.InvalidArgumentException;
-import java.awt.Color;
import java.util.BitSet;
import java.util.Iterator;
import java.util.List;
*/
public int firstResidueOutsideIterator(Iterator<int[]> it);
- public Color getColor(int i);
+ public int getColor(int i);
- public Color setColor(int i, Color c);
+ public int setColor(int i, int rgb);
public void resetColors();
import jalview.api.AlignmentColsCollectionI;
+import java.util.BitSet;
import java.util.Iterator;
public class VisibleColsCollection implements AlignmentColsCollectionI
HiddenColumns hidden;
+ private BitSet bsVisible;
+
public VisibleColsCollection(int s, int e, HiddenColumns h)
{
start = s;
return false;
}
+ /**
+ * Only the visible columns.
+ */
+ @Override
+ public BitSet getOverviewBitSet()
+ {
+ if (bsVisible == null)
+ {
+ bsVisible = new BitSet(end + 1);
+ }
+ bsVisible.clear();
+ bsVisible.set(start, end + 1);
+ bsVisible.andNot(hidden.getBitset());
+
+ return bsVisible;
+ }
+
+ @Override
+ public BitSet getHiddenBitSet()
+ {
+ return new BitSet();
+ }
+
}
{
viewport.setShowSequenceFeatures(true);
showSeqFeatures.setSelected(true);
+ alignPanel.getAlignment().resetColors();
}
}
import javax.swing.SwingUtilities;
/**
- * DOCUMENT ME!
+ * The main panel of an AlignFrame, containing holders for the IdPanel,
+ * SeqPanel, AnnotationLabels (a JPanel), and AnnotationPanel.
+ *
+ * Additional holders contain an IdPanelWidthAdjuster space above the idPanel,
+ * AnnotationScroller (JScrollPane for AnnotationPanel), and vertical and
+ * horizontal scrollbars.
+ *
+ *
*
* @author $author$
* @version $Revision: 1.161 $
if (overviewPanel != null)
{
+ getAlignment().resetColors();
overviewPanel.updateOverviewImage();
}
}
{
super(alignPanel.av);
this.ap = alignPanel;
- if (alignPanel.getSeqPanel() != null
- && alignPanel.getSeqPanel().seqCanvas != null
- && alignPanel.getSeqPanel().seqCanvas.fr != null)
+ SeqPanel sp = alignPanel.getSeqPanel();
+ if (sp != null && sp.seqCanvas != null && sp.seqCanvas.fr != null)
{
- transferSettings(alignPanel.getSeqPanel().seqCanvas.fr);
+ sp.clearColors();
+ transferSettings(sp.seqCanvas.fr);
}
}
}
private OverviewPanel panel;
+ private boolean showProgress;
+
public OverviewCanvas(OverviewPanel panel,
OverviewDimensions overviewDims,
AlignViewportI alignvp, ProgressPanel pp)
{
this.panel = panel;
od = overviewDims;
+ lastMiniMe = null;
av = alignvp;
progressPanel = pp;
-
+ showProgress = (pp != null);
sr = new SequenceRenderer(av);
sr.renderGaps = false;
fr = new jalview.renderer.seqfeatures.FeatureRenderer(av);
public void resetOviewDims(OverviewDimensions overviewDims)
{
od = overviewDims;
+ lastMiniMe = null;
}
/**
else
{
updaterunning = true;
+ restart = false;
}
return restart;
}
this.showSequenceFeatures = showSequenceFeatures;
this.showAnnotation = showAnnotation;
this.featureRenderer = featureRenderer;
-
- // System.out.println("OC draw " + ++ndraw + " showseqf="
- // + showSequenceFeatures + " showAnno=" + showAnnotation);
-
if (showSequenceFeatures)
{
fr.transferSettings(featureRenderer);
}
-
setPreferredSize(new Dimension(od.getWidth(), od.getHeight()));
-
AlignmentI al = av.getAlignment();
- or = new OverviewRenderer(panel.ap, fr, od, al,
- av.getResidueShading(), cf,
- progressPanel != null);
- if (progressPanel != null)
+ or = new OverviewRenderer(panel.ap, fr, od, al, av.getResidueShading(),
+ cf, showProgress);
+ if (showProgress)
{
or.addPropertyChangeListener(progressPanel);
}
- or.draw(od.getRows(al), od.getColumns(al));
+ or.drawMiniMe();
}
- void finalizeDraw(BufferedImage miniMe)
+ synchronized void finalizeDraw(BufferedImage miniMe)
{
- Graphics mg = miniMe.getGraphics();
- if (showAnnotation)
+
+ if (or == null)
{
- mg.translate(0, od.getSequencesHeight());
- or.drawGraph(mg, av.getAlignmentConservationAnnotation(),
- od.getGraphHeight(), od.getColumns(av.getAlignment()));
- mg.translate(0, -od.getSequencesHeight());
+ System.out.println("OC or is null");
}
- mg.dispose(); // BH 2019
- if (progressPanel != null)
+ else if (showProgress)
{
or.removePropertyChangeListener(progressPanel);
}
- or = null;
if (restart)
{
+ or = null;
restart = false;
if (!disposed)
{
}
else
{
+ if (showAnnotation)
+ {
+ or.drawGraph(av.getAlignmentConservationAnnotation());
+ }
+ or = null;
updaterunning = false;
lastMiniMe = miniMe;
repaint();
}
-
}
+
@Override
public void paintComponent(Graphics g)
{
else if (drawMe)
{
// is this a resize?
- if (w != od.getWidth() || h != od.getHeight()) {
- // if there is annotation, scale the alignment and annotation
- // separately
- if (od.getGraphHeight() <= 0 && od.getSequencesHeight() <= 0)
+ if (w != od.getWidth() || h != od.getHeight())
{
- od.setWidth(w);
- od.setHeight(h);
+
+ lastMiniMe = null;
return;
- }
- // System.out.println("OC new subimages");
- BufferedImage topImage = lastMiniMe.getSubimage(0, 0, od.getWidth(),
- od.getSequencesHeight());
- BufferedImage bottomImage = lastMiniMe.getSubimage(0,
- od.getSequencesHeight(), od.getWidth(), od.getGraphHeight());
-
- // must be done at this point as we rely on using old width/height
- // above, and new width/height below
- od.setWidth(w);
- od.setHeight(h);
-
- // stick the images back together so lastMiniMe is consistent in the
- // event of a repaint - BUT probably not thread safe
- // System.out.println("OC new lastminime " + w + " " + h);
- lastMiniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
- Graphics lg = lastMiniMe.getGraphics();
- lg.drawImage(topImage, 0, 0, w, od.getSequencesHeight(), null);
- lg.drawImage(bottomImage, 0, od.getSequencesHeight(), w,
- od.getGraphHeight(), this);
- lg.dispose();
- // BH 2019: removed -- this is now taken care of using vpbox in
- // OverviewDimension
- // // make sure the box is in the right place
- // od.setBoxPosition(av.getAlignment().getHiddenSequences(),
- // av.getAlignment().getHiddenColumns());
+ // // if there is annotation, scale the alignment and annotation
+ // // separately
+ // if (od.getGraphHeight() <= 0 && od.getSequencesHeight() <= 0)
+ // {
+ // od.setWidth(w);
+ // od.setHeight(h);
+ // return;
+ // }
+ // try
+ // {
+ // BufferedImage topImage = lastMiniMe.getSubimage(0, 0,
+ // od.getWidth(), od.getSequencesHeight());
+ //
+ // BufferedImage bottomImage = lastMiniMe.getSubimage(0,
+ // od.getSequencesHeight(), od.getWidth(),
+ // od.getGraphHeight());
+ //
+ // // must be done at this point as we rely on using old width/height
+ // // above, and new width/height below
+ // od.setWidth(w);
+ // od.setHeight(h);
+ //
+ // // stick the images back together so lastMiniMe is consistent in the
+ // // event of a repaint - BUT probably not thread safe
+ //
+ // // right -- this fails with fast user action.
+ //
+ // lastMiniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+ // Graphics lg = lastMiniMe.getGraphics();
+ // lg.drawImage(topImage, 0, 0, w, od.getSequencesHeight(), null);
+ // lg.drawImage(bottomImage, 0, od.getSequencesHeight(), w,
+ // od.getGraphHeight(), this);
+ // lg.dispose();
+ //
+ // } catch (RasterFormatException e)
+ // {
+ // System.out.println("OC Raster Exception " + lastMiniMe.getWidth()
+ // + "/" + w + "," + lastMiniMe.getHeight() + "/" + h + " "
+ // + od.getSequencesHeight() + " " + od.getGraphHeight());
+ // }
+ // BH 2019: removed -- this is now taken care of using vpbox in
+ // OverviewDimension
+ // // make sure the box is in the right place
+ // od.setBoxPosition(av.getAlignment().getHiddenSequences(),
+ // av.getAlignment().getHiddenColumns());
}
}
{
disposed = true;
od = null;
+ lastMiniMe = null;
synchronized (this)
{
setRestart("dispose");
if (getWidth() == od.getWidth()
&& getHeight() == od.getHeight() + ph)
{
+ // BH: resizing is now exceptionally fast.
updateOverviewImage();
}
else
}
od.setWidth(w);
od.setHeight(h - ph);
- repaint();
+ updateOverviewImage();
+ // repaint();
}
// BH 2019.07.29 this is unnecessary -- it is what layout managers are
// for:
*
* Cases:
*
+ * AlignFrame.setFeatureGroupState
+ *
+ * AlignmentPanel.paintAlignment(true,...) (117 references)
+ *
+ * OverviewPanel..componentResized() OverviewPanel.toggleHiddenColumns()
+ *
+ * PopupMenu for action.reveal_sequences, action.reveal_all
+ *
+ * SliderPanel.mouseReleased()
*
*/
public void updateOverviewImage()
{
if (canvas != null)
{
+ setBoxPosition();
canvas.draw(av.isShowSequenceFeatures(),
(av.isShowAnnotation()
&& av.getAlignmentConservationAnnotation() != null),
ap.getFeatureRenderer());
- setBoxPosition();
}
}
@Override
public void propertyChange(PropertyChangeEvent evt)
{
- // Respond to viewport change events (e.g. alignment panel was scrolled)
- // Both scrolling and resizing change viewport ranges: scrolling changes
- // both start and end points, but resize only changes end values.
- // Here we only want to fastpaint on a scroll, with resize using a normal
- // paint, so scroll events are identified as changes to the horizontal or
- // vertical start value.
switch (evt.getPropertyName())
{
case ViewportRanges.STARTRES:
case ViewportRanges.STARTRESANDSEQ:
case ViewportRanges.MOVE_VIEWPORT:
// scroll event, repaint panel
- // TODO: check this?
- // BH: This is actually quite strange. AlignmentPanel is taking care of
- // all of
- // this with fast paint, so why indirectly trigger a repaint from the
- // ScalePanel?
-
+ // original comment:
// Call repaint on alignment panel so that repaints from other alignment
// panel components can be aggregated. Otherwise performance of the
// overview
// window and others may be adversely affected.
- // av.getAlignPanel().repaint();
- System.out.println("ScalePanel propertyChange disabled "
- + evt.getPropertyName());
+
+ // TODO: check this?
+ // BH: This is actually quite strange. AlignmentPanel is taking care of
+ // all of this with fast paint, so why indirectly trigger a repaint from
+ // the ScalePanel? Where do we see this behavior necessary?
+ // I have set this to check for a trigger from some other ViewportRanges,
+ // but I don't actually think that is possible.
+
+ if (evt.getSource() != av.getRanges())
+ {
+ av.getAlignPanel().repaint();
+ }
break;
}
}
int charHeight = av.getCharHeight();
int charWidth = av.getCharWidth();
- int width = getWidth();
- int height = getHeight();
+ int availWidth = getWidth();
+ int availHeight = getHeight();
- width -= (width % charWidth);
- height -= (height % charHeight);
+ availWidth -= (availWidth % charWidth);
+ availHeight -= (availHeight % charHeight);
// BH 2019 can't possibly fastPaint if either width or height is 0
- if (width == 0 || height == 0)
+ if (availWidth == 0 || availHeight == 0)
{
return;
}
// img is a cached version of the last view we drew.
// If we have no img or the size has changed, make a new one.
//
- if (img == null || width != img.getWidth()
- || height != img.getHeight())
+ if (img == null || availWidth != img.getWidth()
+ || availHeight != img.getHeight())
{
- img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ img = new BufferedImage(availWidth, availHeight,
+ BufferedImage.TYPE_INT_RGB);
}
Graphics2D gg = (Graphics2D) img.getGraphics();
}
gg.setColor(Color.white);
- gg.fillRect(0, 0, img.getWidth(), img.getHeight());
+ gg.fillRect(0, 0, availWidth, availHeight);
if (av.getWrapAlignment())
{
- drawWrappedPanel(gg, width, height, ranges.getStartRes());
+ drawWrappedPanel(gg, availWidth, availHeight, ranges.getStartRes());
}
else
{
}
/**
- * Returns the visible width of the canvas in residues, after allowing for
- * East or West scales (if shown)
+ * Using the current font, determine fields labelWidthEast and labelWidthWest,
+ * and return the number of residues that can fill the remaining width.
*
- * @param canvasWidth
+ * @param width
* the width in pixels (possibly including scales)
*
- * @return
+ * @return the visible width in residues, after allowing for East or West
+ * scales (if shown)
+ *
*/
- public int getWrappedCanvasWidth(int canvasWidth)
+ public int getWrappedCanvasWidth(int width)
{
int charWidth = av.getCharWidth();
FontMetrics fm = getFontMetrics(av.getFont());
- int labelWidth = 0;
-
- if (av.getScaleRightWrapped() || av.getScaleLeftWrapped())
- {
- labelWidth = getLabelWidth(fm);
- }
+ int labelWidth = (av.getScaleRightWrapped() || av.getScaleLeftWrapped()
+ ? getLabelWidth(fm)
+ : 0);
labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0;
labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0;
- return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth;
+ return (width - labelWidthEast - labelWidthWest) / charWidth;
}
/**
maxWidth = Math.max(maxWidth, alignment.getSequenceAt(i).getEnd());
}
+ // quick int log10
int length = 0;
for (int i = maxWidth; i > 0; i /= 10)
{
* window
*
* @param g
- * @param canvasWidth
+ * @param availWidth
* available width in pixels
- * @param canvasHeight
+ * @param availHeight
* available height in pixels
* @param startColumn
* the first column (0...) of the alignment to draw
*/
- public void drawWrappedPanel(Graphics g, int canvasWidth,
- int canvasHeight, final int startColumn)
+ public void drawWrappedPanel(Graphics g, int availWidth, int availHeight,
+ final int startColumn)
{
- int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth,
- canvasHeight);
+ int wrappedWidthInResidues = calculateWrappedGeometry(availWidth,
+ availHeight);
av.setWrappedWidth(wrappedWidthInResidues);
// we need to call this again to make sure the startColumn +
// wrappedWidthInResidues values are used to calculate wrappedVisibleWidths
// correctly.
- calculateWrappedGeometry(canvasWidth, canvasHeight);
+ calculateWrappedGeometry(availWidth, availHeight);
/*
* draw one width at a time (excluding any scales shown),
{
int endColumn = Math
.min(maxWidth, start + wrappedWidthInResidues - 1);
- drawWrappedWidth(g, ypos, start, endColumn, canvasHeight);
+ drawWrappedWidth(g, ypos, start, endColumn, availHeight);
ypos += wrappedRepeatHeightPx;
start += wrappedWidthInResidues;
currentWidth++;
* <li>whether scales are shown left, right or above the alignment</li>
* </ul>
*
- * @param canvasWidth
- * @param canvasHeight
+ * @param availWidth
+ * @param availHeight
* @return the number of residue columns in each width
*/
- protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight)
+ protected int calculateWrappedGeometry(int availWidth, int availHeight)
{
int charHeight = av.getCharHeight();
* ensuring a part height includes at least one sequence
*/
ViewportRanges ranges = av.getRanges();
- wrappedVisibleWidths = canvasHeight / wrappedRepeatHeightPx;
- int remainder = canvasHeight % wrappedRepeatHeightPx;
+ wrappedVisibleWidths = availHeight / wrappedRepeatHeightPx;
+ int remainder = availHeight % wrappedRepeatHeightPx;
if (remainder >= (wrappedSpaceAboveAlignment + charHeight))
{
wrappedVisibleWidths++;
/*
* compute width in residues; this also sets East and West label widths
*/
- int wrappedWidthInResidues = getWrappedCanvasWidth(canvasWidth);
+ int wrappedWidthInResidues = getWrappedCanvasWidth(availWidth);
/*
* limit visibleWidths to not exceed width of alignment
import javax.swing.ToolTipManager;
/**
- * DOCUMENT ME!
+ * The main scrollable region containing the alignment and just to the right of
+ * the IDPanel.
*
* @author $author$
* @version $Revision: 1.130 $
SearchResultsI lastSearchResults;
/**
- * Creates a new SeqPanel object
+ * Create a new SeqPanel.
*
* @param viewport
* @param alignPanel
true);
}
+ public void clearColors()
+ {
+ av.getAlignment().getSequences();
+ // TODO Auto-generated method stub
+
+ }
+
}
*/
protected void initData()
{
- seqs = new Vector<SequenceI>();
- annotations = new Vector<AlignmentAnnotation>();
- seqGroups = new ArrayList<SequenceGroup>();
+ seqs = new Vector<>();
+ annotations = new Vector<>();
+ seqGroups = new ArrayList<>();
parseCalled = false;
}
/**
- * DOCUMENT ME!
+ * Create the seqs Vector from a set of parsed sequences in an AlignFile,
+ * FeaturesFile, RnamlFile, or StockholmFile.
*
* @param s
* DOCUMENT ME!
@Override
public void setSeqs(SequenceI[] s)
{
- seqs = new Vector<SequenceI>();
+ seqs = new Vector<>();
for (int i = 0; i < s.length; i++)
{
{
if (newickStrings == null)
{
- newickStrings = new Vector<String[]>();
+ newickStrings = new Vector<>();
}
newickStrings.addElement(new String[] { treeName, newickString });
}
AlignViewportI av = getViewport();
if (av != null)
{
- if (av.getAlignment() != null)
+ AlignmentI a = av.getAlignment();
+ if (a != null)
{
- dataset = av.getAlignment().getDataset();
+ dataset = a.getDataset();
}
if (dataset == null)
{
// working in the applet context ?
- dataset = av.getAlignment();
+ dataset = a;
}
}
else
hscrollFillerPanel.setPreferredSize(new Dimension(70, 10));
hscrollHolder.setBackground(Color.white);
annotationScroller.setBorder(null);
+ annotationScroller.setBackground(Color.BLUE);
annotationScroller.setPreferredSize(new Dimension(10, 80));
this.setPreferredSize(new Dimension(220, 166));
// image to render on
private BufferedImage miniMe;
- // raw number of pixels to allocate to each column
+ /**
+ * Number of pixelsPerCol;
+ */
private float pixelsPerCol;
+ /**
+ * Number of visible columns per pixel.
+ *
+ */
+ private float colsPerPixel;
+
// raw number of pixels to allocate to each row
private float pixelsPerSeq;
private AlignmentViewPanel panel;
+ private int sequencesHeight;
+
public OverviewRenderer(AlignmentViewPanel panel, FeatureRenderer fr,
OverviewDimensions od,
AlignmentI alignment,
}
public OverviewRenderer(AlignmentViewPanel panel,
- jalview.api.FeatureRenderer fr,
- OverviewDimensions od,
+ jalview.api.FeatureRenderer fr, OverviewDimensions od,
AlignmentI alignment, ResidueShaderI resshader,
OverviewResColourFinder colFinder, boolean showProgress)
{
this.panel = panel;
finder = new FeatureColourFinder(fr);
- resColFinder = colFinder;
-
al = alignment;
shader = resshader;
+ resColFinder = colFinder;
+ this.showProgress = showProgress;
- pixelsPerCol = od.getPixelsPerCol();
- pixelsPerSeq = od.getPixelsPerSeq();
+ w = od.getWidth();
+ h = od.getHeight();
+ rows = od.getRows(alignment);
+ cols = od.getColumns(alignment);
graphHeight = od.getGraphHeight();
- miniMe = new BufferedImage(od.getWidth(), od.getHeight(),
- BufferedImage.TYPE_INT_RGB);
- this.showProgress = showProgress;
+ alignmentHeight = od.getSequencesHeight();
+
+ pixelsPerSeq = od.getPixelsPerSeq();
+ pixelsPerCol = od.getPixelsPerCol();
+ colsPerPixel = Math.max(1, 1f / pixelsPerCol);
+
}
final static int STATE_INIT = 0;
private Integer row;
- void mainLoop()
+ /**
+ * Draw alignment rows and columns onto an image. This method is asynchronous
+ * in JavaScript and interruptible in Java.
+ *
+ * Whether hidden rows or columns are drawn depends upon the type of
+ * collection.
+ *
+ * Updated to skip through high-density sequences, where columns/pixels > 1.
+ *
+ * When the process is complete, the image is passed to the AlignmentViewPanel
+ * provided by the constructor.
+ *
+ * @param rows
+ * collection of rows to be drawn
+ * @param cols
+ * collection of columns to be drawn
+ * @return image containing the drawing
+ *
+ * @author Bob Hanson 2019.07.30
+ */
+ public void drawMiniMe()
{
- while (!redraw)
+ state = STATE_INIT;
+ mainLoop();
+ }
+
+ protected void mainLoop()
+ {
+ out: while (!redraw)
{
switch (state)
{
case STATE_INIT:
- seqIndex = 0;
- pixelRow = 0;
+ init();
state = STATE_NEXT;
continue;
case STATE_NEXT:
- if (iter.hasNext())
+ if (!rowIterator.hasNext())
{
- nextRow();
+ state = STATE_DONE;
+ continue;
}
- else
+ nextRow();
+ if (!loop())
{
- state = STATE_DONE;
+ continue;
}
- break;
- case STATE_DONE:
- done();
- return;
- }
- if (delay > 0)
- {
- jsloop();
return;
+ case STATE_DONE:
+ break out;
}
+ // Java will continue without a timeout
}
done();
}
- private void jsloop()
+ private void init()
{
- if (timer == null)
- {
- timer = new Timer(delay, new ActionListener()
- {
- @Override
- public void actionPerformed(ActionEvent e)
- {
- mainLoop();
- }
+ rowIterator = rows.iterator();
+ seqIndex = 0;
+ pixelRow = 0;
+ lastRowUpdate = 0;
+ lastUpdate = 0;
+ totalPixels = w * alignmentHeight;
- });
- timer.setRepeats(false);
- timer.start();
- }
- else
+ if (showProgress)
{
- timer.restart();
+ changeSupport.firePropertyChange(UPDATE, -1, 0);
}
+
+ miniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
+ WritableRaster raster = miniMe.getRaster();
+ DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
+ Platform.timeCheck(null, Platform.TIME_MARK);
+ pixels = db.getBankData()[0];
+ bscol = cols.getOverviewBitSet();
}
private void nextRow()
{
- row = iter.next();
+ row = rowIterator.next();
// System.out.println("OR row " + r);
// get details of this alignment row
SequenceI seq = rows.getSequence(row);
// calculate where this row extends to in pixels
int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq), h);
-
- for (int pixelCol = 0, colIndex = 0, c = bscol
- .nextSetBit(0); c >= 0; c = bscol.nextSetBit(c + 1))
+ for (int pixelCol = 0, colNext = 0, pixelEnd = 0, icol = bscol
+ .nextSetBit(0); icol >= 0; icol = getNextCol(icol, pixelEnd))
{
if (redraw)
{
break;
}
- // calculate where this column extends to in pixels
- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), w);
-
- // don't do expensive colour determination if we're not going to use it
- // NB this is important to avoid performance issues in the overview
- // panel
+ ++colNext;
+ pixelEnd = getNextPixel(colNext, colNext);
- if (pixelCol < endCol)
+ if (pixelCol == pixelEnd)
{
- // System.out.println("OR pc ec " + pixelCol + " " + endCol);
- int rgb = getColumnColourFromSequence(allGroups, seq, c);
+ break;
+ }
+ else if (pixelCol < pixelEnd)
+ {
+ int rgb = getColumnColourFromSequence(allGroups, seq, icol);
// fill in the appropriate number of pixels
+ // System.out.println(
+ // "OR colNext=" + colNext + " " + pixelCol
+ // + "-" + pixelEnd + " icol=" + icol + " " + rgb + " "
+ // + pixelsPerCol);
for (int row = pixelRow; row < endRow; ++row)
{
- for (int col = pixelCol; col < endCol; ++col)
+ for (int col = pixelCol; col < pixelEnd; ++col)
{
// BH 2019.07.27 was:
//
ndone++;
}
}
- // }
-
- pixelCol = endCol;
+ pixelCol = pixelEnd;
// store last update value
if (showProgress)
{
- lastUpdate = sendProgressUpdate(endCol * (endRow - 1 - pixelRow),
+ lastUpdate = sendProgressUpdate(
+ pixelEnd * (endRow - 1 - pixelRow),
totalPixels, lastRowUpdate, lastUpdate);
}
}
+
}
if (pixelRow < endRow)
{
}
}
+ /**
+ * The next column is either the next set bit (when there are multiple pixels
+ * per column) or the next set bit for the column that aligns with the next
+ * pixel (when there are more columns than pixels).
+ *
+ * @param icol
+ * @param pixel
+ * @return
+ */
+ private int getNextCol(int icol, int pixel)
+ {
+ return bscol.nextSetBit(
+ pixelsPerCol >= 1 ? icol + 1 : (int) (pixel * colsPerPixel));
+ }
+
+ private int getNextPixel(int icol, int pixel)
+ {
+ return Math.min(
+ pixelsPerCol >= 1 || pixel == 0
+ ? Math.round(icol * pixelsPerCol)
+ : pixel,
+ w);
+ }
+
+ private boolean loop()
+ {
+ if (delay <= 0)
+ {
+ return false;
+ }
+ if (timer == null)
+ {
+ timer = new Timer(delay, new ActionListener()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ mainLoop();
+ }
+
+ });
+ timer.setRepeats(false);
+ timer.start();
+ }
+ else
+ {
+ timer.restart();
+ }
+ return true;
+ }
+
private void done()
{
Platform.timeCheck(
+ redraw,
Platform.TIME_MARK);
- overlayHiddenRegions(rows, cols);
+ overlayHiddenRegions();
if (showProgress)
{
// final update to progress bar if present
private AlignmentColsCollectionI cols;
- Iterator<Integer> iter;
+ Iterator<Integer> rowIterator;
int alignmentHeight;
int[] pixels;
- BitSet bscol = new BitSet();
+ BitSet bscol;
int w, h;
- /**
- * 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(AlignmentRowsCollectionI rows,
- AlignmentColsCollectionI cols)
- {
- this.rows = rows;
- this.cols = cols;
- iter = rows.iterator();
-
- w = miniMe.getWidth();
- h = miniMe.getHeight();
- alignmentHeight = h - graphHeight;
- totalPixels = w * alignmentHeight;
- lastRowUpdate = 0;
- lastUpdate = 0;
-
- if (showProgress)
- {
- changeSupport.firePropertyChange(UPDATE, -1, 0);
- }
-
- WritableRaster raster = miniMe.getRaster();
- DataBufferInt db = (DataBufferInt) raster.getDataBuffer();
- Platform.timeCheck(null, Platform.TIME_MARK);
- pixels = db.getBankData()[0];
- bscol.clear();
- for (int c : cols)
- {
- bscol.set(c);
- }
- state = STATE_INIT;
- mainLoop();
-
- return miniMe;
- }
-
/*
* Calculate progress update value and fire event
* @param rowOffset number of rows to offset calculation by
{
return (seq == null || icol >= seq.getLength()
? resColFinder.GAP_COLOUR
- : resColFinder.getResidueColour(true, shader, allGroups, seq,
- icol, finder)).getRGB();
+ : resColFinder.getResidueColourInt(true, shader, allGroups, seq,
+ icol, finder));
}
/**
* Overlay the hidden regions on the overview image
*
- * @param rows
- * collection of rows the overview is built over
- * @param cols
- * collection of columns the overview is built over
*/
- private void overlayHiddenRegions(AlignmentRowsCollectionI rows,
- AlignmentColsCollectionI cols)
+ private void overlayHiddenRegions()
{
if (cols.hasHidden() || rows.hasHidden())
{
- BufferedImage mask = buildHiddenImage(rows, cols, miniMe.getWidth(),
- miniMe.getHeight());
+ BufferedImage mask = buildHiddenImage();
Graphics2D g = (Graphics2D) miniMe.getGraphics();
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
* height of overview in pixels
* @return BufferedImage containing mask of hidden regions
*/
- private BufferedImage buildHiddenImage(AlignmentRowsCollectionI rows,
- AlignmentColsCollectionI cols, int width, int height)
+ private BufferedImage buildHiddenImage()
{
// new masking image
- BufferedImage hiddenImage = new BufferedImage(width, height,
+ BufferedImage hiddenImage = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Color hidden = resColFinder.getHiddenColour();
Graphics2D g2d = (Graphics2D) hiddenImage.getGraphics();
+ g2d.setColor(hidden);
// set background to transparent
// g2d.setComposite(AlphaComposite.Clear);
// g2d.fillRect(0, 0, width, height);
// set next colour to opaque
g2d.setComposite(AlphaComposite.Src);
+ // System.out.println(cols.getClass().getName());
if (cols.hasHidden())
{
- int colIndex = 0;
- int pixelCol = 0;
- for (int alignmentCol : cols)
+ // AllColsCollection only
+ BitSet bs = cols.getHiddenBitSet();
+ for (int pixelCol = -1, icol2 = 0, icol = bs
+ .nextSetBit(0); icol >= 0; icol = bs.nextSetBit(icol2))
{
if (redraw)
{
break;
}
-
- // calculate where this column extends to in pixels
- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol),
- width);
-
- // endCol is one more than old endCol
- if (pixelCol < endCol)
+ icol2 = bs.nextClearBit(icol + 1);
+ int pixelEnd = getNextPixel(icol2, 0);
+ if (pixelEnd > pixelCol)
{
- // determine the colour based on the sequence and column position
- if (cols.isHidden(alignmentCol))
- {
- g2d.setColor(hidden);
- g2d.fillRect(pixelCol, 0, endCol - pixelCol, height);
- }
- pixelCol = endCol;
+ pixelCol = getNextPixel(icol, 0);
+ g2d.fillRect(pixelCol, 0, Math.max(1, pixelEnd - pixelCol),
+ h);
+ pixelCol = pixelEnd;
}
}
}
// calculate where this row extends to in pixels
int endRow = Math.min(Math.round((++seqIndex) * pixelsPerSeq),
- height);
+ h);
// get details of this alignment row
if (rows.isHidden(alignmentRow))
{
- g2d.setColor(hidden);
- g2d.fillRect(0, pixelRow, width, endRow - 1 - pixelRow);
+ g2d.fillRect(0, pixelRow, w, endRow - 1 - pixelRow);
}
pixelRow = endRow;
}
/**
* Draw the alignment annotation in the overview panel
*
- * @param g
- * the graphics object to draw on
* @param anno
* alignment annotation information
- * @param y
- * y-position for the annotation graph
- * @param cols
- * the collection of columns used in the overview panel
*/
- public void drawGraph(Graphics g, AlignmentAnnotation anno, int y,
- AlignmentColsCollectionI cols)
+ public void drawGraph(AlignmentAnnotation anno)
{
+ int y = graphHeight;
+ Graphics g = miniMe.getGraphics();
+ g.translate(0, alignmentHeight);
+
Annotation[] annotations = anno.annotations;
float max = anno.graphMax;
g.setColor(Color.white);
- int width = miniMe.getWidth();
- g.fillRect(0, 0, width, y);
+ g.fillRect(0, 0, w, y);
- int colIndex = 0;
- int pixelCol = 0;
- for (int icol : cols)
+ for (int pixelCol = 0, colNext = 0, pixelEnd = 0, len = annotations.length, icol = bscol
+ .nextSetBit(0); icol >= 0
+ && icol < len; icol = getNextCol(icol, pixelEnd))
{
if (redraw)
{
break;
}
- if (icol >= annotations.length)
- {
- break; // no more annotations to draw here
- }
- int endCol = Math.min(Math.round((++colIndex) * pixelsPerCol), width);
+ ++colNext;
+ pixelEnd = getNextPixel(colNext, colNext);
Annotation ann = annotations[icol];
if (ann != null)
{
Color color = ann.colour;
g.setColor(color == null ? Color.black : color);
-
int height = Math.min(y, (int) ((ann.value / max) * y));
- g.fillRect(pixelCol, y - height, endCol - pixelCol, height);
+ g.fillRect(pixelCol, y - height, Math.max(1, pixelEnd - pixelCol),
+ height);
}
- pixelCol = endCol;
+ pixelCol = pixelEnd;
}
+
+ g.translate(0, -alignmentHeight);
+ g.dispose();
+
if (showProgress)
{
changeSupport.firePropertyChange(UPDATE, MAX_PROGRESS - 1,
MAX_PROGRESS);
}
+
}
/**
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
import jalview.util.Comparison;
import java.awt.Color;
public class OverviewResColourFinder extends ResidueColourFinder
{
- final Color GAP_COLOUR; // default colour to use at gaps
+ final int GAP_COLOUR; // default colour to use at gaps
- final Color RESIDUE_COLOUR; // default colour to use at residues
+ final int RESIDUE_COLOUR; // default colour to use at residues
final Color HIDDEN_COLOUR; // colour for hidden regions
{
if (useLegacyColouring)
{
- GAP_COLOUR = Color.white;
- RESIDUE_COLOUR = Color.lightGray;
- HIDDEN_COLOUR = hiddenCol;
+ GAP_COLOUR = Color.white.getRGB();
+ RESIDUE_COLOUR = Color.lightGray.getRGB();
}
else
{
- GAP_COLOUR = gapCol;
- RESIDUE_COLOUR = Color.white;
- HIDDEN_COLOUR = hiddenCol;
+ GAP_COLOUR = gapCol.getRGB();
+ RESIDUE_COLOUR = Color.white.getRGB();
}
+ HIDDEN_COLOUR = hiddenCol;
}
- @Override
- public Color getBoxColour(ResidueShaderI shader, SequenceI seq, int i)
+ public int getBoxColourInt(ResidueShaderI shader, SequenceI seq, int i)
{
- seq.resetColors();
- Color c = seq.getColor(i);
- if (c != null)
- {
- return c;
- }
char currentChar = seq.getCharAt(i);
// In the overview window, gaps are coloured grey, unless the colour scheme
// specifies a gap colour, in which case gaps honour the colour scheme
boolean isGap = Comparison.isGap(currentChar);
if (shader.getColourScheme() == null)
{
- return seq.setColor(i, isGap ? GAP_COLOUR : RESIDUE_COLOUR);
+ return (isGap ? GAP_COLOUR : RESIDUE_COLOUR);
}
- return seq.setColor(i,
- isGap && !shader.getColourScheme().hasGapColour() ? GAP_COLOUR
- : shader.findColour(currentChar, i, seq));
+ return (isGap && !shader.getColourScheme().hasGapColour() ? GAP_COLOUR
+ : shader.findColour(currentChar, i, seq).getRGB());
}
+ public int getResidueColourInt(boolean showBoxes, ResidueShaderI shader,
+ SequenceGroup[] allGroups, final SequenceI seq, int i,
+ FeatureColourFinder finder)
+ {
+
+ int c = seq.getColor(i);
+ if (c != 0)
+ {
+ return c;
+ }
+
+ int col = getResidueBoxColourInt(showBoxes, shader, allGroups, seq,
+ i);
+
+
+ // if there's a FeatureColourFinder we might override the residue colour
+ // here with feature colouring
+ return seq.setColor(i,
+ finder == null || finder.noFeaturesDisplayed() ? col
+ : finder.findFeatureColourInt(col, seq, i));
+ }
+
/**
- * {@inheritDoc} In the overview, the showBoxes setting is ignored, as the
- * overview displays the colours regardless.
+ * In the overview, the showBoxes setting is ignored, as the overview displays
+ * the colours regardless.
*/
- @Override
- protected Color getResidueBoxColour(boolean showBoxes,
+ protected int getResidueBoxColourInt(boolean showBoxes,
ResidueShaderI shader, SequenceGroup[] allGroups, SequenceI seq,
int i)
{
i);
ResidueShaderI currentShader = (currentSequenceGroup == null ? shader
: currentSequenceGroup.getGroupColourScheme());
- return getBoxColour(currentShader, seq, i);
+ return getBoxColourInt(currentShader, seq, i);
}
/**
public SequenceGroup getCurrentSequenceGroup(SequenceGroup[] allGroups,
int res)
{
- if (allGroups == null)
+ int n;
+ if (allGroups == null || (n = allGroups.length) == 0)
{
return null;
}
- for (int i = 0; i < allGroups.length; i++)
+ for (int i = 0; i < n; i++)
{
if ((allGroups[i].getStartRes() <= res)
&& (allGroups[i].getEndRes() >= res))
return c;
}
+ public int findFeatureColourInt(int defaultColour, SequenceI seq,
+ int column)
+ {
+ // if (noFeaturesDisplayed())
+ // {
+ // return defaultColour;
+ // }
+
+ Graphics g = null;
+
+ /*
+ * if transparency applies, provide a notional 1x1 graphics context
+ * that has been primed with the default colour
+ */
+ if (featureRenderer.getTransparency() != 1f)
+ {
+ g = goff;
+ if (defaultColour != 0)
+ {
+ offscreenImage.setRGB(0, 0, defaultColour);
+ }
+ }
+
+ Color c = featureRenderer.findFeatureColour(seq, column + 1, g);
+ if (c == null)
+ {
+ return defaultColour;
+ }
+
+ if (g != null)
+ {
+ return offscreenImage.getRGB(0, 0);
+ }
+ return c.getRGB();
+ }
/**
* Answers true if feature display is turned off, or there are no features
* configured to be visible
*
* @return
*/
- boolean noFeaturesDisplayed()
+ public boolean noFeaturesDisplayed()
{
if (featureRenderer == null
|| !featureRenderer.getViewport().isShowSequenceFeatures())
boxY = Math.round(vpbox.y / heightRatio);
// boxWidth is the width in residues translated to pixels
- boxWidth = Math.round(vpbox.width / widthRatio);
+ boxWidth = Math.max(1, Math.round(vpbox.width / widthRatio));
// boxHeight is the height in sequences translated to pixels
- boxHeight = Math.round(vpbox.height / heightRatio);
+ boxHeight = Math.max(1, Math.round(vpbox.height / heightRatio));
}
/**
boolean findingFeatures = false;
+ int nup;
+
protected boolean updateFeatures()
{
if (av.getFeaturesDisplayed() == null || renderOrder == null
|| newFeatureAdded)
{
+ System.out.println("updateFeatures " + ++nup);
findAllFeatures();
if (av.getFeaturesDisplayed().getVisibleFeatureCount() < 1)
{