X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FChimeraCommands.java;h=4afc526322b4ea52b680f8a3dbba890cca809668;hb=c7fc7690c14127c28c641ea3ff8a8193652fec17;hp=41c7abbf251beda9f67558e735ece5d708fc795e;hpb=ae216fceddd8539d20fa5425cdaef3da73aba8f0;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index 41c7abb..4afc526 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -22,16 +22,20 @@ package jalview.ext.rbvi.chimera; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; -import jalview.api.structures.JalviewStructureDisplayI; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; -import jalview.util.Format; +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,109 +58,233 @@ 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) + { + /* + * 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. + */ + 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++) { - float cols[] = new float[4]; StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); - StringBuffer command = new StringBuffer(); - StructureMappingcommandSet smc; - ArrayList str = new ArrayList(); if (mapping == null || mapping.length < 1) + { continue; + } - int startPos = -1, lastPos = -1, startModel = -1, lastModel = -1; - String startChain = "", lastChain = ""; - Color lastCol = null; + 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++) { - 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; } int pos = mapping[m].getPDBResNum(asp.findPosition(r)); if (pos < 1 || pos == lastPos) + { 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) { - - lastCol.getRGBComponents(cols); - String newSelcom = "color " + cols[0] + "," + cols[1] - + "," + cols[2] + " #" + startModel + ":" - + startPos + "-" + lastPos + "." + lastChain; - if (str.size() > 0 - && (str.get(str.size() - 1).length() + newSelcom - .length()) < 4096) - { - str.set(str.size() - 1, str.get(str.size() - 1) + ";" - + newSelcom); - } - else - { - str.add(newSelcom); - } + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } - lastCol = null; startPos = pos; - startModel = pdbfnum; - startChain = mapping[m].getChain(); } - lastCol = col; + lastColour = colour; lastPos = pos; - lastModel = pdbfnum; - lastChain = mapping[m].getChain(); + lastChain = chain; } // final colour range - if (lastCol != null) + if (lastColour != null) { - - lastCol.getRGBComponents(cols); - String newSelcom = "color " + cols[0] + "," + cols[1] + "," - + cols[2] + " #" + startModel + ":" + startPos + "-" - + lastPos + "." + lastChain; - if (str.size() > 0 - && (str.get(str.size() - 1).length() + newSelcom - .length()) < 4096) - { - str.set(str.size() - 1, str.get(str.size() - 1) + ";" - + newSelcom); - } - else - { - str.add(newSelcom); - } + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } break; } } } - // Finally, add the command set ready to be returned. - cset.add(new StructureMappingcommandSet(ChimeraCommands.class, - files[pdbfnum], str.toArray(new String[str.size()]))); } - return cset.toArray(new StructureMappingcommandSet[cset.size()]); + return colourMap; + } + + /** + * Helper method to add one contiguous colour range to the colour map. + * + * @param colourMap + * @param colour + * @param model + * @param startPos + * @param endPos + * @param chain + */ + protected static void addColourRange( + Map>>> colourMap, + Color colour, int model, int startPos, int endPos, String chain) + { + /* + * Get/initialize map of data for the colour + */ + Map>> colourData = colourMap + .get(colour); + if (colourData == null) + { + colourMap + .put(colour, + colourData = new TreeMap>>()); + } + + /* + * Get/initialize map of data for the colour and model + */ + Map> modelData = colourData.get(model); + if (modelData == null) + { + colourData.put(model, modelData = new TreeMap>()); + } + + /* + * 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 }); } }