2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.rbvi.chimera;
23 import jalview.api.FeatureRenderer;
24 import jalview.api.SequenceRenderer;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.SequenceI;
27 import jalview.structure.StructureMapping;
28 import jalview.structure.StructureMappingcommandSet;
29 import jalview.structure.StructureSelectionManager;
30 import jalview.util.ColorUtils;
31 import jalview.util.Comparison;
33 import java.awt.Color;
34 import java.util.ArrayList;
35 import java.util.LinkedHashMap;
36 import java.util.List;
38 import java.util.SortedMap;
39 import java.util.TreeMap;
42 * Routines for generating Chimera commands for Jalview/Chimera binding
47 public class ChimeraCommands
51 * utility to construct the commands to colour chains by the given alignment
52 * for passing to Chimera
54 * @returns Object[] { Object[] { <model being coloured>,
57 public static StructureMappingcommandSet[] getColourBySequenceCommand(
58 StructureSelectionManager ssm, String[] files,
59 SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
62 Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap = buildColoursMap(
63 ssm, files, sequence, sr, fr, alignment);
65 List<String> colourCommands = buildColourCommands(colourMap);
67 StructureMappingcommandSet cs = new StructureMappingcommandSet(
68 ChimeraCommands.class, null,
69 colourCommands.toArray(new String[0]));
71 return new StructureMappingcommandSet[] { cs };
75 * Traverse the map of colours/models/chains/positions to construct a list of
76 * 'color' commands (one per distinct colour used). The format of each command
79 * <blockquote> color colorname #modelnumber:range.chain e.g. color #00ff00
80 * #0:2.B,4.B,9-12.B|#1:1.A,2-6.A,...
83 * ://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec
89 protected static List<String> buildColourCommands(
90 Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap)
93 * This version concatenates all commands into a single String (semi-colon
94 * delimited). If length limit issues arise, refactor to return one color
97 List<String> commands = new ArrayList<String>();
98 StringBuilder sb = new StringBuilder(256);
99 boolean firstColour = true;
100 for (Color colour : colourMap.keySet())
102 String colourCode = ColorUtils.toTkCode(colour);
107 sb.append("color ").append(colourCode).append(" ");
109 boolean firstModelForColour = true;
110 final Map<Integer, Map<String, List<int[]>>> colourData = colourMap
112 for (Integer model : colourData.keySet())
114 boolean firstPositionForModel = true;
115 if (!firstModelForColour)
119 firstModelForColour = false;
120 sb.append("#").append(model).append(":");
122 final Map<String, List<int[]>> modelData = colourData.get(model);
123 for (String chain : modelData.keySet())
125 boolean hasChain = !"".equals(chain.trim());
126 for (int[] range : modelData.get(chain))
128 if (!firstPositionForModel)
132 if (range[0] == range[1])
138 sb.append(range[0]).append("-").append(range[1]);
142 sb.append(".").append(chain);
144 firstPositionForModel = false;
149 commands.add(sb.toString());
155 * Build a data structure which maps contiguous subsequences for each colour.
156 * This generates a data structure from which we can easily generate the
157 * Chimera command for colour by sequence.
161 * list of start/end ranges
162 * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
165 protected static Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> buildColoursMap(
166 StructureSelectionManager ssm, String[] files,
167 SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
168 AlignmentI alignment)
170 Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap = new LinkedHashMap<Color, SortedMap<Integer, Map<String, List<int[]>>>>();
171 Color lastColour = null;
172 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
174 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
176 if (mapping == null || mapping.length < 1)
181 int startPos = -1, lastPos = -1;
182 String lastChain = "";
183 for (int s = 0; s < sequence[pdbfnum].length; s++)
185 for (int sp, m = 0; m < mapping.length; m++)
187 final SequenceI seq = sequence[pdbfnum][s];
188 if (mapping[m].getSequence() == seq
189 && (sp = alignment.findIndex(seq)) > -1)
191 SequenceI asp = alignment.getSequenceAt(sp);
192 for (int r = 0; r < asp.getLength(); r++)
194 // no mapping to gaps in sequence
195 if (Comparison.isGap(asp.getCharAt(r)))
199 int pos = mapping[m].getPDBResNum(asp.findPosition(r));
201 if (pos < 1 || pos == lastPos)
206 Color colour = sr.getResidueColour(seq, r, fr);
207 final String chain = mapping[m].getChain();
210 * Just keep incrementing the end position for this colour range
211 * _unless_ colour, PDB model or chain has changed, or there is a
212 * gap in the mapped residue sequence
214 final boolean newColour = !colour.equals(lastColour);
215 final boolean nonContig = lastPos + 1 != pos;
216 final boolean newChain = !chain.equals(lastChain);
217 if (newColour || nonContig || newChain)
221 addColourRange(colourMap, lastColour, pdbfnum, startPos,
230 // final colour range
231 if (lastColour != null)
233 addColourRange(colourMap, lastColour, pdbfnum, startPos,
245 * Helper method to add one contiguous colour range to the colour map.
254 protected static void addColourRange(
255 Map<Color, SortedMap<Integer, Map<String, List<int[]>>>> colourMap,
256 Color colour, int model, int startPos, int endPos, String chain)
259 * Get/initialize map of data for the colour
261 SortedMap<Integer, Map<String, List<int[]>>> colourData = colourMap
263 if (colourData == null)
267 colourData = new TreeMap<Integer, Map<String, List<int[]>>>());
271 * Get/initialize map of data for the colour and model
273 Map<String, List<int[]>> modelData = colourData.get(model);
274 if (modelData == null)
276 colourData.put(model, modelData = new TreeMap<String, List<int[]>>());
280 * Get/initialize map of data for colour, model and chain
282 List<int[]> chainData = modelData.get(chain);
283 if (chainData == null)
285 modelData.put(chain, chainData = new ArrayList<int[]>());
289 * Add the start/end positions
291 chainData.add(new int[] { startPos, endPos });