X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FChimeraCommands.java;h=4afc526322b4ea52b680f8a3dbba890cca809668;hb=c7fc7690c14127c28c641ea3ff8a8193652fec17;hp=4ee74aa99e68e1958725e84c5ad15bc01101d651;hpb=2dd1164b5450a3b1b3b17420543fadece6e5fcbe;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index 4ee74aa..4afc526 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -27,11 +27,15 @@ import jalview.datamodel.SequenceI; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; +import jalview.util.ColorUtils; +import jalview.util.Comparison; import java.awt.Color; import java.util.ArrayList; 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 @@ -54,13 +58,112 @@ public class ChimeraCommands SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { + Map>>> colourMap = buildColoursMap( + ssm, files, sequence, sr, fr, alignment); - ArrayList cset = new ArrayList(); + List colourCommands = buildColourCommands(colourMap); + StructureMappingcommandSet cs = new StructureMappingcommandSet( + ChimeraCommands.class, null, + colourCommands.toArray(new String[0])); + + return new StructureMappingcommandSet[] + { cs }; + } + + /** + * Traverse the map of colours/models/chains/positions to construct a list of + * 'color' commands (one per distinct colour used). The format of each command + * is + * + *
color colorname #modelnumber:range.chain e.g. color #00ff00 + * #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,... + * + * @see http + * ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec + * .html + * + * @param colourMap + * @return + */ + protected static List buildColourCommands( + Map>>> colourMap) + { /* - * Map of { colour, positionSpecs} + * This version concatenates all commands into a single String (semi-colon + * delimited). If length limit issues arise, refactor to return one color + * command per colour. */ - Map colranges = new LinkedHashMap(); + List commands = new ArrayList(); + StringBuilder sb = new StringBuilder(256); + boolean firstColour = true; + for (Color colour : colourMap.keySet()) + { + String colourCode = ColorUtils.toTkCode(colour); + if (!firstColour) + { + sb.append("; "); + } + sb.append("color ").append(colourCode).append(" "); + firstColour = false; + boolean firstModelForColour = true; + final Map>> colourData = colourMap.get(colour); + for (Integer model : colourData.keySet()) + { + boolean firstPositionForModel = true; + if (!firstModelForColour) + { + sb.append("|"); + } + firstModelForColour = false; + sb.append("#").append(model).append(":"); + + final Map> modelData = colourData.get(model); + for (String chain : modelData.keySet()) + { + for (int[] range : modelData.get(chain)) + { + if (!firstPositionForModel) + { + sb.append(","); + } + if (range[0] == range[1]) + { + sb.append(range[0]); + } + else + { + sb.append(range[0]).append("-").append(range[1]); + } + sb.append(".").append(chain); + firstPositionForModel = false; + } + } + } + } + commands.add(sb.toString()); + return commands; + } + + /** + *
+   * Build a data structure which maps contiguous subsequences for each colour. 
+   * This generates a data structure from which 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)
+   * 
+ */ + protected static Map>>> buildColoursMap( + StructureSelectionManager ssm, String[] files, + SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, + AlignmentI alignment) + { + Map>>> colourMap = new LinkedHashMap>>>(); + Color lastColour = null; for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); @@ -70,21 +173,21 @@ public class ChimeraCommands continue; } - int startPos = -1, lastPos = -1, startModel = -1, lastModel = -1; + int startPos = -1, lastPos = -1; String lastChain = ""; - Color lastCol = null; for (int s = 0; s < sequence[pdbfnum].length; s++) { for (int sp, m = 0; m < mapping.length; m++) { - if (mapping[m].getSequence() == sequence[pdbfnum][s] - && (sp = alignment.findIndex(sequence[pdbfnum][s])) > -1) + final SequenceI seq = sequence[pdbfnum][s]; + if (mapping[m].getSequence() == seq + && (sp = alignment.findIndex(seq)) > -1) { SequenceI asp = alignment.getSequenceAt(sp); for (int r = 0; r < asp.getLength(); r++) { // no mapping to gaps in sequence - if (jalview.util.Comparison.isGap(asp.getCharAt(r))) + if (Comparison.isGap(asp.getCharAt(r))) { continue; } @@ -95,78 +198,93 @@ public class ChimeraCommands continue; } - Color col = sr.getResidueBoxColour(sequence[pdbfnum][s], r); + Color colour = sr.getResidueColour(seq, r, fr); + final String chain = mapping[m].getChain(); - if (fr != null) - { - col = fr.findFeatureColour(col, sequence[pdbfnum][s], r); - } - if (lastCol != col || lastPos + 1 != pos - || pdbfnum != lastModel - || !mapping[m].getChain().equals(lastChain)) + /* + * 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 (lastCol != null) + if (startPos != -1) { - addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } - lastCol = null; startPos = pos; - startModel = pdbfnum; } - lastCol = col; + lastColour = colour; lastPos = pos; - lastModel = pdbfnum; - lastChain = mapping[m].getChain(); + lastChain = chain; } // final colour range - if (lastCol != null) + if (lastColour != null) { - addColourRange(colranges, lastCol,startModel,startPos,lastPos,lastChain); + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } break; } } } - // Finally, add the command set ready to be returned. - StringBuilder coms = new StringBuilder(256); - for (String cr:colranges.keySet()) - { - coms.append("color #"+cr+" "+colranges.get(cr)+";"); - } - cset.add(new StructureMappingcommandSet(ChimeraCommands.class, - files[pdbfnum], new String[] { coms.toString() })); } - return cset.toArray(new StructureMappingcommandSet[cset.size()]); + return colourMap; } /** - * Helper method to record a range of positions of the same colour. + * Helper method to add one contiguous colour range to the colour map. * - * @param colranges + * @param colourMap * @param colour * @param model * @param startPos * @param endPos * @param chain */ - private static void addColourRange(Map colranges, - Color colour, int model, - int startPos, int endPos, String chain) + protected static void addColourRange( + Map>>> colourMap, + Color colour, int model, int startPos, int endPos, String chain) { - String colstring = ((colour.getRed()< 16) ? "0":"")+Integer.toHexString(colour.getRed()) - + ((colour.getGreen()< 16) ? "0":"")+Integer.toHexString(colour.getGreen()) - + ((colour.getBlue()< 16) ? "0":"")+Integer.toHexString(colour.getBlue()); - StringBuilder currange = colranges.get(colstring); - if (currange == null) + /* + * Get/initialize map of data for the colour + */ + Map>> colourData = colourMap + .get(colour); + if (colourData == null) { - colranges.put(colstring, currange = new StringBuilder(256)); + colourMap + .put(colour, + colourData = new TreeMap>>()); } - if (currange.length() > 0) + + /* + * Get/initialize map of data for the colour and model + */ + Map> modelData = colourData.get(model); + if (modelData == null) { - currange.append("|"); + colourData.put(model, modelData = new TreeMap>()); } - currange.append("#" + model + ":" + ((startPos==endPos) ? startPos : startPos + "-" - + endPos) + "." + chain); + + /* + * Get/initialize map of data for colour, model and chain + */ + List chainData = modelData.get(chain); + if (chainData == null) + { + modelData.put(chain, chainData = new ArrayList()); + } + + /* + * Add the start/end positions + */ + chainData.add(new int[] + { startPos, endPos }); } }