/* * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2) * Copyright (C) 2014 The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 * of the License, or (at your option) any later version. * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.ext.rbvi.chimera; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.datamodel.AlignmentI; import jalview.datamodel.SequenceI; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; 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; /** * Routines for generating Chimera commands for Jalview/Chimera binding * * @author JimP * */ public class ChimeraCommands { /** * utility to construct the commands to colour chains by the given alignment * for passing to Chimera * * @returns Object[] { Object[] { , * */ public static StructureMappingcommandSet[] getColourBySequenceCommand( StructureSelectionManager ssm, String[] files, 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 of { colour, positionSpecs} */ Map colranges = new LinkedHashMap(); StringBuilder setAttributes = new StringBuilder(256); String lastColour = "none"; Color lastCol = null; for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { boolean startModel = true; 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 = alignment.findIndex(seq)) > -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 pos = mapping[m].getPDBResNum(asp.findPosition(r)); if (pos < 1 || pos == lastPos) { continue; } Color col = getResidueColour(seq, r, sr, fr); /* * 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 nonContig = lastPos + 1 != pos; final boolean newChain = !mapping[m].getChain().equals(lastChain); if (newColour || nonContig || startModel || newChain) { if (/* lastCol != null */startPos != -1) { addColourRange(colranges, lastCol, pdbfnum, startPos, lastPos, lastChain, startModel); startModel = false; } // lastCol = null; startPos = pos; } lastCol = col; lastPos = pos; // lastModel = pdbfnum; lastChain = mapping[m].getChain(); } // final colour range if (lastCol != null) { addColourRange(colranges, lastCol, pdbfnum, startPos, lastPos, lastChain, false); } 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()]); } /** * 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. * * @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 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) { 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 */ // if (currange.length() > 0) // { // currange.append("|"); // } // currange.append("#" + model + ":" + ((startPos==endPos) ? startPos : // startPos + "-" // + endPos) + "." + chain); if (currange.length() == 0) { currange.append("#" + model + ":"); } else if (startModel) { currange.append(",#" + model + ":"); } else { currange.append(","); } final String rangeSpec = (startPos == endPos) ? Integer .toString(startPos) : (startPos + "-" + endPos); currange.append(rangeSpec + "." + chain); } }