X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fext%2Frbvi%2Fchimera%2FChimeraCommands.java;h=4afc526322b4ea52b680f8a3dbba890cca809668;hb=c7fc7690c14127c28c641ea3ff8a8193652fec17;hp=3c47ed1a3e4befa31041491ddb95ea757e8ed454;hpb=b12200cd43693a8d3b3a3609444ffbc04eb14f7b;p=jalview.git diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index 3c47ed1..4afc526 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -27,16 +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.io.File; -import java.io.FileOutputStream; -import java.io.IOException; 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 @@ -59,31 +58,114 @@ public class ChimeraCommands SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr, AlignmentI alignment) { - String defAttrPath = null; - FileOutputStream fos = null; - try - { - File outFile = File.createTempFile("jalviewdefattr", ".xml"); - outFile.deleteOnExit(); - defAttrPath = outFile.getPath(); - fos = new FileOutputStream(outFile); - fos.write("attribute: jalviewclr\n".getBytes()); - } catch (IOException e1) - { - e1.printStackTrace(); - } - List cset = new ArrayList(); + 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])); + + 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(); - StringBuilder setAttributes = new StringBuilder(256); - String lastColour = "none"; - Color lastCol = null; + 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++) { - boolean startModel = true; StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); if (mapping == null || mapping.length < 1) @@ -116,200 +198,93 @@ public class ChimeraCommands continue; } - Color col = getResidueColour(seq, r, sr, fr); + Color colour = sr.getResidueColour(seq, r, fr); + 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 = !col.equals(lastCol); + final boolean newColour = !colour.equals(lastColour); final boolean nonContig = lastPos + 1 != pos; - final boolean newChain = !mapping[m].getChain().equals(lastChain); - if (newColour || nonContig || startModel || newChain) + final boolean newChain = !chain.equals(lastChain); + if (newColour || nonContig || newChain) { - if (/* lastCol != null */startPos != -1) + if (startPos != -1) { - addColourRange(colranges, lastCol, pdbfnum, startPos, - lastPos, lastChain, startModel); - startModel = false; + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } - // lastCol = null; startPos = pos; } - 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, pdbfnum, startPos, - lastPos, lastChain, false); + addColourRange(colourMap, lastColour, pdbfnum, startPos, + lastPos, lastChain); } break; } } } } - try - { - lastColour = buildColourCommands(cset, colranges, - fos, setAttributes); - } catch (IOException e) - { - e.printStackTrace(); - } - - try - { - fos.close(); - } catch (IOException e) - { - e.printStackTrace(); - } - - /* - * Send a rangeColor command, preceded by either defattr or setattr, - * whichever we end up preferring! - * - * rangecolor requires a minimum of two attribute values to operate on - */ - StringBuilder rangeColor = new StringBuilder(256); - rangeColor.append("rangecolor jalviewclr"); - int colourId = 0; - for (String colour : colranges.keySet()) - { - colourId++; - rangeColor.append(" " + colourId + " " + colour); - } - String rangeColorCommand = rangeColor.toString(); - if (rangeColorCommand.split(" ").length < 5) - { - rangeColorCommand += " max " + lastColour; - } - final String defAttrCommand = "defattr " + defAttrPath - + " raiseTool false"; - final String setAttrCommand = setAttributes.toString(); - final String attrCommand = false ? defAttrCommand : setAttrCommand; - cset.add(new StructureMappingcommandSet(ChimeraCommands.class, null, - new String[] - { attrCommand /* , rangeColorCommand */})); - - return cset.toArray(new StructureMappingcommandSet[cset.size()]); + return colourMap; } /** - * Get the residue colour at the given sequence position - as determined by - * the sequence group colour (if any), else the colour scheme, possibly - * overridden by a feature colour. + * Helper method to add one contiguous colour range to the colour map. * - * @param seq - * @param position - * @param sr - * @param fr - * @return - */ - protected static Color getResidueColour(final SequenceI seq, - int position, SequenceRenderer sr, FeatureRenderer fr) - { - Color col = sr.getResidueBoxColour(seq, position); - - if (fr != null) - { - col = fr.findFeatureColour(col, seq, position); - } - return col; - } - - /** - * Helper method to build the colour commands for one PDBfile. - * - * @param cset - * the list of commands to be added to - * @param colranges - * the map of colours to residue positions already determined - * @param fos - * file to write 'defattr' commands to - * @param setAttributes - * @throws IOException - */ - protected static String buildColourCommands( - List cset, - Map colranges, - FileOutputStream fos, StringBuilder setAttributes) - throws IOException - { - int colourId = 0; - String lastColour = null; - for (String colour : colranges.keySet()) - { - lastColour = colour; - colourId++; - /* - * Using color command directly is slow for larger structures. - * setAttributes.append("color #" + colour + " " + colranges.get(colour)+ - * ";"); - */ - setAttributes.append("color " + colour + " " + colranges.get(colour) - + ";"); - final String atomSpec = new String(colranges.get(colour)); - // setAttributes.append("setattr r jalviewclr " + colourId + " " - // + atomSpec + ";"); - fos.write(("\t" + atomSpec + "\t" + colourId + "\n").getBytes()); - } - return lastColour; - } - - /** - * Helper method to record a range of positions of the same colour. - * - * @param colranges + * @param colourMap * @param colour * @param model * @param startPos * @param endPos * @param chain - * @param changeModel */ - private static void addColourRange(Map colranges, - Color colour, int model, int startPos, int endPos, String chain, - boolean startModel) + 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) - { - colranges.put(colstring, currange = new StringBuilder(256)); - } /* - * Format as (e.g.) #0:1-3.A,5.A,7-10.A,...#1:1-4.B,..etc + * Get/initialize map of data for the colour */ - // if (currange.length() > 0) - // { - // currange.append("|"); - // } - // currange.append("#" + model + ":" + ((startPos==endPos) ? startPos : - // startPos + "-" - // + endPos) + "." + chain); - if (currange.length() == 0) + Map>> colourData = colourMap + .get(colour); + if (colourData == null) { - currange.append("#" + model + ":"); + colourMap + .put(colour, + colourData = new TreeMap>>()); } - else if (startModel) + + /* + * Get/initialize map of data for the colour and model + */ + Map> modelData = colourData.get(model); + if (modelData == null) { - currange.append(",#" + model + ":"); + colourData.put(model, modelData = new TreeMap>()); } - else + + /* + * Get/initialize map of data for colour, model and chain + */ + List chainData = modelData.get(chain); + if (chainData == null) { - currange.append(","); + modelData.put(chain, chainData = new ArrayList()); } - final String rangeSpec = (startPos == endPos) ? Integer - .toString(startPos) : (startPos + "-" + endPos); - currange.append(rangeSpec + "." + chain); + + /* + * Add the start/end positions + */ + chainData.add(new int[] + { startPos, endPos }); } }