From cc4459eeccbe2e9bd0619f83c3a63170609be359 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Thu, 8 Aug 2019 16:01:24 +0100 Subject: [PATCH] JAL-3390 StructureCommands atomspec helper, corrected Jmol atomspec/test --- src/jalview/ext/jmol/JalviewJmolBinding.java | 15 +- src/jalview/ext/jmol/JmolCommands.java | 30 +-- src/jalview/ext/rbvi/chimera/ChimeraCommands.java | 233 +---------------- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 2 +- .../ext/rbvi/chimera/StructureCommands.java | 274 ++++++++++++++++++++ .../structures/models/AAStructureBindingModel.java | 126 --------- test/jalview/ext/jmol/JmolCommandsTest.java | 86 ++++-- .../ext/rbvi/chimera/ChimeraCommandsTest.java | 23 +- 8 files changed, 393 insertions(+), 396 deletions(-) create mode 100644 src/jalview/ext/rbvi/chimera/StructureCommands.java diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index c727baf..7acafd7 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -28,6 +28,7 @@ import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.AtomSpecModel; +import jalview.ext.rbvi.chimera.StructureCommands; import jalview.gui.IProgressIndicator; import jalview.io.DataSourceType; import jalview.io.StructureFile; @@ -458,7 +459,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel } // System.out.println("Select regions:\n" + selectioncom.toString()); evalStateCommand("select *; cartoons off; backbone; select (" - + selectioncom.toString() + "); cartoons; "); + + selectioncom.toString() + "); cartoons; zoom 0"); // evalStateCommand("select *; backbone; select "+selcom.toString()+"; // cartoons; center "+selcom.toString()); } @@ -515,7 +516,7 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel protected String[] getColourBySequenceCommands( String[] files, AlignmentViewPanel viewPanel) { - Map map = buildColoursMap(viewPanel); + Map map = StructureCommands.buildColoursMap(this, viewPanel); return JmolCommands.getColourBySequenceCommand(map); } @@ -1430,21 +1431,19 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel public void showStructures(AlignViewportI av, boolean refocus) { StringBuilder cmd = new StringBuilder(128); - if (isShowAlignmentOnly()) { - cmd.append("hide *;"); - AtomSpecModel model = getShownResidues(av); String atomSpec = JmolCommands.getAtomSpec(model); - cmd.append("display ").append(atomSpec); + cmd.append("hide *;display ").append(atomSpec) + .append("; select displayed"); } else { - cmd.append("display *"); + cmd.append(";display *"); } - cmd.append("; cartoon"); + cmd.append("; cartoon only"); if (refocus) { cmd.append("; zoom 0"); diff --git a/src/jalview/ext/jmol/JmolCommands.java b/src/jalview/ext/jmol/JmolCommands.java index e3625fa..512080a 100644 --- a/src/jalview/ext/jmol/JmolCommands.java +++ b/src/jalview/ext/jmol/JmolCommands.java @@ -28,6 +28,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.AtomSpecModel; +import jalview.ext.rbvi.chimera.StructureCommands; import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; @@ -40,15 +41,13 @@ import java.util.List; import java.util.Map; /** - * Routines for generating Jmol commands for Jalview/Jmol binding another - * cruisecontrol test. + * Routines for generating Jmol commands for Jalview/Jmol binding * * @author JimP * */ -public class JmolCommands +public class JmolCommands extends StructureCommands { - private static final String COMMA = ","; /** @@ -272,20 +271,19 @@ public class JmolCommands sb.append(COMMA); } boolean firstRange = true; - for (int[] range : atomSpecModel.getRanges(model, chain)) + + List rangeList = atomSpecModel.getRanges(model, chain); + + if (rangeList.size() > 1) { - if (!firstRange) - { - sb.append(COMMA); - } - firstRange = false; - sb.append(range[0]); - if (range[1] != range[0]) - { - sb.append("-").append(range[1]); - } + sb.append("("); + } + appendResidueRange(sb, rangeList, null, firstRange); + if (rangeList.size() > 1) + { + sb.append(")&"); } - sb.append(":").append(chain).append("/") + sb.append(":").append(chain.trim()).append("/") .append(String.valueOf(model + 1)) .append(".1"); } diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index 45b22f7..8c00e74 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -23,27 +23,20 @@ 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.HiddenColumns; import jalview.datamodel.MappedFeatures; import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.gui.Desktop; -import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; import jalview.util.ColorUtils; -import jalview.util.Comparison; -import jalview.util.IntRangeComparator; import java.awt.Color; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -54,7 +47,7 @@ import java.util.Map; * @author JimP * */ -public class ChimeraCommands +public class ChimeraCommands extends StructureCommands { public static final String NAMESPACE_PREFIX = "jv_"; @@ -123,161 +116,6 @@ public class ChimeraCommands } /** - * Build a data structure which records contiguous subsequences for each colour. - * From this we can easily generate the Chimera command for colour by sequence. - * - *
-   * Color
-   *     Model number
-   *         Chain
-   *             list of start/end ranges
-   * 
- * - * Ordering is by order of addition (for colours and positions), natural - * ordering (for models and chains) - * - * @param ssm - * @param files - * @param sequence - * @param sr - * @param hideHiddenRegions - * @param viewPanel - * @return - */ - protected static Map buildColoursMap( - StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, - boolean hideHiddenRegions, AlignmentViewPanel viewPanel) - { - FeatureRenderer fr = viewPanel.getFeatureRenderer(); - FeatureColourFinder finder = new FeatureColourFinder(fr); - AlignViewportI viewport = viewPanel.getAlignViewport(); - HiddenColumns cs = viewport.getAlignment().getHiddenColumns(); - AlignmentI al = viewport.getAlignment(); - Map colourMap = new LinkedHashMap<>(); - Color lastColour = null; - - for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) - { - StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); - - if (mapping == null || mapping.length < 1) - { - continue; - } - - int startPos = -1, lastPos = -1; - String lastChain = ""; - for (int s = 0; s < sequence[pdbfnum].length; s++) - { - for (int sp, m = 0; m < mapping.length; m++) - { - final SequenceI seq = sequence[pdbfnum][s]; - if (mapping[m].getSequence() == seq - && (sp = al.findIndex(seq)) > -1) - { - SequenceI asp = al.getSequenceAt(sp); - for (int r = 0; r < asp.getLength(); r++) - { - // no mapping to gaps in sequence - if (Comparison.isGap(asp.getCharAt(r))) - { - continue; - } - int pos = mapping[m].getPDBResNum(asp.findPosition(r)); - - if (pos < 1 || pos == lastPos) - { - continue; - } - - Color colour = sr.getResidueColour(seq, r, finder); - - /* - * hidden regions are shown gray or, optionally, ignored - */ - if (!cs.isVisible(r)) - { - if (hideHiddenRegions) - { - continue; - } - else - { - colour = Color.GRAY; - } - } - - final String chain = mapping[m].getChain(); - - /* - * Just keep incrementing the end position for this colour range - * _unless_ colour, PDB model or chain has changed, or there is a - * gap in the mapped residue sequence - */ - final boolean newColour = !colour.equals(lastColour); - final boolean nonContig = lastPos + 1 != pos; - final boolean newChain = !chain.equals(lastChain); - if (newColour || nonContig || newChain) - { - if (startPos != -1) - { - addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos, - lastPos, lastChain); - } - startPos = pos; - } - lastColour = colour; - lastPos = pos; - lastChain = chain; - } - // final colour range - if (lastColour != null) - { - addAtomSpecRange(colourMap, lastColour, pdbfnum, startPos, - lastPos, lastChain); - } - // break; - } - } - } - } - return colourMap; - } - - /** - * Helper method to add one contiguous range to the AtomSpec model for the given - * value (creating the model if necessary). As used by Jalview, {@code value} is - *
    - *
  • a colour, when building a 'colour structure by sequence' command
  • - *
  • a feature value, when building a 'set Chimera attributes from features' - * command
  • - *
- * - * @param map - * @param value - * @param model - * @param startPos - * @param endPos - * @param chain - */ - public static void addAtomSpecRange(Map map, - Object value, int model, int startPos, int endPos, String chain) - { - /* - * Get/initialize map of data for the colour - */ - AtomSpecModel atomSpec = map.get(value); - if (atomSpec == null) - { - atomSpec = new AtomSpecModel(); - map.put(value, atomSpec); - } - - atomSpec.addRange(model, startPos, endPos, chain); - } - - /** * Constructs and returns Chimera commands to set attributes on residues * corresponding to features in Jalview. Attribute names are the Jalview feature * type, with a "jv_" prefix. @@ -638,8 +476,6 @@ public class ChimeraCommands sb.append("|"); } firstModel = false; - // todo use JalviewChimeraBinding.getModelSpec(model) - // which means this cannot be static sb.append(binding.getModelSpec(model)).append(":"); boolean firstPositionForModel = true; @@ -650,77 +486,24 @@ public class ChimeraCommands List rangeList = atomSpec.getRanges(model, chain); - /* - * sort ranges into ascending start position order - */ - Collections.sort(rangeList, IntRangeComparator.ASCENDING); - - int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0]; - int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1]; - - Iterator iterator = rangeList.iterator(); - while (iterator.hasNext()) - { - int[] range = iterator.next(); - if (range[0] <= end + 1) - { - /* - * range overlaps or is contiguous with the last one - * - so just extend the end position, and carry on - * (unless this is the last in the list) - */ - end = Math.max(end, range[1]); - } - else - { - /* - * we have a break so append the last range - */ - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - start = range[0]; - end = range[1]; - } - } - - /* - * and append the last range - */ - if (!rangeList.isEmpty()) - { - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - } + String chainToken = " ".equals(chain) ? "." : "." + chain; + appendResidueRange(sb, rangeList, chainToken, + firstPositionForModel); + firstPositionForModel = false; } } return sb.toString(); } /** - * A helper method that appends one start-end range to a Chimera atomspec + * Chimera atomspec requires chain to be specified for each start-end residue + * range, otherwise it will apply to all chains * * @param sb - * @param start - * @param end * @param chain - * @param firstPositionForModel */ - static void appendRange(StringBuilder sb, int start, int end, - String chain, boolean firstPositionForModel) + protected static void appendChainToRange(StringBuilder sb, String chain) { - if (!firstPositionForModel) - { - sb.append(","); - } - if (end == start) - { - sb.append(start); - } - else - { - sb.append(start).append("-").append(end); - } - sb.append("."); if (!" ".equals(chain)) { diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index a5273f5..2bb24d9 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -671,7 +671,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel protected String[] getColourBySequenceCommands( String[] files, AlignmentViewPanel viewPanel) { - Map colourMap = buildColoursMap(viewPanel); + Map colourMap = StructureCommands.buildColoursMap(this, viewPanel); return ChimeraCommands.getColourBySequenceCommand(colourMap, this); } diff --git a/src/jalview/ext/rbvi/chimera/StructureCommands.java b/src/jalview/ext/rbvi/chimera/StructureCommands.java new file mode 100644 index 0000000..5f707ae --- /dev/null +++ b/src/jalview/ext/rbvi/chimera/StructureCommands.java @@ -0,0 +1,274 @@ +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.HiddenColumns; +import jalview.datamodel.SequenceI; +import jalview.renderer.seqfeatures.FeatureColourFinder; +import jalview.structure.StructureMapping; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.Comparison; +import jalview.util.IntRangeComparator; + +import java.awt.Color; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * A class with common methods for building commands for Jmol, Chimera, or other + * + * @author gmcarstairs + */ +public abstract class StructureCommands +{ + + /** + * Helper method to add one contiguous range to the AtomSpec model for the given + * value (creating the model if necessary). As used by Jalview, {@code value} is + *
    + *
  • a colour, when building a 'colour structure by sequence' command
  • + *
  • a feature value, when building a 'set Chimera attributes from features' + * command
  • + *
+ * + * @param map + * @param value + * @param model + * @param startPos + * @param endPos + * @param chain + */ + protected static void addAtomSpecRange(Map map, + Object value, + int model, int startPos, int endPos, String chain) + { + /* + * Get/initialize map of data for the colour + */ + AtomSpecModel atomSpec = map.get(value); + if (atomSpec == null) + { + atomSpec = new AtomSpecModel(); + map.put(value, atomSpec); + } + + atomSpec.addRange(model, startPos, endPos, chain); + } + + /** + * Build a data structure which records contiguous subsequences by model and + * chain. From this we can easily generate the Chimera or Jmol specific + * selection expression. + * + *
+   * Color
+   *     Model number
+   *         Chain
+   *             list of start/end ranges
+   * 
+ * + * Ordering is by order of addition (for colours and positions), natural + * ordering (for models and chains) + * + * @param viewPanel + * @return + */ + public static Map buildColoursMap( + AAStructureBindingModel binding, AlignmentViewPanel viewPanel) + { + FeatureRenderer fr = viewPanel.getFeatureRenderer(); + FeatureColourFinder finder = new FeatureColourFinder(fr); + AlignViewportI viewport = viewPanel.getAlignViewport(); + HiddenColumns cs = viewport.getAlignment().getHiddenColumns(); + AlignmentI al = viewport.getAlignment(); + SequenceRenderer sr = binding.getSequenceRenderer(viewPanel); + String[] files = binding.getStructureFiles(); + SequenceI[][] sequence = binding.getSequence(); + + Map colourMap = new LinkedHashMap<>(); + Color lastColour = null; + + for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) + { + StructureMapping[] mapping = binding.getSsm() + .getMapping(files[pdbfnum]); + + if (mapping == null || mapping.length < 1) + { + continue; + } + + int startPos = -1, lastPos = -1; + String lastChain = ""; + for (int s = 0; s < sequence[pdbfnum].length; s++) + { + for (int sp, m = 0; m < mapping.length; m++) + { + final SequenceI seq = sequence[pdbfnum][s]; + if (mapping[m].getSequence() == seq + && (sp = al.findIndex(seq)) > -1) + { + SequenceI asp = al.getSequenceAt(sp); + for (int r = 0; r < asp.getLength(); r++) + { + // no mapping to gaps in sequence + if (Comparison.isGap(asp.getCharAt(r))) + { + continue; + } + int pos = mapping[m].getPDBResNum(asp.findPosition(r)); + + if (pos < 1 || pos == lastPos) + { + continue; + } + + Color colour = sr.getResidueColour(seq, r, finder); + + /* + * hidden regions are shown gray or, optionally, ignored + */ + if (!cs.isVisible(r)) + { + if (binding.isHideHiddenRegions()) + { + continue; + } + else + { + colour = Color.GRAY; + } + } + + final String chain = mapping[m].getChain(); + + /* + * Just keep incrementing the end position for this colour range + * _unless_ colour, PDB model or chain has changed, or there is a + * gap in the mapped residue sequence + */ + final boolean newColour = !colour.equals(lastColour); + final boolean nonContig = lastPos + 1 != pos; + final boolean newChain = !chain.equals(lastChain); + if (newColour || nonContig || newChain) + { + if (startPos != -1) + { + StructureCommands.addAtomSpecRange(colourMap, lastColour, + pdbfnum, startPos, + lastPos, lastChain); + } + startPos = pos; + } + lastColour = colour; + lastPos = pos; + lastChain = chain; + } + // final colour range + if (lastColour != null) + { + StructureCommands.addAtomSpecRange(colourMap, lastColour, + pdbfnum, + startPos, lastPos, lastChain); + } + } + } + } + } + return colourMap; + } + + /** + * A helper method that takes a list of [start-end] ranges, format them as + * s1-e1,s2-e2 etc and appends to the string buffer. Ranges are sorted, and + * coalesced if they overlap. The chain token, if not null, is appended to each + * resulting subrange. + * + * @param sb + * @param rangeList + * @param chainToken + * @param firstPositionForModel + */ + protected static void appendResidueRange(StringBuilder sb, List rangeList, + String chainToken, boolean firstPositionForModel) + { + /* + * sort ranges into ascending start position order + */ + Collections.sort(rangeList, IntRangeComparator.ASCENDING); + + int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0]; + int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1]; + + Iterator iterator = rangeList.iterator(); + while (iterator.hasNext()) + { + int[] range = iterator.next(); + if (range[0] <= end + 1) + { + /* + * range overlaps or is contiguous with the last one + * - so just extend the end position, and carry on + * (unless this is the last in the list) + */ + end = Math.max(end, range[1]); + } + else + { + /* + * we have a break so append the last range + */ + appendRange(sb, start, end, chainToken, firstPositionForModel); + firstPositionForModel = false; + start = range[0]; + end = range[1]; + } + } + + /* + * and append the last range + */ + if (!rangeList.isEmpty()) + { + appendRange(sb, start, end, chainToken, firstPositionForModel); + } + } + + /** + * A helper method that appends one start-end range, and an (optional) chain + * token to an atomspec (a null token is not appended) + * + * @param sb + * @param start + * @param end + * @param chainToken + * @param firstPositionForModel + */ + protected static void appendRange(StringBuilder sb, int start, int end, + String chainToken, boolean firstPositionForModel) + { + if (!firstPositionForModel) + { + sb.append(","); + } + if (end == start) + { + sb.append(start); + } + else + { + sb.append(start).append("-").append(end); + } + if (chainToken != null) + { + sb.append(chainToken); + } + } + +} diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index b55885d..1dea2c6 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -22,7 +22,6 @@ 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; @@ -31,9 +30,7 @@ import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.AtomSpecModel; -import jalview.ext.rbvi.chimera.ChimeraCommands; import jalview.io.DataSourceType; -import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.schemes.ColourSchemeI; import jalview.structure.AtomSpec; import jalview.structure.StructureListener; @@ -48,9 +45,7 @@ import java.util.Arrays; import java.util.BitSet; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; /** * @@ -1051,127 +1046,6 @@ public abstract class AAStructureBindingModel } /** - * Build a data structure which records contiguous subsequences for each colour. - * From this we can easily generate the Chimera command for colour by sequence. - * - *
-   * Color
-   *     Model number
-   *         Chain
-   *             list of start/end ranges
-   * 
- * - * Ordering is by order of addition (for colours and positions), natural - * ordering (for models and chains) - * - * @param viewPanel - * @return - */ - public Map buildColoursMap( - AlignmentViewPanel viewPanel) - { - FeatureRenderer fr = viewPanel.getFeatureRenderer(); - FeatureColourFinder finder = new FeatureColourFinder(fr); - AlignViewportI viewport = viewPanel.getAlignViewport(); - HiddenColumns cs = viewport.getAlignment().getHiddenColumns(); - AlignmentI al = viewport.getAlignment(); - SequenceRenderer sr = getSequenceRenderer(viewPanel); - String[] files = getStructureFiles(); - - Map colourMap = new LinkedHashMap<>(); - Color lastColour = null; - - for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) - { - StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); - - if (mapping == null || mapping.length < 1) - { - continue; - } - - int startPos = -1, lastPos = -1; - String lastChain = ""; - for (int s = 0; s < sequence[pdbfnum].length; s++) - { - for (int sp, m = 0; m < mapping.length; m++) - { - final SequenceI seq = sequence[pdbfnum][s]; - if (mapping[m].getSequence() == seq - && (sp = al.findIndex(seq)) > -1) - { - SequenceI asp = al.getSequenceAt(sp); - for (int r = 0; r < asp.getLength(); r++) - { - // no mapping to gaps in sequence - if (Comparison.isGap(asp.getCharAt(r))) - { - continue; - } - int pos = mapping[m].getPDBResNum(asp.findPosition(r)); - - if (pos < 1 || pos == lastPos) - { - continue; - } - - Color colour = sr.getResidueColour(seq, r, finder); - - /* - * hidden regions are shown gray or, optionally, ignored - */ - if (!cs.isVisible(r)) - { - if (hideHiddenRegions) - { - continue; - } - else - { - colour = Color.GRAY; - } - } - - final String chain = mapping[m].getChain(); - - /* - * Just keep incrementing the end position for this colour range - * _unless_ colour, PDB model or chain has changed, or there is a - * gap in the mapped residue sequence - */ - final boolean newColour = !colour.equals(lastColour); - final boolean nonContig = lastPos + 1 != pos; - final boolean newChain = !chain.equals(lastChain); - if (newColour || nonContig || newChain) - { - if (startPos != -1) - { - ChimeraCommands.addAtomSpecRange(colourMap, lastColour, - pdbfnum, startPos, - lastPos, lastChain); - } - startPos = pos; - } - lastColour = colour; - lastPos = pos; - lastChain = chain; - } - // final colour range - if (lastColour != null) - { - ChimeraCommands.addAtomSpecRange(colourMap, lastColour, - pdbfnum, - startPos, lastPos, lastChain); - } - // break; - } - } - } - } - return colourMap; - } - - /** * Returns a list of chains mapped in this viewer. Note this list is not * currently scoped per structure. * diff --git a/test/jalview/ext/jmol/JmolCommandsTest.java b/test/jalview/ext/jmol/JmolCommandsTest.java index e42b54f..f1cc820 100644 --- a/test/jalview/ext/jmol/JmolCommandsTest.java +++ b/test/jalview/ext/jmol/JmolCommandsTest.java @@ -28,6 +28,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; +import jalview.ext.rbvi.chimera.AtomSpecModel; import jalview.gui.AlignFrame; import jalview.gui.JvOptionPane; import jalview.gui.SequenceRenderer; @@ -66,7 +67,8 @@ public class JmolCommandsTest // need some mappings! StructureMappingcommandSet[] commands = JmolCommands - .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel); + .getColourBySequenceCommand(ssm, files, seqs, sr, + af.alignPanel); } @Test(groups = { "Functional" }) @@ -91,11 +93,11 @@ public class JmolCommandsTest SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } }; String[] files = new String[] { "seq1.pdb", "seq2.pdb" }; StructureSelectionManager ssm = new StructureSelectionManager(); - + /* * map residues 1-10 to residues 21-30 (atoms 105-150) in structures */ - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); for (int pos = 1; pos <= seq1.getLength(); pos++) { map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) }); @@ -106,37 +108,91 @@ public class JmolCommandsTest StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2", "B", map, null); ssm.addStructureMapping(sm2); - + StructureMappingcommandSet[] commands = JmolCommands - .getColourBySequenceCommand(ssm, files, seqs, sr, af.alignPanel); + .getColourBySequenceCommand(ssm, files, seqs, sr, + af.alignPanel); assertEquals(commands.length, 2); assertEquals(commands[0].commands.length, 1); String chainACommand = commands[0].commands[0]; // M colour is #82827d == (130, 130, 125) (see strand.html help page) - assertTrue(chainACommand - .contains("select 21:A/1.1;color[130,130,125]")); // first one + assertTrue( + chainACommand.contains("select 21:A/1.1;color[130,130,125]")); // first one // H colour is #60609f == (96, 96, 159) assertTrue(chainACommand.contains(";select 22:A/1.1;color[96,96,159]")); // hidden columns are Gray (128, 128, 128) assertTrue(chainACommand .contains(";select 23-25:A/1.1;color[128,128,128]")); // S and G are both coloured #4949b6 == (73, 73, 182) - assertTrue(chainACommand - .contains(";select 26-30:A/1.1;color[73,73,182]")); + assertTrue( + chainACommand.contains(";select 26-30:A/1.1;color[73,73,182]")); String chainBCommand = commands[1].commands[0]; // M colour is #82827d == (130, 130, 125) - assertTrue(chainBCommand - .contains("select 21:B/2.1;color[130,130,125]")); + assertTrue( + chainBCommand.contains("select 21:B/2.1;color[130,130,125]")); // V colour is #ffff00 == (255, 255, 0) - assertTrue(chainBCommand -.contains(";select 22:B/2.1;color[255,255,0]")); + assertTrue(chainBCommand.contains(";select 22:B/2.1;color[255,255,0]")); // hidden columns are Gray (128, 128, 128) assertTrue(chainBCommand .contains(";select 23-25:B/2.1;color[128,128,128]")); // S and G are both coloured #4949b6 == (73, 73, 182) - assertTrue(chainBCommand - .contains(";select 26-30:B/2.1;color[73,73,182]")); + assertTrue( + chainBCommand.contains(";select 26-30:B/2.1;color[73,73,182]")); + } + + @Test(groups = { "Functional" }) + public void testGetAtomSpec() + { + AtomSpecModel model = new AtomSpecModel(); + assertEquals(JmolCommands.getAtomSpec(model), ""); + + /* + * Jalview numbers models from 0, Jmol from 1 + */ + model.addRange(1, 2, 4, "A"); + assertEquals(JmolCommands.getAtomSpec(model), "2-4:A/2.1"); + + model.addRange(1, 8, 8, "A"); + assertEquals(JmolCommands.getAtomSpec(model), "(2-4,8)&:A/2.1"); + + model.addRange(1, 5, 7, "B"); + assertEquals(JmolCommands.getAtomSpec(model), + "(2-4,8)&:A/2.1,5-7:B/2.1"); + + model.addRange(1, 3, 5, "A"); + // 3-5 merges with 2-4 to make 2-5: + assertEquals(JmolCommands.getAtomSpec(model), + "(2-5,8)&:A/2.1,5-7:B/2.1"); + + model.addRange(0, 1, 4, "B"); + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,(2-5,8)&:A/2.1,5-7:B/2.1"); + + model.addRange(0, 5, 9, "C"); + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,5-9:C/1.1,(2-5,8)&:A/2.1,5-7:B/2.1"); + + model.addRange(1, 8, 10, "B"); + // 8-10 extends 5-7 to make 5-10 + // note code generates (5-10)&:B which is equivalent to 5-10:B + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,5-9:C/1.1,(2-5,8)&:A/2.1,(5-10)&:B/2.1"); + + model.addRange(1, 8, 9, "B"); + // subsumed by 5-10 so makes no difference + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,5-9:C/1.1,(2-5,8)&:A/2.1,(5-10)&:B/2.1"); + + model.addRange(0, 3, 10, "C"); + // subsumes 5-9 so replaces it + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,(3-10)&:C/1.1,(2-5,8)&:A/2.1,(5-10)&:B/2.1"); + + // empty chain code - e.g. from homology modelling + model.addRange(5, 25, 35, " "); + assertEquals(JmolCommands.getAtomSpec(model), + "1-4:B/1.1,(3-10)&:C/1.1,(2-5,8)&:A/2.1,(5-10)&:B/2.1,25-35:/6.1"); } } diff --git a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java index 18c4a14..9adbd8e 100644 --- a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java +++ b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java @@ -300,8 +300,7 @@ public class ChimeraCommandsTest PA.setValue(mockBinding, "ssm", ssm); PA.setValue(mockBinding, "sequence", seqs); - Map colourMap = mockBinding - .buildColoursMap(af.alignPanel); + Map colourMap = StructureCommands.buildColoursMap(mockBinding, af.alignPanel); String[] commands = ChimeraCommands .getColourBySequenceCommand(colourMap, mockBinding); assertEquals(1, commands.length); @@ -323,35 +322,49 @@ public class ChimeraCommandsTest { AtomSpecModel model = new AtomSpecModel(); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), ""); + model.addRange(1, 2, 4, "A"); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#1:2-4.A"); + model.addRange(1, 8, 8, "A"); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#1:2-4.A,8.A"); + model.addRange(1, 5, 7, "B"); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#1:2-4.A,8.A,5-7.B"); + model.addRange(1, 3, 5, "A"); + // 3-5 combines with 2-4 to make 2-5: assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 1, 4, "B"); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B|#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 5, 9, "C"); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B"); + model.addRange(1, 8, 10, "B"); + // 8-10 extends 5-7 to make 5-10 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(1, 8, 9, "B"); + // subsumed range makes no difference assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); - model.addRange(0, 3, 10, "C"); // subsumes 5-9 + + model.addRange(0, 3, 10, "C"); + // subsumes 5-9 so replaces it assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); - model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology - // modelling + + // empty chain code - e.g. from homology modelling + model.addRange(5, 25, 35, " "); assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35."); -- 1.7.10.2