From: kiramt Date: Wed, 22 Mar 2017 12:36:24 +0000 (+0000) Subject: Merge remote-tracking branch 'origin/develop' into X-Git-Tag: Release_2_10_2~3^2~150 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=6e5286721b2711415a80d34364ae24b7a81fe2f9;hp=32f013e4de40a95ac786d49a30e802a00bb8f1ba;p=jalview.git Merge remote-tracking branch 'origin/develop' into bug/JAL-2436featureRendererThreading Conflicts: src/jalview/appletgui/OverviewPanel.java src/jalview/gui/OverviewPanel.java --- diff --git a/src/MCview/AppletPDBCanvas.java b/src/MCview/AppletPDBCanvas.java index aac796c..3ae0650 100644 --- a/src/MCview/AppletPDBCanvas.java +++ b/src/MCview/AppletPDBCanvas.java @@ -28,6 +28,7 @@ import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; import jalview.io.StructureFile; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.AtomSpec; import jalview.structure.StructureListener; import jalview.structure.StructureMapping; @@ -577,6 +578,8 @@ public class AppletPDBCanvas extends Panel implements MouseListener, showFeatures = true; } + FeatureColourFinder finder = new FeatureColourFinder(fr); + PDBChain chain; if (bysequence && pdb != null) { @@ -604,25 +607,16 @@ public class AppletPDBCanvas extends Panel implements MouseListener, if (pos > 0) { pos = sequence[s].findIndex(pos); - tmp.startCol = sr.getResidueBoxColour(sequence[s], pos); - if (showFeatures) - { - tmp.startCol = fr.findFeatureColour(tmp.startCol, - sequence[s], pos); - } + tmp.startCol = sr.getResidueColour(sequence[s], pos, + finder); } pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1; if (pos > 0) { pos = sequence[s].findIndex(pos); - tmp.endCol = sr.getResidueBoxColour(sequence[s], pos); - if (showFeatures) - { - tmp.endCol = fr.findFeatureColour(tmp.endCol, - sequence[s], pos); - } + tmp.endCol = sr + .getResidueColour(sequence[s], pos, finder); } - } } } diff --git a/src/MCview/PDBCanvas.java b/src/MCview/PDBCanvas.java index 292de91..08bca8c 100644 --- a/src/MCview/PDBCanvas.java +++ b/src/MCview/PDBCanvas.java @@ -28,6 +28,7 @@ import jalview.gui.FeatureRenderer; import jalview.gui.SequenceRenderer; import jalview.io.DataSourceType; import jalview.io.StructureFile; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.AtomSpec; import jalview.structure.StructureListener; import jalview.structure.StructureMapping; @@ -546,6 +547,7 @@ public class PDBCanvas extends JPanel implements MouseListener, showFeatures = true; } + FeatureColourFinder finder = new FeatureColourFinder(fr); PDBChain chain; if (bysequence && pdb != null) { @@ -573,23 +575,15 @@ public class PDBCanvas extends JPanel implements MouseListener, if (pos > 0) { pos = sequence[s].findIndex(pos); - tmp.startCol = sr.getResidueBoxColour(sequence[s], pos); - if (showFeatures) - { - tmp.startCol = fr.findFeatureColour(tmp.startCol, - sequence[s], pos); - } + tmp.startCol = sr.getResidueColour(sequence[s], pos, + finder); } pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1; if (pos > 0) { pos = sequence[s].findIndex(pos); - tmp.endCol = sr.getResidueBoxColour(sequence[s], pos); - if (showFeatures) - { - tmp.endCol = fr.findFeatureColour(tmp.endCol, - sequence[s], pos); - } + tmp.endCol = sr + .getResidueColour(sequence[s], pos, finder); } } diff --git a/src/jalview/api/FeatureRenderer.java b/src/jalview/api/FeatureRenderer.java index f54231e..7123b8c 100644 --- a/src/jalview/api/FeatureRenderer.java +++ b/src/jalview/api/FeatureRenderer.java @@ -24,6 +24,7 @@ import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import java.awt.Color; +import java.awt.Graphics; import java.util.List; import java.util.Map; @@ -37,18 +38,32 @@ public interface FeatureRenderer { /** - * compute the perceived colour for a given column position in sequenceI, - * taking transparency and feature visibility into account. + * Computes the feature colour for a given sequence and column position, + * taking into account sequence feature locations, feature colour schemes, + * render ordering, feature and feature group visibility, and transparency. + *

+ * The graphics argument should be provided if transparency is applied + * (getTransparency() < 1). With feature transparency, visible features are + * written to the graphics context and the composite colour may be read off + * from it. In this case, the returned feature colour is not the composite + * colour but that of the last feature drawn. + *

+ * If no transparency applies, then the graphics argument may be null, and the + * returned colour is the one that would be drawn for the feature. + *

+ * Returns null if there is no visible feature at the position. + *

+ * This is provided to support rendering of feature colours other than on the + * sequence alignment, including by structure viewers and the overview window. + * Note this method takes no account of whether the sequence or column is + * hidden. * - * @param col - * - background colour (due to alignment/group shading schemes, etc). - * @param sequenceI - * - sequence providing features - * @param r - * - column position + * @param sequence + * @param column + * @param g * @return */ - Color findFeatureColour(Color col, SequenceI sequenceI, int r); + Color findFeatureColour(SequenceI sequence, int column, Graphics g); /** * trigger the feature discovery process for a newly created feature renderer. @@ -170,4 +185,19 @@ public interface FeatureRenderer */ void setVisible(String featureType); + /** + * Sets the transparency value, between 0 (full transparency) and 1 (no + * transparency) + * + * @param value + */ + void setTransparency(float value); + + /** + * Returns the transparency value, between 0 (full transparency) and 1 (no + * transparency) + * + * @return + */ + float getTransparency(); } diff --git a/src/jalview/api/SequenceRenderer.java b/src/jalview/api/SequenceRenderer.java index d708902..54f7fb6 100644 --- a/src/jalview/api/SequenceRenderer.java +++ b/src/jalview/api/SequenceRenderer.java @@ -21,14 +21,14 @@ package jalview.api; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import java.awt.Color; public interface SequenceRenderer { - Color getResidueBoxColour(SequenceI sequenceI, int r); - - Color getResidueColour(SequenceI seq, int position, FeatureRenderer fr); + Color getResidueColour(SequenceI seq, int position, + FeatureColourFinder finder); } diff --git a/src/jalview/appletgui/AppletJmolBinding.java b/src/jalview/appletgui/AppletJmolBinding.java index f938cad..9b8a235 100644 --- a/src/jalview/appletgui/AppletJmolBinding.java +++ b/src/jalview/appletgui/AppletJmolBinding.java @@ -54,21 +54,7 @@ class AppletJmolBinding extends JalviewJmolBinding public jalview.api.FeatureRenderer getFeatureRenderer( AlignmentViewPanel alignment) { - AlignmentPanel ap = (AlignmentPanel) alignment; - if (appletJmolBinding.ap.av.isShowSequenceFeatures()) - { - if (appletJmolBinding.fr == null) - { - appletJmolBinding.fr = new jalview.appletgui.FeatureRenderer( - appletJmolBinding.ap.av); - } - - appletJmolBinding.fr - .transferSettings(appletJmolBinding.ap.seqPanel.seqCanvas - .getFeatureRenderer()); - } - - return appletJmolBinding.fr; + return appletJmolBinding.ap.getFeatureRenderer(); } @Override diff --git a/src/jalview/appletgui/ExtJmol.java b/src/jalview/appletgui/ExtJmol.java index 189fe88..b369318 100644 --- a/src/jalview/appletgui/ExtJmol.java +++ b/src/jalview/appletgui/ExtJmol.java @@ -82,10 +82,10 @@ public class ExtJmol extends JalviewJmolBinding @Override public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment) { - AlignmentPanel ap = (AlignmentPanel) alignment; - if (ap.av.isShowSequenceFeatures()) + AlignmentPanel alignPanel = (AlignmentPanel) alignment; + if (alignPanel.av.isShowSequenceFeatures()) { - return ap.getFeatureRenderer(); + return alignPanel.getFeatureRenderer(); } else { diff --git a/src/jalview/appletgui/FeatureRenderer.java b/src/jalview/appletgui/FeatureRenderer.java index 67ca8e9..b88a1dc 100644 --- a/src/jalview/appletgui/FeatureRenderer.java +++ b/src/jalview/appletgui/FeatureRenderer.java @@ -377,9 +377,6 @@ public class FeatureRenderer extends if (dialog.accept) { - // This ensures that the last sequence - // is refreshed and new features are rendered - lastSeq = null; lastFeatureAdded = name.getText().trim(); lastFeatureGroupAdded = source.getText().trim(); lastDescriptionAdded = description.getText().replace('\n', ' '); diff --git a/src/jalview/appletgui/FeatureSettings.java b/src/jalview/appletgui/FeatureSettings.java index 2c454a4..1b9fbf9 100755 --- a/src/jalview/appletgui/FeatureSettings.java +++ b/src/jalview/appletgui/FeatureSettings.java @@ -696,8 +696,7 @@ public class FeatureSettings extends Panel implements ItemListener, public void adjustmentValueChanged(AdjustmentEvent evt) { fr.setTransparency((100 - transparency.getValue()) / 100f); - ap.seqPanel.seqCanvas.repaint(); - + ap.paintAlignment(true); } class MyCheckbox extends Checkbox diff --git a/src/jalview/appletgui/OverviewPanel.java b/src/jalview/appletgui/OverviewPanel.java index e2d986e..2fc5716 100755 --- a/src/jalview/appletgui/OverviewPanel.java +++ b/src/jalview/appletgui/OverviewPanel.java @@ -21,6 +21,7 @@ package jalview.appletgui; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; @@ -248,6 +249,7 @@ public class OverviewPanel extends Panel implements Runnable, int sameCol = 0; SequenceI seq = null; + FeatureColourFinder finder = new FeatureColourFinder(fr); final boolean hasHiddenCols = av.hasHiddenColumns(); boolean hiddenRow = false; @@ -277,7 +279,7 @@ public class OverviewPanel extends Panel implements Runnable, lastcol = (int) (col * sampleCol); color = getColumnColourFromSequence(seq, hiddenRow, - hasHiddenCols, lastcol); + hasHiddenCols, lastcol, finder); mg.setColor(color); if (sameCol == 1 && sameRow == 1) @@ -305,21 +307,12 @@ public class OverviewPanel extends Panel implements Runnable, */ private Color getColumnColourFromSequence( jalview.datamodel.SequenceI seq, boolean hiddenRow, - boolean hasHiddenCols, int lastcol) + boolean hasHiddenCols, int lastcol, FeatureColourFinder finder) { - Color color; + Color color = Color.white; if (seq.getLength() > lastcol) { - color = sr.getResidueBoxColour(seq, lastcol); - - if (av.isShowSequenceFeatures()) - { - color = fr.findFeatureColour(color, seq, lastcol); - } - } - else - { - color = Color.white; + color = sr.getResidueColour(seq, lastcol, finder); } if (hiddenRow diff --git a/src/jalview/appletgui/SeqCanvas.java b/src/jalview/appletgui/SeqCanvas.java index b28b800..ed8a46d 100755 --- a/src/jalview/appletgui/SeqCanvas.java +++ b/src/jalview/appletgui/SeqCanvas.java @@ -635,7 +635,7 @@ public class SeqCanvas extends Panel if (av.isShowSequenceFeatures()) { fr.drawSequence(g, nextSeq, startRes, endRes, offset - + ((i - startSeq) * avcharHeight)); + + ((i - startSeq) * avcharHeight), false); } // / Highlight search Results once all sequences have been drawn diff --git a/src/jalview/appletgui/SequenceRenderer.java b/src/jalview/appletgui/SequenceRenderer.java index 86d1f98..78ed4a3 100755 --- a/src/jalview/appletgui/SequenceRenderer.java +++ b/src/jalview/appletgui/SequenceRenderer.java @@ -20,10 +20,10 @@ */ package jalview.appletgui; -import jalview.api.FeatureRenderer; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.ResidueShaderI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import java.awt.Color; import java.awt.Font; @@ -69,8 +69,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer this.renderGaps = renderGaps; } - @Override - public Color getResidueBoxColour(SequenceI seq, int i) + protected Color getResidueBoxColour(SequenceI seq, int i) { allGroups = av.getAlignment().findAllGroups(seq); @@ -96,20 +95,20 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer * * @param seq * @param position - * @param fr + * @param finder * @return */ @Override public Color getResidueColour(final SequenceI seq, int position, - FeatureRenderer fr) + FeatureColourFinder finder) { // TODO replace 8 or so code duplications with calls to this method // (refactored as needed) Color col = getResidueBoxColour(seq, position); - if (fr != null) + if (finder != null) { - col = fr.findFeatureColour(col, seq, position); + col = finder.findFeatureColour(col, seq, position); } return col; } diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 82e188f..94df99a 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -20,7 +20,7 @@ */ package jalview.ext.jmol; -import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; @@ -499,17 +499,15 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel /** * @param files * @param sr - * @param fr - * @param viewport + * @param viewPanel * @return */ @Override protected StructureMappingcommandSet[] getColourBySequenceCommands( - String[] files, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) + String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel) { return JmolCommands.getColourBySequenceCommand(getSsm(), files, - getSequence(), sr, fr, viewport); + getSequence(), sr, viewPanel); } /** diff --git a/src/jalview/ext/jmol/JmolCommands.java b/src/jalview/ext/jmol/JmolCommands.java index a809cae..4212749 100644 --- a/src/jalview/ext/jmol/JmolCommands.java +++ b/src/jalview/ext/jmol/JmolCommands.java @@ -21,11 +21,13 @@ package jalview.ext.jmol; import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; @@ -53,9 +55,12 @@ public class JmolCommands */ public static StructureMappingcommandSet[] getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) + SequenceI[][] sequence, SequenceRenderer sr, + AlignmentViewPanel viewPanel) { + FeatureRenderer fr = viewPanel.getFeatureRenderer(); + FeatureColourFinder finder = new FeatureColourFinder(fr); + AlignViewportI viewport = viewPanel.getAlignViewport(); ColumnSelection cs = viewport.getColumnSelection(); AlignmentI al = viewport.getAlignment(); List cset = new ArrayList(); @@ -97,12 +102,8 @@ public class JmolCommands lastPos = pos; - Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r); - - if (fr != null) - { - col = fr.findFeatureColour(col, sequence[pdbfnum][s], r); - } + Color col = sr.getResidueColour(sequence[pdbfnum][s], r, + finder); /* * shade hidden regions darker diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index e665982..1d8b944 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -21,12 +21,14 @@ package jalview.ext.rbvi.chimera; import jalview.api.AlignViewportI; +import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; @@ -59,16 +61,16 @@ public class ChimeraCommands * @param sequence * @param sr * @param fr - * @param viewport + * @param viewPanel * @return */ public static StructureMappingcommandSet[] getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) + SequenceI[][] sequence, SequenceRenderer sr, + AlignmentViewPanel viewPanel) { Map colourMap = buildColoursMap(ssm, files, - sequence, sr, fr, viewport); + sequence, sr, viewPanel); List colourCommands = buildColourCommands(colourMap); @@ -186,9 +188,12 @@ public class ChimeraCommands */ protected static Map buildColoursMap( StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) + SequenceI[][] sequence, SequenceRenderer sr, + AlignmentViewPanel viewPanel) { + FeatureRenderer fr = viewPanel.getFeatureRenderer(); + FeatureColourFinder finder = new FeatureColourFinder(fr); + AlignViewportI viewport = viewPanel.getAlignViewport(); ColumnSelection cs = viewport.getColumnSelection(); AlignmentI al = viewport.getAlignment(); Map colourMap = new LinkedHashMap(); @@ -228,7 +233,7 @@ public class ChimeraCommands continue; } - Color colour = sr.getResidueColour(seq, r, fr); + Color colour = sr.getResidueColour(seq, r, finder); /* * darker colour for hidden regions @@ -310,16 +315,15 @@ public class ChimeraCommands * @param ssm * @param files * @param seqs - * @param fr - * @param alignment + * @param viewPanel * @return */ public static StructureMappingcommandSet getSetAttributeCommandsForFeatures( StructureSelectionManager ssm, String[] files, - SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment) + SequenceI[][] seqs, AlignmentViewPanel viewPanel) { Map> featureMap = buildFeaturesMap( - ssm, files, seqs, fr, alignment); + ssm, files, seqs, viewPanel); List commands = buildSetAttributeCommands(featureMap); @@ -339,22 +343,28 @@ public class ChimeraCommands * @param ssm * @param files * @param seqs - * @param fr - * @param alignment + * @param viewPanel * @return */ protected static Map> buildFeaturesMap( StructureSelectionManager ssm, String[] files, - SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment) + SequenceI[][] seqs, AlignmentViewPanel viewPanel) { Map> theMap = new LinkedHashMap>(); + FeatureRenderer fr = viewPanel.getFeatureRenderer(); + if (fr == null) + { + return theMap; + } + List visibleFeatures = fr.getDisplayedFeatureTypes(); if (visibleFeatures.isEmpty()) { return theMap; } + AlignmentI alignment = viewPanel.getAlignment(); for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 9a570fc..fad3137 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -20,9 +20,7 @@ */ package jalview.ext.rbvi.chimera; -import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; -import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; @@ -175,11 +173,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { getSsm().addStructureViewerListener(this); // ssm.addSelectionListener(this); - FeatureRenderer fr = getFeatureRenderer(null); - if (fr != null) - { - fr.featuresAdded(); - } refreshGUI(); } return true; @@ -699,17 +692,15 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel /** * @param files * @param sr - * @param fr - * @param viewport + * @param viewPanel * @return */ @Override protected StructureMappingcommandSet[] getColourBySequenceCommands( - String[] files, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) + String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel) { return ChimeraCommands.getColourBySequenceCommand(getSsm(), files, - getSequence(), sr, fr, viewport); + getSequence(), sr, viewPanel); } /** @@ -1110,15 +1101,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { // TODO refactor as required to pull up to an interface AlignmentI alignment = avp.getAlignment(); - FeatureRenderer fr = getFeatureRenderer(avp); - - /* - * fr is null if feature display is turned off - */ - if (fr == null) - { - return 0; - } String[] files = getPdbFile(); if (files == null) @@ -1128,7 +1110,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel StructureMappingcommandSet commandSet = ChimeraCommands .getSetAttributeCommandsForFeatures(getSsm(), files, - getSequence(), fr, alignment); + getSequence(), avp); String[] commands = commandSet.commands; if (commands.length > 10) { diff --git a/src/jalview/ext/varna/VarnaCommands.java b/src/jalview/ext/varna/VarnaCommands.java index a41e10e..d65f1d5 100644 --- a/src/jalview/ext/varna/VarnaCommands.java +++ b/src/jalview/ext/varna/VarnaCommands.java @@ -20,10 +20,10 @@ */ package jalview.ext.varna; -import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureSelectionManager; @@ -47,7 +47,8 @@ public class VarnaCommands */ public static String[] getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, + SequenceI[][] sequence, SequenceRenderer sr, + FeatureColourFinder finder, AlignmentI alignment) { ArrayList str = new ArrayList(); @@ -58,7 +59,9 @@ public class VarnaCommands StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); if (mapping == null || mapping.length < 1) + { continue; + } int lastPos = -1; for (int s = 0; s < sequence[pdbfnum].length; s++) @@ -79,14 +82,15 @@ public class VarnaCommands int pos = mapping[m].getPDBResNum(asp.findPosition(r)); if (pos < 1 || pos == lastPos) + { continue; + } lastPos = pos; - Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r); + Color col = sr.getResidueColour(sequence[pdbfnum][s], r, + finder); - if (fr != null) - col = fr.findFeatureColour(col, sequence[pdbfnum][s], r); String newSelcom = (mapping[m].getChain() != " " ? ":" + mapping[m].getChain() : "") + "/" diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java index 75e0c5e..f822358 100644 --- a/src/jalview/gui/AppJmolBinding.java +++ b/src/jalview/gui/AppJmolBinding.java @@ -40,8 +40,6 @@ public class AppJmolBinding extends JalviewJmolBinding { private AppJmol appJmolWindow; - private FeatureRenderer fr = null; - public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm, PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol) { @@ -50,26 +48,6 @@ public class AppJmolBinding extends JalviewJmolBinding } @Override - public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment) - { - AlignmentPanel ap = (alignment == null) ? appJmolWindow - .getAlignmentPanel() : (AlignmentPanel) alignment; - if (ap.av.isShowSequenceFeatures()) - { - if (fr == null) - { - fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer(); - } - else - { - ap.updateFeatureRenderer(fr); - } - } - - return fr; - } - - @Override public SequenceRenderer getSequenceRenderer(AlignmentViewPanel alignment) { return new SequenceRenderer(((AlignmentPanel) alignment).av); @@ -215,4 +193,18 @@ public class AppJmolBinding extends JalviewJmolBinding { return appJmolWindow; } + + @Override + public jalview.api.FeatureRenderer getFeatureRenderer( + AlignmentViewPanel alignment) + { + AlignmentPanel ap = (alignment == null) ? appJmolWindow + .getAlignmentPanel() : (AlignmentPanel) alignment; + if (ap.av.isShowSequenceFeatures()) + { + return ap.av.getAlignPanel().getSeqPanel().seqCanvas.fr; + } + + return null; + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index fe4a000..ec9feb7 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import jalview.api.FeatureRenderer; import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.PDBEntry; @@ -613,6 +614,16 @@ public class ChimeraViewFrame extends StructureViewerBase jmb.setFinishedInit(true); jmb.setLoadingFromArchive(false); + /* + * ensure that any newly discovered features (e.g. RESNUM) + * are added to any open feature settings dialog + */ + FeatureRenderer fr = getBinding().getFeatureRenderer(null); + if (fr != null) + { + fr.featuresAdded(); + } + // refresh the sequence colours for the new structure(s) for (AlignmentPanel ap : _colourwith) { diff --git a/src/jalview/gui/FeatureRenderer.java b/src/jalview/gui/FeatureRenderer.java index ed6a3c5..f519f99 100644 --- a/src/jalview/gui/FeatureRenderer.java +++ b/src/jalview/gui/FeatureRenderer.java @@ -46,7 +46,6 @@ import java.util.Comparator; import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; @@ -61,8 +60,7 @@ import javax.swing.SwingConstants; * @version $Revision$ */ public class FeatureRenderer extends - jalview.renderer.seqfeatures.FeatureRenderer implements - jalview.api.FeatureRenderer + jalview.renderer.seqfeatures.FeatureRenderer { Color resBoxColour; @@ -339,9 +337,6 @@ public class FeatureRenderer extends if (reply == JvOptionPane.OK_OPTION && name.getText().length() > 0) { - // This ensures that the last sequence - // is refreshed and new features are rendered - lastSeq = null; lastFeatureAdded = name.getText().trim(); lastFeatureGroupAdded = source.getText().trim(); lastDescriptionAdded = description.getText().replaceAll("\n", " "); diff --git a/src/jalview/gui/JalviewChimeraBindingModel.java b/src/jalview/gui/JalviewChimeraBindingModel.java index a1f05bd..c9b35d8 100644 --- a/src/jalview/gui/JalviewChimeraBindingModel.java +++ b/src/jalview/gui/JalviewChimeraBindingModel.java @@ -34,9 +34,6 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding { private ChimeraViewFrame cvf; - private FeatureRenderer fr = null; - - public JalviewChimeraBindingModel(ChimeraViewFrame chimeraViewFrame, StructureSelectionManager ssm, PDBEntry[] pdbentry, SequenceI[][] sequenceIs, DataSourceType protocol) @@ -52,17 +49,10 @@ public class JalviewChimeraBindingModel extends JalviewChimeraBinding : (AlignmentPanel) alignment; if (ap.av.isShowSequenceFeatures()) { - if (fr == null) - { - fr = (jalview.gui.FeatureRenderer) ap.cloneFeatureRenderer(); - } - else - { - ap.updateFeatureRenderer(fr); - } + return ap.getSeqPanel().seqCanvas.fr; } - return fr; + return null; } @Override diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index 67cdbc2..ac2138f 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -22,6 +22,7 @@ package jalview.gui; import jalview.datamodel.SequenceI; import jalview.renderer.AnnotationRenderer; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; @@ -70,7 +71,7 @@ public class OverviewPanel extends JPanel implements Runnable // main visible SeqCanvas private SequenceRenderer sr; - private jalview.renderer.seqfeatures.FeatureRenderer fr; + jalview.renderer.seqfeatures.FeatureRenderer fr; /** * Creates a new OverviewPanel object. @@ -87,7 +88,7 @@ public class OverviewPanel extends JPanel implements Runnable sr = new SequenceRenderer(av); sr.renderGaps = false; sr.forOverview = true; - fr = new FeatureRenderer(alPanel); + fr = new FeatureRenderer(ap); od = new OverviewDimensions(av.getRanges(), av.isShowAnnotation()); @@ -238,9 +239,10 @@ public class OverviewPanel extends JPanel implements Runnable { int lastcol = -1; int lastrow = -1; - int color = Color.white.getRGB(); + int rgbColour = Color.white.getRGB(); SequenceI seq = null; + FeatureColourFinder finder = new FeatureColourFinder(fr); final boolean hasHiddenCols = av.hasHiddenColumns(); boolean hiddenRow = false; @@ -267,18 +269,18 @@ public class OverviewPanel extends JPanel implements Runnable { if (doCopy) { - color = miniMe.getRGB(col, row - 1); + rgbColour = miniMe.getRGB(col, row - 1); } else if ((int) (col * sampleCol) != lastcol || (int) (row * sampleRow) != lastrow) { lastcol = (int) (col * sampleCol); - color = getColumnColourFromSequence(seq, hiddenRow, hasHiddenCols, - lastcol); + rgbColour = 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, color); + miniMe.setRGB(col, row, rgbColour); } } } @@ -286,37 +288,26 @@ public class OverviewPanel extends JPanel implements Runnable /* * Find the colour of a sequence at a specified column position */ - private int getColumnColourFromSequence(jalview.datamodel.SequenceI seq, - boolean hiddenRow, boolean hasHiddenCols, int lastcol) + private int getColumnColourFromSequence( + jalview.datamodel.SequenceI seq, + boolean hiddenRow, boolean hasHiddenCols, int lastcol, + FeatureColourFinder finder) { - int color; + Color color = Color.white; - if (seq == null) + if ((seq != null) && (seq.getLength() > lastcol)) { - color = Color.white.getRGB(); - } - else if (seq.getLength() > lastcol) - { - color = sr.getResidueBoxColour(seq, lastcol).getRGB(); - - if (av.isShowSequenceFeatures()) - { - color = fr.findFeatureColour(color, seq, lastcol); - } - } - else - { - color = Color.white.getRGB(); + color = sr.getResidueColour(seq, lastcol, finder); } if (hiddenRow || (hasHiddenCols && !av.getColumnSelection() .isVisible(lastcol))) { - color = new Color(color).darker().darker().getRGB(); + color = color.darker().darker(); } - return color; + return color.getRGB(); } /** diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 0593e24..4557819 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -737,7 +737,7 @@ public class SeqCanvas extends JComponent if (av.isShowSequenceFeatures()) { fr.drawSequence(g, nextSeq, startRes, endRes, offset - + ((i - startSeq) * charHeight)); + + ((i - startSeq) * charHeight), false); } // / Highlight search Results once all sequences have been drawn diff --git a/src/jalview/gui/SequenceRenderer.java b/src/jalview/gui/SequenceRenderer.java index 95c3261..1c0420d 100755 --- a/src/jalview/gui/SequenceRenderer.java +++ b/src/jalview/gui/SequenceRenderer.java @@ -20,10 +20,10 @@ */ package jalview.gui; -import jalview.api.FeatureRenderer; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; import jalview.renderer.ResidueShaderI; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.util.Comparison; import java.awt.Color; @@ -53,14 +53,13 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer boolean forOverview = false; /** - * Creates a new SequenceRenderer object. + * Creates a new SequenceRenderer object * - * @param av - * DOCUMENT ME! + * @param viewport */ - public SequenceRenderer(AlignViewport av) + public SequenceRenderer(AlignViewport viewport) { - this.av = av; + this.av = viewport; } /** @@ -83,8 +82,7 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer this.renderGaps = renderGaps; } - @Override - public Color getResidueBoxColour(SequenceI seq, int i) + protected Color getResidueBoxColour(SequenceI seq, int i) { // rate limiting step when rendering overview for lots of groups allGroups = av.getAlignment().findAllGroups(seq); @@ -111,20 +109,18 @@ public class SequenceRenderer implements jalview.api.SequenceRenderer * * @param seq * @param position - * @param fr + * @param finder * @return */ @Override public Color getResidueColour(final SequenceI seq, int position, - FeatureRenderer fr) + FeatureColourFinder finder) { - // TODO replace 8 or so code duplications with calls to this method - // (refactored as needed) Color col = getResidueBoxColour(seq, position); - if (fr != null) + if (finder != null) { - col = fr.findFeatureColour(col, seq, position); + col = finder.findFeatureColour(col, seq, position); } return col; } diff --git a/src/jalview/io/JSONFile.java b/src/jalview/io/JSONFile.java index 27ebe5a..053a65e 100644 --- a/src/jalview/io/JSONFile.java +++ b/src/jalview/io/JSONFile.java @@ -46,6 +46,7 @@ import jalview.json.binding.biojson.v1.ColourSchemeMapper; import jalview.json.binding.biojson.v1.SequenceFeaturesPojo; import jalview.json.binding.biojson.v1.SequenceGrpPojo; import jalview.json.binding.biojson.v1.SequencePojo; +import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.schemes.JalviewColourScheme; import jalview.schemes.ResidueColourScheme; import jalview.util.ColorUtils; @@ -328,6 +329,8 @@ public class JSONFile extends AlignFile implements ComplexAlignFile return sequenceFeaturesPojo; } + FeatureColourFinder finder = new FeatureColourFinder(fr); + for (SequenceI seq : sqs) { SequenceI dataSetSequence = seq.getDatasetSequence(); @@ -350,7 +353,7 @@ public class JSONFile extends AlignFile implements ComplexAlignFile String.valueOf(seq.hashCode())); String featureColour = (fr == null) ? null : jalview.util.Format - .getHexString(fr.findFeatureColour(Color.white, seq, + .getHexString(finder.findFeatureColour(Color.white, seq, seq.findIndex(sf.getBegin()))); jsonFeature.setXstart(seq.findIndex(sf.getBegin()) - 1); jsonFeature.setXend(seq.findIndex(sf.getEnd())); diff --git a/src/jalview/javascript/MouseOverStructureListener.java b/src/jalview/javascript/MouseOverStructureListener.java index 6d366d0..7580222 100644 --- a/src/jalview/javascript/MouseOverStructureListener.java +++ b/src/jalview/javascript/MouseOverStructureListener.java @@ -221,8 +221,8 @@ public class MouseOverStructureListener extends JSFunctionExec implements ArrayList ccomands = new ArrayList(); ArrayList pdbfn = new ArrayList(); StructureMappingcommandSet[] colcommands = JmolCommands - .getColourBySequenceCommand(ssm, modelSet, sequence, sr, fr, - ((AlignmentViewPanel) source).getAlignViewport()); + .getColourBySequenceCommand(ssm, modelSet, sequence, sr, + (AlignmentViewPanel) source); if (colcommands == null) { return; diff --git a/src/jalview/renderer/seqfeatures/FeatureColourFinder.java b/src/jalview/renderer/seqfeatures/FeatureColourFinder.java new file mode 100644 index 0000000..1db2004 --- /dev/null +++ b/src/jalview/renderer/seqfeatures/FeatureColourFinder.java @@ -0,0 +1,124 @@ +package jalview.renderer.seqfeatures; + +import jalview.api.FeatureRenderer; +import jalview.api.FeaturesDisplayedI; +import jalview.datamodel.SequenceI; +import jalview.viewmodel.seqfeatures.FeatureRendererModel; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; + +/** + * A helper class to find feature colour using an associated FeatureRenderer + * + * @author gmcarstairs + * + */ +public class FeatureColourFinder +{ + /* + * the class we delegate feature finding to + */ + private FeatureRenderer featureRenderer; + + /* + * a 1-pixel image on which features can be drawn, for the case where + * transparency allows 'see-through' of multiple feature colours + */ + private BufferedImage offscreenImage; + + /** + * Constructor + * + * @param fr + */ + public FeatureColourFinder(FeatureRenderer fr) + { + featureRenderer = fr; + offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + } + + /** + * Answers the feature colour to show for the given sequence and column + * position. This delegates to the FeatureRenderer to find the colour, which + * will depend on feature location, visibility, ordering, colour scheme, and + * whether or not transparency is applied. For feature rendering with + * transparency, this class provides a dummy 'offscreen' graphics context + * where multiple feature colours can be overlaid and the combined colour read + * back. + *

+ * This method is not thread-safe when transparency is applied, since a shared + * BufferedImage would be used by all threads to hold the composite colour at + * a position. Each thread should use a separate instance of this class. + * + * @param defaultColour + * @param seq + * @param column + * alignment column position (base zero) + * @return + */ + public Color findFeatureColour(Color 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 = offscreenImage.getGraphics(); + if (defaultColour != null) + { + offscreenImage.setRGB(0, 0, defaultColour.getRGB()); + } + } + + Color c = featureRenderer.findFeatureColour(seq, column, g); + if (c == null) + { + return defaultColour; + } + + if (g != null) + { + c = new Color(offscreenImage.getRGB(0, 0)); + } + return c; + } + + /** + * Answers true if feature display is turned off, or there are no features + * configured to be visible + * + * @return + */ + boolean noFeaturesDisplayed() + { + if (featureRenderer == null + || !featureRenderer.getViewport().isShowSequenceFeatures()) + { + return true; + } + + if (!((FeatureRendererModel) featureRenderer).hasRenderOrder()) + { + return true; + } + + FeaturesDisplayedI displayed = featureRenderer.getFeaturesDisplayed(); + if (displayed == null || displayed.getVisibleFeatureCount() == 0) + { + return true; + } + + return false; + } +} diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index 9e0089f..72ac2c8 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -23,6 +23,7 @@ package jalview.renderer.seqfeatures; import jalview.api.AlignViewportI; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; +import jalview.util.Comparison; import jalview.viewmodel.seqfeatures.FeatureRendererModel; import java.awt.AlphaComposite; @@ -30,28 +31,11 @@ import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.image.BufferedImage; public class FeatureRenderer extends FeatureRendererModel { - - FontMetrics fm; - - int charOffset; - - boolean offscreenRender = false; - - protected SequenceI lastSeq; - - char s; - - int i; - - int av_charHeight, av_charWidth; - - boolean av_validCharWidth, av_isShowSeqFeatureHeight; - - private Integer currentColour; + private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite + .getInstance(AlphaComposite.SRC_OVER, 1.0f); /** * Constructor given a viewport @@ -63,273 +47,252 @@ public class FeatureRenderer extends FeatureRendererModel this.av = viewport; } - protected void updateAvConfig() + /** + * Renders the sequence using the given feature colour between the given start + * and end columns. Returns true if at least one column is drawn, else false + * (the feature range does not overlap the start and end positions). + * + * @param g + * @param seq + * @param featureStart + * @param featureEnd + * @param featureColour + * @param start + * @param end + * @param y1 + * @param colourOnly + * @return + */ + boolean renderFeature(Graphics g, SequenceI seq, int featureStart, + int featureEnd, Color featureColour, int start, int end, int y1, + boolean colourOnly) { - av_charHeight = av.getCharHeight(); - av_charWidth = av.getCharWidth(); - av_validCharWidth = av.isValidCharWidth(); - av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight(); - } + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + boolean validCharWidth = av.isValidCharWidth(); - void renderFeature(Graphics g, SequenceI seq, int fstart, int fend, - Color featureColour, int start, int end, int y1) - { - updateAvConfig(); - if (((fstart <= end) && (fend >= start))) + if (featureStart > end || featureEnd < start) { - if (fstart < start) - { // fix for if the feature we have starts before the sequence start, - fstart = start; // but the feature end is still valid!! - } - - if (fend >= end) - { - fend = end; - } - int pady = (y1 + av_charHeight) - av_charHeight / 5; - for (i = fstart; i <= fend; i++) - { - s = seq.getCharAt(i); - - if (jalview.util.Comparison.isGap(s)) - { - continue; - } - - g.setColor(featureColour); - - g.fillRect((i - start) * av_charWidth, y1, av_charWidth, - av_charHeight); - - if (offscreenRender || !av_validCharWidth) - { - continue; - } - - g.setColor(Color.white); - charOffset = (av_charWidth - fm.charWidth(s)) / 2; - g.drawString(String.valueOf(s), charOffset - + (av_charWidth * (i - start)), pady); + return false; + } - } + if (featureStart < start) + { + featureStart = start; } - } + if (featureEnd >= end) + { + featureEnd = end; + } + int pady = (y1 + charHeight) - charHeight / 5; - void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend, - Color featureColour, int start, int end, int y1, byte[] bs) - { - updateAvConfig(); - if (((fstart <= end) && (fend >= start))) + FontMetrics fm = g.getFontMetrics(); + for (int i = featureStart; i <= featureEnd; i++) { - if (fstart < start) - { // fix for if the feature we have starts before the sequence start, - fstart = start; // but the feature end is still valid!! - } + char s = seq.getCharAt(i); - if (fend >= end) - { - fend = end; - } - int pady = (y1 + av_charHeight) - av_charHeight / 5; - int ystrt = 0, yend = av_charHeight; - if (bs[0] != 0) - { - // signed - zero is always middle of residue line. - if (bs[1] < 128) - { - yend = av_charHeight * (128 - bs[1]) / 512; - ystrt = av_charHeight - yend / 2; - } - else - { - ystrt = av_charHeight / 2; - yend = av_charHeight * (bs[1] - 128) / 512; - } - } - else + if (Comparison.isGap(s)) { - yend = av_charHeight * bs[1] / 255; - ystrt = av_charHeight - yend; - + continue; } - for (i = fstart; i <= fend; i++) - { - s = seq.getCharAt(i); - - if (jalview.util.Comparison.isGap(s)) - { - continue; - } - g.setColor(featureColour); - int x = (i - start) * av_charWidth; - g.drawRect(x, y1, av_charWidth, av_charHeight); - g.fillRect(x, y1 + ystrt, av_charWidth, yend); + g.setColor(featureColour); - if (offscreenRender || !av_validCharWidth) - { - continue; - } + g.fillRect((i - start) * charWidth, y1, charWidth, + charHeight); - g.setColor(Color.black); - charOffset = (av_charWidth - fm.charWidth(s)) / 2; - g.drawString(String.valueOf(s), charOffset - + (av_charWidth * (i - start)), pady); + if (colourOnly || !validCharWidth) + { + continue; } - } - } - - BufferedImage offscreenImage; - @Override - public Color findFeatureColour(Color initialCol, SequenceI seq, int res) - { - return new Color(findFeatureColour(initialCol.getRGB(), seq, res)); + g.setColor(Color.white); + int charOffset = (charWidth - fm.charWidth(s)) / 2; + g.drawString(String.valueOf(s), charOffset + + (charWidth * (i - start)), pady); + } + return true; } /** - * This is used by Structure Viewers and the Overview Window to get the - * feature colour of the rendered sequence, returned as an RGB value + * Renders the sequence using the given SCORE feature colour between the given + * start and end columns. Returns true if at least one column is drawn, else + * false (the feature range does not overlap the start and end positions). * - * @param defaultColour + * @param g * @param seq - * @param column + * @param fstart + * @param fend + * @param featureColour + * @param start + * @param end + * @param y1 + * @param bs + * @param colourOnly * @return */ - public synchronized int findFeatureColour(int defaultColour, - final SequenceI seq, int column) + boolean renderScoreFeature(Graphics g, SequenceI seq, int fstart, + int fend, Color featureColour, int start, int end, int y1, + byte[] bs, boolean colourOnly) { - if (!av.isShowSequenceFeatures()) + if (fstart > end || fend < start) { - return defaultColour; + return false; } - SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures(); - if (seq != lastSeq) + if (fstart < start) + { // fix for if the feature we have starts before the sequence start, + fstart = start; // but the feature end is still valid!! + } + + if (fend >= end) + { + fend = end; + } + int charHeight = av.getCharHeight(); + int pady = (y1 + charHeight) - charHeight / 5; + int ystrt = 0, yend = charHeight; + if (bs[0] != 0) { - lastSeq = seq; - lastSequenceFeatures = sequenceFeatures; - if (lastSequenceFeatures != null) + // signed - zero is always middle of residue line. + if (bs[1] < 128) { - sfSize = lastSequenceFeatures.length; + yend = charHeight * (128 - bs[1]) / 512; + ystrt = charHeight - yend / 2; + } + else + { + ystrt = charHeight / 2; + yend = charHeight * (bs[1] - 128) / 512; } } else { - if (lastSequenceFeatures != sequenceFeatures) + yend = charHeight * bs[1] / 255; + ystrt = charHeight - yend; + + } + + FontMetrics fm = g.getFontMetrics(); + int charWidth = av.getCharWidth(); + + for (int i = fstart; i <= fend; i++) + { + char s = seq.getCharAt(i); + + if (Comparison.isGap(s)) { - lastSequenceFeatures = sequenceFeatures; - if (lastSequenceFeatures != null) - { - sfSize = lastSequenceFeatures.length; - } + continue; } + + g.setColor(featureColour); + int x = (i - start) * charWidth; + g.drawRect(x, y1, charWidth, charHeight); + g.fillRect(x, y1 + ystrt, charWidth, yend); + + if (colourOnly || !av.isValidCharWidth()) + { + continue; + } + + g.setColor(Color.black); + int charOffset = (charWidth - fm.charWidth(s)) / 2; + g.drawString(String.valueOf(s), charOffset + + (charWidth * (i - start)), pady); } + return true; + } - if (lastSequenceFeatures == null || sfSize == 0) + /** + * {@inheritDoc} + */ + @Override + public Color findFeatureColour(SequenceI seq, int column, Graphics g) + { + if (!av.isShowSequenceFeatures()) { - return defaultColour; + return null; } - if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column))) + SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures(); + + if (sequenceFeatures == null || sequenceFeatures.length == 0) { - return Color.white.getRGB(); + return null; } - // Only bother making an offscreen image if transparency is applied - if (transparency != 1.0f && offscreenImage == null) + if (Comparison.isGap(seq.getCharAt(column))) { - offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + return Color.white; } - currentColour = null; - // TODO: non-threadsafe - each rendering thread needs its own instance of - // the feature renderer - or this should be synchronized. - offscreenRender = true; - - if (offscreenImage != null) + Color renderedColour = null; + if (transparency == 1.0f) { - offscreenImage.setRGB(0, 0, defaultColour); - drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0); - - return offscreenImage.getRGB(0, 0); + /* + * simple case - just find the topmost rendered visible feature colour + */ + renderedColour = findFeatureColour(seq, seq.findPosition(column)); } else { - drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1); - - if (currentColour == null) - { - return defaultColour; - } - else - { - return currentColour.intValue(); - } + /* + * transparency case - draw all visible features in render order to + * build up a composite colour on the graphics context + */ + renderedColour = drawSequence(g, seq, column, column, 0, true); } - + return renderedColour; } - private volatile SequenceFeature[] lastSequenceFeatures; - - int sfSize; - - int sfindex; - - int spos; - - int epos; - /** - * Draws the sequence on the graphics context, or just determines the colour - * that would be drawn (if flag offscreenrender is true). + * Draws the sequence features on the graphics context, or just determines the + * colour that would be drawn (if flag colourOnly is true). Returns the last + * colour drawn (which may not be the effective colour if transparency + * applies), or null if no feature is drawn in the range given. * * @param g + * the graphics context to draw on (may be null if colourOnly==true) * @param seq * @param start - * start column (or sequence position in offscreenrender mode) + * start column * @param end - * end column (not used in offscreenrender mode) + * end column * @param y1 * vertical offset at which to draw on the graphics + * @param colourOnly + * if true, only do enough to determine the colour for the position, + * do not draw the character + * @return */ - public synchronized void drawSequence(Graphics g, final SequenceI seq, - int start, int end, int y1) + public synchronized Color drawSequence(final Graphics g, + final SequenceI seq, int start, int end, int y1, + boolean colourOnly) { SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures(); if (sequenceFeatures == null || sequenceFeatures.length == 0) { - return; - } - - if (g != null) - { - fm = g.getFontMetrics(); + return null; } updateFeatures(); - if (lastSeq == null || seq != lastSeq - || sequenceFeatures != lastSequenceFeatures) - { - lastSeq = seq; - lastSequenceFeatures = sequenceFeatures; - } - - if (transparency != 1 && g != null) + if (transparency != 1f && g != null) { Graphics2D g2 = (Graphics2D) g; g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency)); } - if (!offscreenRender) - { - spos = lastSeq.findPosition(start); - epos = lastSeq.findPosition(end); - } + int startPos = seq.findPosition(start); + int endPos = seq.findPosition(end); + + int sfSize = sequenceFeatures.length; + Color drawnColour = null; - sfSize = lastSequenceFeatures.length; + /* + * iterate over features in ordering of their rendering (last is on top) + */ for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++) { String type = renderOrder[renderIndex]; @@ -340,27 +303,29 @@ public class FeatureRenderer extends FeatureRendererModel // loop through all features in sequence to find // current feature to render - for (sfindex = 0; sfindex < sfSize; sfindex++) + for (int sfindex = 0; sfindex < sfSize; sfindex++) { - final SequenceFeature sequenceFeature = lastSequenceFeatures[sfindex]; + final SequenceFeature sequenceFeature = sequenceFeatures[sfindex]; if (!sequenceFeature.type.equals(type)) { continue; } + /* + * a feature type may be flagged as shown but the group + * an instance of it belongs to may be hidden + */ if (featureGroupNotShown(sequenceFeature)) { continue; } /* - * check feature overlaps the visible part of the alignment, - * unless doing offscreenRender (to the Overview window or a - * structure viewer) which is not limited + * check feature overlaps the target range + * TODO: efficient retrieval of features overlapping a range */ - if (!offscreenRender - && (sequenceFeature.getBegin() > epos || sequenceFeature - .getEnd() < spos)) + if (sequenceFeature.getBegin() > endPos + || sequenceFeature.getEnd() < startPos) { continue; } @@ -368,58 +333,46 @@ public class FeatureRenderer extends FeatureRendererModel Color featureColour = getColour(sequenceFeature); boolean isContactFeature = sequenceFeature.isContactFeature(); - if (offscreenRender && offscreenImage == null) - { - /* - * offscreen mode with no image (image is only needed if transparency - * is applied to feature colours) - just check feature is rendered at - * the requested position (start == sequence position in this mode) - */ - boolean featureIsAtPosition = sequenceFeature.begin <= start - && sequenceFeature.end >= start; - if (isContactFeature) - { - featureIsAtPosition = sequenceFeature.begin == start - || sequenceFeature.end == start; - } - if (featureIsAtPosition) - { - // this is passed out to the overview and other sequence renderers - // (e.g. molecule viewer) to get displayed colour for rendered - // sequence - currentColour = new Integer(featureColour.getRGB()); - // used to be retreived from av.featuresDisplayed - // currentColour = av.featuresDisplayed - // .get(sequenceFeatures[sfindex].type); - - } - } - else if (isContactFeature) + if (isContactFeature) { - renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1, + boolean drawn = renderFeature(g, seq, + seq.findIndex(sequenceFeature.begin) - 1, seq.findIndex(sequenceFeature.begin) - 1, featureColour, - start, end, y1); - renderFeature(g, seq, seq.findIndex(sequenceFeature.end) - 1, + start, end, y1, colourOnly); + drawn |= renderFeature(g, seq, + seq.findIndex(sequenceFeature.end) - 1, seq.findIndex(sequenceFeature.end) - 1, featureColour, - start, end, y1); - + start, end, y1, colourOnly); + if (drawn) + { + drawnColour = featureColour; + } } else if (showFeature(sequenceFeature)) { - if (av_isShowSeqFeatureHeight + if (av.isShowSequenceFeaturesHeight() && !Float.isNaN(sequenceFeature.score)) { - renderScoreFeature(g, seq, + boolean drawn = renderScoreFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1, - seq.findIndex(sequenceFeature.end) - 1, - featureColour, start, end, y1, - normaliseScore(sequenceFeature)); + seq.findIndex(sequenceFeature.end) - 1, featureColour, + start, end, y1, normaliseScore(sequenceFeature), + colourOnly); + if (drawn) + { + drawnColour = featureColour; + } } else { - renderFeature(g, seq, seq.findIndex(sequenceFeature.begin) - 1, - seq.findIndex(sequenceFeature.end) - 1, - featureColour, start, end, y1); + boolean drawn = renderFeature(g, seq, + seq.findIndex(sequenceFeature.begin) - 1, + seq.findIndex(sequenceFeature.end) - 1, featureColour, + start, end, y1, colourOnly); + if (drawn) + { + drawnColour = featureColour; + } } } } @@ -427,10 +380,14 @@ public class FeatureRenderer extends FeatureRendererModel if (transparency != 1.0f && g != null) { + /* + * reset transparency + */ Graphics2D g2 = (Graphics2D) g; - g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, - 1.0f)); + g2.setComposite(NO_TRANSPARENCY); } + + return drawnColour; } /** @@ -459,7 +416,78 @@ public class FeatureRenderer extends FeatureRendererModel @Override public void featuresAdded() { - lastSeq = null; findAllFeatures(); } + + /** + * Returns the sequence feature colour rendered at the given sequence + * position, or null if none found. The feature of highest render order (i.e. + * on top) is found, subject to both feature type and feature group being + * visible, and its colour returned. + * + * @param seq + * @param pos + * @return + */ + Color findFeatureColour(SequenceI seq, int pos) + { + SequenceFeature[] sequenceFeatures = seq.getSequenceFeatures(); + if (sequenceFeatures == null || sequenceFeatures.length == 0) + { + return null; + } + + /* + * check for new feature added while processing + */ + updateFeatures(); + + /* + * inspect features in reverse renderOrder (the last in the array is + * displayed on top) until we find one that is rendered at the position + */ + for (int renderIndex = renderOrder.length - 1; renderIndex >= 0; renderIndex--) + { + String type = renderOrder[renderIndex]; + if (!showFeatureOfType(type)) + { + continue; + } + + for (int sfindex = 0; sfindex < sequenceFeatures.length; sfindex++) + { + SequenceFeature sequenceFeature = sequenceFeatures[sfindex]; + if (!sequenceFeature.type.equals(type)) + { + continue; + } + + if (featureGroupNotShown(sequenceFeature)) + { + continue; + } + + /* + * check the column position is within the feature range + * (or is one of the two contact positions for a contact feature) + */ + boolean featureIsAtPosition = sequenceFeature.begin <= pos + && sequenceFeature.end >= pos; + if (sequenceFeature.isContactFeature()) + { + featureIsAtPosition = sequenceFeature.begin == pos + || sequenceFeature.end == pos; + } + if (featureIsAtPosition) + { + return getColour(sequenceFeature); + } + } + } + + /* + * no displayed feature found at position + */ + return null; + } } diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 7d57886..84475fe 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -20,9 +20,7 @@ */ package jalview.structures.models; -import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; -import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.api.StructureSelectionManagerProvider; import jalview.api.structures.JalviewStructureDisplayI; @@ -724,18 +722,7 @@ public abstract class AAStructureBindingModel extends public abstract void setBackgroundColour(Color col); protected abstract StructureMappingcommandSet[] getColourBySequenceCommands( - String[] files, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI alignViewportI); - - /** - * returns the current featureRenderer that should be used to colour the - * structures - * - * @param alignment - * - * @return - */ - public abstract FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment); + String[] files, SequenceRenderer sr, AlignmentViewPanel avp); /** * returns the current sequenceRenderer that should be used to colour the @@ -773,16 +760,8 @@ public abstract class AAStructureBindingModel extends SequenceRenderer sr = getSequenceRenderer(alignmentv); - FeatureRenderer fr = null; - boolean showFeatures = alignmentv.getAlignViewport() - .isShowSequenceFeatures(); - if (showFeatures) - { - fr = getFeatureRenderer(alignmentv); - } - StructureMappingcommandSet[] colourBySequenceCommands = getColourBySequenceCommands( - files, sr, fr, alignmentv.getAlignViewport()); + files, sr, alignmentv); colourBySequence(colourBySequenceCommands); } @@ -790,4 +769,7 @@ public abstract class AAStructureBindingModel extends { return fileLoadingError != null && fileLoadingError.length() > 0; } + + public abstract jalview.api.FeatureRenderer getFeatureRenderer( + AlignmentViewPanel alignment); } diff --git a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java index 8468329..84c9477 100644 --- a/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java +++ b/src/jalview/viewmodel/seqfeatures/FeatureRendererModel.java @@ -550,11 +550,13 @@ public abstract class FeatureRendererModel implements } /** - * calculate the render colour for a specific feature using current feature - * settings. + * Returns the configured colour for a particular feature instance. This + * includes calculation of 'colour by label', or of a graduated score colour, + * if applicable. It does not take into account feature visibility or colour + * transparency. * * @param feature - * @return render colour for the given feature + * @return */ public Color getColour(SequenceFeature feature) { @@ -586,11 +588,13 @@ public abstract class FeatureRendererModel implements featureColours.put(featureType, col); } + @Override public void setTransparency(float value) { transparency = value; } + @Override public float getTransparency() { return transparency; @@ -822,7 +826,7 @@ public abstract class FeatureRendererModel implements * @return list of groups */ @Override - public List getGroups(boolean visible) + public List getGroups(boolean visible) { if (featureGroups != null) { diff --git a/test/jalview/ext/jmol/JmolCommandsTest.java b/test/jalview/ext/jmol/JmolCommandsTest.java index 89da196..2c23311 100644 --- a/test/jalview/ext/jmol/JmolCommandsTest.java +++ b/test/jalview/ext/jmol/JmolCommandsTest.java @@ -58,7 +58,6 @@ public class JmolCommandsTest // need some mappings! StructureMappingcommandSet[] commands = JmolCommands - .getColourBySequenceCommand(ssm, files, seqs, sr, null, - af.getViewport()); + .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel); } } diff --git a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java index d351171..49a951e 100644 --- a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java +++ b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java @@ -202,8 +202,7 @@ public class ChimeraCommandsTest ssm.addStructureMapping(sm2); StructureMappingcommandSet[] commands = ChimeraCommands - .getColourBySequenceCommand(ssm, files, seqs, sr, null, - af.getViewport()); + .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel); assertEquals(1, commands.length); assertEquals(1, commands[0].commands.length); String theCommand = commands[0].commands[0]; diff --git a/test/jalview/gui/SequenceRendererTest.java b/test/jalview/gui/SequenceRendererTest.java index 81289b0..29a9a52 100644 --- a/test/jalview/gui/SequenceRendererTest.java +++ b/test/jalview/gui/SequenceRendererTest.java @@ -53,10 +53,10 @@ public class SequenceRendererTest av.setGlobalColourScheme(new ZappoColourScheme()); // @see ResidueProperties.zappo - assertEquals(Color.pink, sr.getResidueColour(seq, 0, null)); // M - assertEquals(Color.green, sr.getResidueColour(seq, 2, null)); // T - assertEquals(Color.magenta, sr.getResidueColour(seq, 5, null)); // G - assertEquals(Color.orange, sr.getResidueColour(seq, 12, null)); // F + assertEquals(Color.pink, sr.getResidueBoxColour(seq, 0)); // M + assertEquals(Color.green, sr.getResidueBoxColour(seq, 2)); // T + assertEquals(Color.magenta, sr.getResidueBoxColour(seq, 5)); // G + assertEquals(Color.orange, sr.getResidueBoxColour(seq, 12)); // F } // TODO more tests for getResidueBoxColour covering groups, feature rendering, // gaps, overview... diff --git a/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java new file mode 100644 index 0000000..59566ed --- /dev/null +++ b/test/jalview/renderer/seqfeatures/FeatureColourFinderTest.java @@ -0,0 +1,448 @@ +package jalview.renderer.seqfeatures; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import jalview.api.FeatureColourI; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.gui.AlignFrame; +import jalview.gui.AlignViewport; +import jalview.gui.FeatureRenderer; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; +import jalview.schemes.FeatureColour; + +import java.awt.Color; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +/** + * Unit tests for feature colour determination, including but not limited to + *

+ */ +public class FeatureColourFinderTest +{ + private AlignViewport av; + + private SequenceI seq; + + private FeatureColourFinder finder; + + private AlignFrame af; + + private FeatureRenderer fr; + + @BeforeTest(alwaysRun = true) + public void setUp() + { + // aligned column 8 is sequence position 6 + String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n"; + af = new FileLoader().LoadFileWaitTillLoaded(s, + DataSourceType.PASTE); + av = af.getViewport(); + seq = av.getAlignment().getSequenceAt(0); + fr = af.getFeatureRenderer(); + finder = new FeatureColourFinder(fr); + } + + /** + * Clear down any sequence features before each test (not as easy as it + * sounds...) + */ + @BeforeMethod(alwaysRun = true) + public void setUpBeforeTest() + { + SequenceFeature[] sfs = seq.getSequenceFeatures(); + if (sfs != null) + { + for (SequenceFeature sf : sfs) + { + seq.deleteFeature(sf); + } + } + fr.findAllFeatures(true); + + /* + * reset all feature groups to visible + */ + for (String group : fr.getGroups(false)) + { + fr.setGroupVisibility(group, true); + } + + fr.clearRenderOrder(); + av.setShowSequenceFeatures(true); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_noFeatures() + { + av.setShowSequenceFeatures(false); + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + + av.setShowSequenceFeatures(true); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_noFeaturesShown() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + fr.featuresAdded(); + av.setShowSequenceFeatures(false); + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_singleFeatureAtPosition() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + fr.setColour("Metal", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_gapPosition() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, 0f, + null)); + fr.setColour("Metal", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + Color c = finder.findFeatureColour(null, seq, 6); + assertEquals(c, Color.white); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency() + { + /* + * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any + * new features 'on top' (but reverses the order of any added features) + */ + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15, + Float.NaN, "DomainGroup")); + FeatureColour green = new FeatureColour(Color.green); + fr.setColour("Domain", green); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * expect Domain (green) to be rendered above Metal (red) + */ + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.green); + + /* + * now promote Metal above Domain + * - currently no way other than mimicking reordering of + * table in Feature Settings + */ + Object[][] data = new Object[2][]; + data[0] = new Object[] { "Metal", red, true }; + data[1] = new Object[] { "Domain", green, true }; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + + /* + * ..and turn off display of Metal + */ + data[0][2] = false; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.green); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_singleFeatureNotAtPosition() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12, + Float.NaN, "MetalGroup")); + fr.setColour("Metal", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + // column 2 = sequence position 3 + Color c = finder.findFeatureColour(Color.blue, seq, 2); + assertEquals(c, Color.blue); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_featureTypeNotDisplayed() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + + /* + * turn off display of Metal - is this the easiest way to do it?? + */ + Object[][] data = new Object[1][]; + data[0] = new Object[] { "Metal", red, false }; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + + /* + * turn display of Metal back on + */ + data[0] = new Object[] { "Metal", red, true }; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_featureGroupNotDisplayed() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + + /* + * turn off display of MetalGroup + */ + fr.setGroupVisibility("MetalGroup", false); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + + /* + * turn display of MetalGroup back on + */ + fr.setGroupVisibility("MetalGroup", true); + c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_contactFeature() + { + /* + * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !! + */ + seq.addSequenceFeature(new SequenceFeature("Disulphide Bond", + "Contact", 2, 12, Float.NaN, "Disulphide")); + fr.setColour("Disulphide Bond", new FeatureColour(Color.red)); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * Contact positions are residues 2 and 12 + * which are columns 1 and 14 + * positions in between don't count for a contact feature! + */ + Color c = finder.findFeatureColour(Color.blue, seq, 10); + assertEquals(c, Color.blue); + c = finder.findFeatureColour(Color.blue, seq, 8); + assertEquals(c, Color.blue); + c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, Color.red); + c = finder.findFeatureColour(Color.blue, seq, 14); + assertEquals(c, Color.red); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_graduatedFeatureColour() + { + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, + 2, 0f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4, + 4, 5f, "KdGroup")); + seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7, + 7, 10f, "KdGroup")); + + /* + * graduated colour from 0 to 10 + */ + Color min = new Color(100, 50, 150); + Color max = new Color(200, 0, 100); + FeatureColourI fc = new FeatureColour(min, max, 0, 10); + fr.setColour("kd", fc); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * position 2, column 1, score 0 - minimum colour in range + */ + Color c = finder.findFeatureColour(Color.blue, seq, 1); + assertEquals(c, min); + + /* + * position 7, column 9, score 10 - maximum colour in range + */ + c = finder.findFeatureColour(Color.blue, seq, 9); + assertEquals(c, max); + + /* + * position 4, column 3, score 5 - half way from min to max + */ + c = finder.findFeatureColour(Color.blue, seq, 3); + assertEquals(c, new Color(150, 25, 125)); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_transparencySingleFeature() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * the FeatureSettings transparency slider has range 0-70 which + * corresponds to a transparency value of 1 - 0.3 + * A value of 0.4 gives a combination of + * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153) + */ + fr.setTransparency(0.4f); + Color c = finder.findFeatureColour(Color.cyan, seq, 10); + assertEquals(c, new Color(102, 153, 153)); + } + + @Test(groups = "Functional") + public void testFindFeatureColour_transparencyTwoFeatures() + { + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15, + Float.NaN, "DomainGroup")); + FeatureColour green = new FeatureColour(Color.green); + fr.setColour("Domain", green); + fr.featuresAdded(); + av.setShowSequenceFeatures(true); + + /* + * Domain (green) rendered above Metal (red) above background (cyan) + * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102) + * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded + */ + fr.setTransparency(0.6f); + Color c = finder.findFeatureColour(Color.cyan, seq, 10); + assertEquals(c, new Color(61, 194, 41)); + + /* + * now promote Metal above Domain + * - currently no way other than mimicking reordering of + * table in Feature Settings + * Metal (red) rendered above Domain (green) above background (cyan) + * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102) + * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded + */ + Object[][] data = new Object[2][]; + data[0] = new Object[] { "Metal", red, true }; + data[1] = new Object[] { "Domain", green, true }; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.cyan, seq, 10); + assertEquals(c, new Color(153, 102, 41)); + + /* + * ..and turn off display of Metal + * Domain (green) above background (pink) + * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70) + */ + data[0][2] = false; + fr.setFeaturePriority(data); + c = finder.findFeatureColour(Color.pink, seq, 10); + assertEquals(c, new Color(102, 223, 70)); + } + + @Test(groups = "Functional") + public void testNoFeaturesDisplayed() + { + /* + * no features on alignment to render + */ + assertTrue(finder.noFeaturesDisplayed()); + + /* + * add a feature + * it will be automatically set visible but we leave + * the viewport configured not to show features + */ + av.setShowSequenceFeatures(false); + seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, + Float.NaN, "MetalGroup")); + FeatureColour red = new FeatureColour(Color.red); + fr.setColour("Metal", red); + fr.featuresAdded(); + assertTrue(finder.noFeaturesDisplayed()); + + /* + * turn on feature display + */ + av.setShowSequenceFeatures(true); + assertFalse(finder.noFeaturesDisplayed()); + + /* + * turn off display of Metal + */ + Object[][] data = new Object[1][]; + data[0] = new Object[] { "Metal", red, false }; + fr.setFeaturePriority(data); + assertTrue(finder.noFeaturesDisplayed()); + + /* + * turn display of Metal back on + */ + fr.setVisible("Metal"); + assertFalse(finder.noFeaturesDisplayed()); + + /* + * turn off MetalGroup - has no effect here since the group of a + * sequence feature instance is independent of its type + */ + fr.setGroupVisibility("MetalGroup", false); + assertFalse(finder.noFeaturesDisplayed()); + + /* + * a finder with no feature renderer + */ + FeatureColourFinder finder2 = new FeatureColourFinder(null); + assertTrue(finder2.noFeaturesDisplayed()); + } +} diff --git a/test/jalview/schemes/ColourSchemesTest.java b/test/jalview/schemes/ColourSchemesTest.java index 4618ed7..0aaa38c 100644 --- a/test/jalview/schemes/ColourSchemesTest.java +++ b/test/jalview/schemes/ColourSchemesTest.java @@ -228,9 +228,9 @@ public class ColourSchemesTest * set and check Taylor colours */ af.changeColour_actionPerformed(JalviewColourScheme.Taylor.toString()); - Color taylor1 = sr.getResidueBoxColour(seq, 88); // E 255,0,102 - Color taylor2 = sr.getResidueBoxColour(seq, 89); // A 204,255,0 - Color taylor3 = sr.getResidueBoxColour(seq, 90); // G 255,153,0 + Color taylor1 = sr.getResidueColour(seq, 88, null); // E 255,0,102 + Color taylor2 = sr.getResidueColour(seq, 89, null); // A 204,255,0 + Color taylor3 = sr.getResidueColour(seq, 90, null); // G 255,153,0 assertEquals(taylor1, new Color(255, 0, 102)); assertEquals(taylor2, new Color(204, 255, 0)); assertEquals(taylor3, new Color(255, 153, 0)); @@ -239,9 +239,9 @@ public class ColourSchemesTest * set and check Zappo colours */ af.changeColour_actionPerformed(JalviewColourScheme.Zappo.toString()); - Color zappo1 = sr.getResidueBoxColour(seq, 88); // E red - Color zappo2 = sr.getResidueBoxColour(seq, 89); // A pink - Color zappo3 = sr.getResidueBoxColour(seq, 90); // G magenta + Color zappo1 = sr.getResidueColour(seq, 88, null); // E red + Color zappo2 = sr.getResidueColour(seq, 89, null); // A pink + Color zappo3 = sr.getResidueColour(seq, 90, null); // G magenta assertEquals(zappo1, Color.red); assertEquals(zappo2, Color.pink); assertEquals(zappo3, Color.magenta); @@ -250,9 +250,9 @@ public class ColourSchemesTest * set 'stripy' colours - odd columns are Taylor and even are Zappo */ af.changeColour_actionPerformed("stripy"); - Color stripy1 = sr.getResidueBoxColour(seq, 88); - Color stripy2 = sr.getResidueBoxColour(seq, 89); - Color stripy3 = sr.getResidueBoxColour(seq, 90); + Color stripy1 = sr.getResidueColour(seq, 88, null); + Color stripy2 = sr.getResidueColour(seq, 89, null); + Color stripy3 = sr.getResidueColour(seq, 90, null); assertEquals(stripy1, zappo1); assertEquals(stripy2, taylor2); assertEquals(stripy3, zappo3); @@ -261,9 +261,9 @@ public class ColourSchemesTest * set and check Clustal colours */ af.changeColour_actionPerformed(JalviewColourScheme.Clustal.toString()); - Color clustal1 = sr.getResidueBoxColour(seq, 88); - Color clustal2 = sr.getResidueBoxColour(seq, 89); - Color clustal3 = sr.getResidueBoxColour(seq, 90); + Color clustal1 = sr.getResidueColour(seq, 88, null); + Color clustal2 = sr.getResidueColour(seq, 89, null); + Color clustal3 = sr.getResidueColour(seq, 90, null); assertEquals(clustal1, ClustalColour.MAGENTA.colour); assertEquals(clustal2, ClustalColour.BLUE.colour); assertEquals(clustal3, ClustalColour.ORANGE.colour); @@ -272,9 +272,9 @@ public class ColourSchemesTest * set 'MyClustal' colours - uses AWT colour equivalents */ af.changeColour_actionPerformed("MyClustal"); - Color myclustal1 = sr.getResidueBoxColour(seq, 88); - Color myclustal2 = sr.getResidueBoxColour(seq, 89); - Color myclustal3 = sr.getResidueBoxColour(seq, 90); + Color myclustal1 = sr.getResidueColour(seq, 88, null); + Color myclustal2 = sr.getResidueColour(seq, 89, null); + Color myclustal3 = sr.getResidueColour(seq, 90, null); assertEquals(myclustal1, Color.MAGENTA); assertEquals(myclustal2, Color.BLUE); assertEquals(myclustal3, Color.ORANGE); diff --git a/test/jalview/structures/models/AAStructureBindingModelTest.java b/test/jalview/structures/models/AAStructureBindingModelTest.java index c92daea..7ba22b4 100644 --- a/test/jalview/structures/models/AAStructureBindingModelTest.java +++ b/test/jalview/structures/models/AAStructureBindingModelTest.java @@ -24,7 +24,6 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; -import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; @@ -184,14 +183,7 @@ public class AAStructureBindingModelTest @Override protected StructureMappingcommandSet[] getColourBySequenceCommands( - String[] files, SequenceRenderer sr, FeatureRenderer fr, - AlignViewportI viewport) - { - return null; - } - - @Override - public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment) + String[] files, SequenceRenderer sr, AlignmentViewPanel avp) { return null; } @@ -218,6 +210,13 @@ public class AAStructureBindingModelTest public void colourByCharge() { } + + @Override + public FeatureRenderer getFeatureRenderer( + AlignmentViewPanel alignment) + { + return null; + } }; }