From c0d803f0d4b7374d93f91055007a9a1f16f8c8dd Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 8 Nov 2016 12:42:22 +0000 Subject: [PATCH] JAL-2295 extracted AtomSpecModel for reuse in Chimera commands --- src/jalview/ext/rbvi/chimera/AtomSpecModel.java | 180 +++++++++++++ src/jalview/ext/rbvi/chimera/ChimeraCommands.java | 280 +++++++++++++++----- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 90 +------ .../ext/rbvi/chimera/AtomSpecModelTest.java | 34 +++ .../ext/rbvi/chimera/ChimeraCommandsTest.java | 59 +---- 5 files changed, 443 insertions(+), 200 deletions(-) create mode 100644 src/jalview/ext/rbvi/chimera/AtomSpecModel.java create mode 100644 test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java diff --git a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java new file mode 100644 index 0000000..d62cc3c --- /dev/null +++ b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java @@ -0,0 +1,180 @@ +package jalview.ext.rbvi.chimera; + +import jalview.util.RangeComparator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * A class to model a Chimera atomspec pattern, for example + * + *
+ * #0:15.A,28.A,54.A,63.A,70-72.A,83-84.A,97-98.A|#1:2.A,6.A,11.A,13-14.A,70.A,82.A,96-97.A
+ * 
+ * + * where + * + * + *
+ * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
+ * 
+ */ +public class AtomSpecModel +{ + private Map>> atomSpec; + + /** + * Constructor + */ + public AtomSpecModel() + { + atomSpec = new TreeMap>>(); + } + + /** + * Adds one contiguous range to this atom spec + * + * @param model + * @param startPos + * @param endPos + * @param chain + */ + public void addRange(int model, int startPos, int endPos, String chain) + { + /* + * Get/initialize map of data for the colour and model + */ + Map> modelData = atomSpec.get(model); + if (modelData == null) + { + atomSpec.put(model, modelData = new TreeMap>()); + } + + /* + * Get/initialize map of data for colour, model and chain + */ + List chainData = modelData.get(chain); + if (chainData == null) + { + chainData = new ArrayList(); + modelData.put(chain, chainData); + } + + /* + * Add the start/end positions + */ + chainData.add(new int[] { startPos, endPos }); + // TODO add intelligently, using a RangeList class + } + + /** + * Returns the range(s) formatted as a Chimera atomspec + * + * @return + */ + public String getAtomSpec() + { + StringBuilder sb = new StringBuilder(128); + boolean firstModel = true; + for (Integer model : atomSpec.keySet()) + { + if (!firstModel) + { + sb.append("|"); + } + firstModel = false; + sb.append("#").append(model).append(":"); + + boolean firstPositionForModel = true; + final Map> modelData = atomSpec.get(model); + + for (String chain : modelData.keySet()) + { + chain = chain.trim(); + + List rangeList = modelData.get(chain); + + /* + * sort ranges into ascending start position order + */ + Collections.sort(rangeList, new RangeComparator(true)); + + 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; + } + } + } + return sb.toString(); + } + + /** + * @param sb + * @param start + * @param end + * @param chain + * @param firstPositionForModel + */ + protected void appendRange(StringBuilder sb, int start, int end, + String chain, boolean firstPositionForModel) + { + if (!firstPositionForModel) + { + sb.append(","); + } + if (end == start) + { + sb.append(start); + } + else + { + sb.append(start).append("-").append(end); + } + if (chain.length() > 0) + { + sb.append(".").append(chain); + } + } +} diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index bece4eb..93262aa 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -23,6 +23,7 @@ package jalview.ext.rbvi.chimera; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; @@ -32,10 +33,10 @@ import jalview.util.Comparison; import java.awt.Color; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.TreeMap; /** * Routines for generating Chimera commands for Jalview/Chimera binding @@ -53,21 +54,21 @@ public class ChimeraCommands * @returns Object[] { Object[] { , * */ - public static StructureMappingcommandSet[] getColourBySequenceCommand( + public static StructureMappingcommandSet getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { - Map>>> colourMap = buildColoursMap( + Map colourMap = buildColoursMap( ssm, files, sequence, sr, fr, alignment); List colourCommands = buildColourCommands(colourMap); StructureMappingcommandSet cs = new StructureMappingcommandSet( ChimeraCommands.class, null, - colourCommands.toArray(new String[0])); + colourCommands.toArray(new String[colourCommands.size()])); - return new StructureMappingcommandSet[] { cs }; + return cs; } /** @@ -76,19 +77,17 @@ public class ChimeraCommands * is * *
-   * 
color colorname #modelnumber:range.chain e.g. color #00ff00 - * #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... + *
+ * color colorname #modelnumber:range.chain + * e.g. color #00ff00 #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... *
*
* * @param colourMap * @return - * @see http - * ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec - * .html */ protected static List buildColourCommands( - Map>>> colourMap) + Map colourMap) { /* * This version concatenates all commands into a single String (semi-colon @@ -107,48 +106,63 @@ public class ChimeraCommands } sb.append("color ").append(colourCode).append(" "); firstColour = false; - boolean firstModelForColour = true; - final Map>> colourData = colourMap + final AtomSpecModel colourData = colourMap .get(colour); - for (Integer model : colourData.keySet()) + sb.append(colourData.getAtomSpec()); + } + commands.add(sb.toString()); + return commands; + } + + /** + * Traverses a map of { modelNumber, {chain, {list of from-to ranges} } } and + * builds a Chimera format atom spec + * + * @param modelAndChainRanges + */ + protected static String getAtomSpec( + Map>> modelAndChainRanges) + { + StringBuilder sb = new StringBuilder(128); + boolean firstModelForColour = true; + for (Integer model : modelAndChainRanges.keySet()) + { + boolean firstPositionForModel = true; + if (!firstModelForColour) { - boolean firstPositionForModel = true; - if (!firstModelForColour) - { - sb.append("|"); - } - firstModelForColour = false; - sb.append("#").append(model).append(":"); + sb.append("|"); + } + firstModelForColour = false; + sb.append("#").append(model).append(":"); - final Map> modelData = colourData.get(model); - for (String chain : modelData.keySet()) + final Map> modelData = modelAndChainRanges + .get(model); + for (String chain : modelData.keySet()) + { + boolean hasChain = !"".equals(chain.trim()); + for (int[] range : modelData.get(chain)) { - boolean hasChain = !"".equals(chain.trim()); - for (int[] range : modelData.get(chain)) + if (!firstPositionForModel) { - if (!firstPositionForModel) - { - sb.append(","); - } - if (range[0] == range[1]) - { - sb.append(range[0]); - } - else - { - sb.append(range[0]).append("-").append(range[1]); - } - if (hasChain) - { - sb.append(".").append(chain); - } - firstPositionForModel = false; + sb.append(","); + } + if (range[0] == range[1]) + { + sb.append(range[0]); + } + else + { + sb.append(range[0]).append("-").append(range[1]); + } + if (hasChain) + { + sb.append(".").append(chain); } + firstPositionForModel = false; } } } - commands.add(sb.toString()); - return commands; + return sb.toString(); } /** @@ -163,12 +177,12 @@ public class ChimeraCommands * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains) * */ - protected static Map>>> buildColoursMap( + protected static Map buildColoursMap( StructureSelectionManager ssm, String[] files, SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { - Map>>> colourMap = new LinkedHashMap>>>(); + Map colourMap = new LinkedHashMap(); Color lastColour = null; for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { @@ -253,43 +267,175 @@ public class ChimeraCommands * @param chain */ protected static void addColourRange( - Map>>> colourMap, +Map colourMap, Color colour, int model, int startPos, int endPos, String chain) { + // refactor for reuse as addRange /* * Get/initialize map of data for the colour */ - Map>> colourData = colourMap - .get(colour); + AtomSpecModel colourData = colourMap.get(colour); if (colourData == null) { - colourMap - .put(colour, - colourData = new TreeMap>>()); + colourData = new AtomSpecModel(); + colourMap.put(colour, colourData); } - /* - * Get/initialize map of data for the colour and model - */ - Map> modelData = colourData.get(model); - if (modelData == null) + colourData.addRange(model, startPos, endPos, chain); + } + + /** + * Constructs and returns a set of Chimera commands to set attributes on + * residues corresponding to features in Jalview. + * + * @param ssm + * @param files + * @param seqs + * @param fr + * @param alignment + * @return + */ + public static StructureMappingcommandSet getSetAttributeCommandsForFeatures( + StructureSelectionManager ssm, String[] files, + SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment) + { + Map>>> featureMap = buildFeaturesMap( + ssm, files, seqs, fr, alignment); + + List colourCommands = buildSetAttributeCommands(featureMap); + + StructureMappingcommandSet cs = new StructureMappingcommandSet( + ChimeraCommands.class, null, + colourCommands.toArray(new String[colourCommands.size()])); + + return cs; + } + + /** + *
+   * Helper method to build a map of 
+   * { featureType, {modelNumber, {chain, {list of from-to ranges} } } }
+   * 
+ * + * @param ssm + * @param files + * @param seqs + * @param fr + * @param alignment + * @return + */ + protected static Map>>> buildFeaturesMap( + StructureSelectionManager ssm, String[] files, + SequenceI[][] seqs, FeatureRenderer fr, AlignmentI alignment) + { + Map>>> theMap = new HashMap>>>(); + + List visibleFeatures = fr.getDisplayedFeatureTypes(); + if (visibleFeatures.isEmpty()) { - colourData.put(model, modelData = new TreeMap>()); + return theMap; } - + /* - * Get/initialize map of data for colour, model and chain + * traverse mappings to structures */ - List chainData = modelData.get(chain); - if (chainData == null) + for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { - modelData.put(chain, chainData = new ArrayList()); + StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); + + if (mapping == null || mapping.length < 1) + { + continue; + } + + int lastPos = -1; + for (int seqNo = 0; seqNo < seqs[pdbfnum].length; seqNo++) + { + for (int m = 0; m < mapping.length; m++) + { + final SequenceI seq = seqs[pdbfnum][seqNo]; + int sp = alignment.findIndex(seq); + if (mapping[m].getSequence() == seq && sp > -1) + { + SequenceI asp = alignment.getSequenceAt(sp); + + /* + * traverse each sequence for its mapped positions + */ + for (int r = 0; r < asp.getLength(); r++) + { + // no mapping to gaps in sequence + if (Comparison.isGap(asp.getCharAt(r))) + { + continue; + } + int residuePos = asp.findPosition(r); + int pos = mapping[m].getPDBResNum(residuePos); + + if (pos < 1 || pos == lastPos) + { + continue; + } + final String chain = mapping[m].getChain(); + + /* + * record any features at this position, with the model, chain + * and residue number they map to + */ + List features = fr.findFeaturesAtRes(asp, + residuePos); + for (SequenceFeature feature : features) + { + if (!visibleFeatures.contains(feature)) + { + continue; + } + } + } + } + } + } + } + return theMap; + } + + /** + * Traverse the map of features/models/chains/positions to construct a list of + * 'setattr' commands (one per feature type). The format of each command is + * + *
+   * 
setattr r " " #modelnumber:range.chain + * e.g. setattr r jv:chain " " #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... + *
+ *
+ *

+ * Note we are not (currently) setting attribute values, only the type + * (presence) of each attribute. This is to avoid overloading the Chimera REST + * interface by sending too many distinct commands. Analysis by feature values + * may still be performed in Jalview, on selections created in Chimera. + * + * @param featureMap + * @return + * @see http + * ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec + * .html + */ + protected static List buildSetAttributeCommands( + Map>>> featureMap) + { + List commands = new ArrayList(); + for (String featureType : featureMap.keySet()) + { + StringBuilder sb = new StringBuilder(128); + featureType = featureType.replace(" ", "_"); + sb.append("setattr r jv:").append(featureType).append(" \" \" "); + final Map>> featureData = featureMap + .get(featureType); + sb.append(getAtomSpec(featureData)); + commands.add(sb.toString()); } - /* - * Add the start/end positions - */ - chainData.add(new int[] { startPos, endPos }); + return commands; } } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 2534421..40b6059 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -27,17 +27,14 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceFeature; import jalview.datamodel.SequenceI; import jalview.httpserver.AbstractRequestHandler; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; import jalview.structure.AtomSpec; -import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; -import jalview.util.Comparison; import jalview.util.MessageManager; import java.awt.Color; @@ -659,32 +656,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } AlignmentI alignment = alignmentv.getAlignment(); - for (jalview.structure.StructureMappingcommandSet cpdbbyseq : getColourBySequenceCommands( - files, sr, fr, alignment)) + StructureMappingcommandSet colourBySequenceCommands = ChimeraCommands + .getColourBySequenceCommand(getSsm(), files, getSequence(), sr, + fr, alignment); + for (String command : colourBySequenceCommands.commands) { - for (String command : cpdbbyseq.commands) - { - sendAsynchronousCommand(command, COLOURING_CHIMERA); - } + sendAsynchronousCommand(command, COLOURING_CHIMERA); } } /** - * @param files - * @param sr - * @param fr - * @param alignment - * @return - */ - protected StructureMappingcommandSet[] getColourBySequenceCommands( - String[] files, SequenceRenderer sr, FeatureRenderer fr, - AlignmentI alignment) - { - return ChimeraCommands.getColourBySequenceCommand(getSsm(), files, - getSequence(), sr, fr, alignment); - } - - /** * @param command */ protected void executeWhenReady(String command) @@ -1154,61 +1135,14 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { return; } - for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) - { - StructureMapping[] mapping = getSsm().getMapping(files[pdbfnum]); - - if (mapping == null || mapping.length < 1) - { - continue; - } - - int lastPos = -1; - for (int seqNo = 0; seqNo < getSequence()[pdbfnum].length; seqNo++) - { - for (int m = 0; m < mapping.length; m++) - { - final SequenceI seq = getSequence()[pdbfnum][seqNo]; - int sp = alignment.findIndex(seq); - if (mapping[m].getSequence() == seq && sp > -1) - { - SequenceI asp = alignment.getSequenceAt(sp); - for (int r = 0; r < asp.getLength(); r++) - { - // no mapping to gaps in sequence - if (Comparison.isGap(asp.getCharAt(r))) - { - continue; - } - int residuePos = asp.findPosition(r); - int pos = mapping[m].getPDBResNum(residuePos); - if (pos < 1 || pos == lastPos) - { - continue; - } - final String chain = mapping[m].getChain(); - List features = fr.findFeaturesAtRes(asp, - residuePos); - for (SequenceFeature feature : features) - { - String desc = feature.getDescription(); - float score = feature.getScore(); - if (score != 0 && score != Float.NaN) - { - desc = Float.toString(score); - } - String attName = "jv:" - + feature.getType().replace(" ", "_"); - String cmd = "setattr r " + attName + " \"" - + desc + "\" #" + pdbfnum + ":" + pos + "." + chain; - System.out.println(cmd); - sendAsynchronousCommand(cmd, null); - } - } - } - } - } + StructureMappingcommandSet commandSet = ChimeraCommands + .getSetAttributeCommandsForFeatures(getSsm(), files, + getSequence(), fr, alignment); + for (String command : commandSet.commands) + { + sendAsynchronousCommand(command, null); } + } } diff --git a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java new file mode 100644 index 0000000..c9e1cad --- /dev/null +++ b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java @@ -0,0 +1,34 @@ +package jalview.ext.rbvi.chimera; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class AtomSpecModelTest +{ + @Test(groups = "Functional") + public void testGetAtomSpec() + { + AtomSpecModel model = new AtomSpecModel(); + assertEquals(model.getAtomSpec(), ""); + model.addRange(1, 2, 4, "A"); + assertEquals(model.getAtomSpec(), "#1:2-4.A"); + model.addRange(1, 8, 8, "A"); + assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A"); + model.addRange(1, 5, 7, "B"); + assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A,5-7.B"); + model.addRange(1, 3, 5, "A"); + assertEquals(model.getAtomSpec(), "#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 1, 4, "B"); + assertEquals(model.getAtomSpec(), "#0:1-4.B|#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 5, 9, "C"); + assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B"); + model.addRange(1, 8, 10, "B"); + assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(1, 8, 9, "B"); + assertEquals(model.getAtomSpec(), "#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 + assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); + } + +} diff --git a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java index 29c7d16..3c6f830 100644 --- a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java +++ b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java @@ -21,12 +21,9 @@ package jalview.ext.rbvi.chimera; import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertTrue; import java.awt.Color; -import java.util.Arrays; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import org.testng.annotations.Test; @@ -34,58 +31,10 @@ import org.testng.annotations.Test; public class ChimeraCommandsTest { @Test(groups = { "Functional" }) - public void testAddColourRange() - { - Map>>> map = new LinkedHashMap>>>(); - ChimeraCommands.addColourRange(map, Color.pink, 1, 2, 4, "A"); - ChimeraCommands.addColourRange(map, Color.pink, 1, 8, 8, "A"); - ChimeraCommands.addColourRange(map, Color.pink, 1, 5, 7, "B"); - ChimeraCommands.addColourRange(map, Color.red, 1, 3, 5, "A"); - ChimeraCommands.addColourRange(map, Color.red, 0, 1, 4, "B"); - ChimeraCommands.addColourRange(map, Color.orange, 0, 5, 9, "C"); - - // three colours mapped - assertEquals(3, map.keySet().size()); - - // Red has two models, Pink and Orange one each - assertEquals(2, map.get(Color.red).keySet().size()); - assertEquals(1, map.get(Color.orange).keySet().size()); - assertEquals(1, map.get(Color.pink).keySet().size()); - - // pink model 1 has two chains, red.0 / red.1 / orange.0 one each - assertEquals(2, map.get(Color.pink).get(1).keySet().size()); - assertEquals(1, map.get(Color.red).get(0).keySet().size()); - assertEquals(1, map.get(Color.red).get(1).keySet().size()); - assertEquals(1, map.get(Color.orange).get(0).keySet().size()); - - // inspect positions - List posList = map.get(Color.pink).get(1).get("A"); - assertEquals(2, posList.size()); - assertTrue(Arrays.equals(new int[] { 2, 4 }, posList.get(0))); - assertTrue(Arrays.equals(new int[] { 8, 8 }, posList.get(1))); - - posList = map.get(Color.pink).get(1).get("B"); - assertEquals(1, posList.size()); - assertTrue(Arrays.equals(new int[] { 5, 7 }, posList.get(0))); - - posList = map.get(Color.red).get(0).get("B"); - assertEquals(1, posList.size()); - assertTrue(Arrays.equals(new int[] { 1, 4 }, posList.get(0))); - - posList = map.get(Color.red).get(1).get("A"); - assertEquals(1, posList.size()); - assertTrue(Arrays.equals(new int[] { 3, 5 }, posList.get(0))); - - posList = map.get(Color.orange).get(0).get("C"); - assertEquals(1, posList.size()); - assertTrue(Arrays.equals(new int[] { 5, 9 }, posList.get(0))); - } - - @Test(groups = { "Functional" }) public void testBuildColourCommands() { - Map>>> map = new LinkedHashMap>>>(); + Map map = new LinkedHashMap(); ChimeraCommands.addColourRange(map, Color.blue, 0, 2, 5, "A"); ChimeraCommands.addColourRange(map, Color.blue, 0, 7, 7, "B"); ChimeraCommands.addColourRange(map, Color.blue, 0, 9, 23, "A"); @@ -94,13 +43,13 @@ public class ChimeraCommandsTest ChimeraCommands.addColourRange(map, Color.yellow, 1, 8, 8, "A"); ChimeraCommands.addColourRange(map, Color.yellow, 1, 3, 5, "A"); ChimeraCommands.addColourRange(map, Color.red, 0, 3, 5, "A"); + ChimeraCommands.addColourRange(map, Color.red, 0, 6, 9, "A"); // Colours should appear in the Chimera command in the order in which - // they were added; within colour, by model, by chain, and positions as - // added + // they were added; within colour, by model, by chain, ranges in start order String command = ChimeraCommands.buildColourCommands(map).get(0); assertEquals( - "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:8.A,3-5.A; color #ff0000 #0:3-5.A", + "color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:3-5.A,8.A; color #ff0000 #0:3-9.A", command); } } -- 1.7.10.2