1 package jalview.structure;
3 import jalview.api.AlignViewportI;
4 import jalview.api.AlignmentViewPanel;
5 import jalview.api.FeatureRenderer;
6 import jalview.api.SequenceRenderer;
7 import jalview.datamodel.AlignmentI;
8 import jalview.datamodel.HiddenColumns;
9 import jalview.datamodel.SequenceI;
10 import jalview.renderer.seqfeatures.FeatureColourFinder;
11 import jalview.util.Comparison;
13 import java.awt.Color;
14 import java.util.ArrayList;
15 import java.util.LinkedHashMap;
16 import java.util.List;
18 import java.util.Map.Entry;
21 * A base class holding methods useful to all classes that implement commands
22 * for structure viewers
27 public abstract class StructureCommandsBase implements StructureCommandsI
29 private static final String CMD_SEPARATOR = ";";
32 * Returns something that separates concatenated commands
36 protected static String getCommandSeparator()
42 public String[] setAttributesForFeatures(StructureSelectionManager ssm,
43 String[] files, SequenceI[][] sequence, AlignmentViewPanel avp)
45 // default does nothing, override where this is implemented
50 * Returns the lowest model number used by the structure viewer
55 public int getModelStartNo()
62 * Build a data structure which records contiguous subsequences for each colour.
63 * From this we can easily generate the viewer command for colour by sequence.
67 * list of start/end ranges
68 * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
78 protected Map<Object, AtomSpecModel> buildColoursMap(
79 StructureSelectionManager ssm, String[] files,
80 SequenceI[][] sequence, SequenceRenderer sr, AlignmentViewPanel viewPanel)
82 FeatureRenderer fr = viewPanel.getFeatureRenderer();
83 FeatureColourFinder finder = new FeatureColourFinder(fr);
84 AlignViewportI viewport = viewPanel.getAlignViewport();
85 HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
86 AlignmentI al = viewport.getAlignment();
87 Map<Object, AtomSpecModel> colourMap = new LinkedHashMap<>();
88 Color lastColour = null;
90 for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
92 final int modelNumber = pdbfnum + getModelStartNo();
93 StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
95 if (mapping == null || mapping.length < 1)
100 int startPos = -1, lastPos = -1;
101 String lastChain = "";
102 for (int s = 0; s < sequence[pdbfnum].length; s++)
104 for (int sp, m = 0; m < mapping.length; m++)
106 final SequenceI seq = sequence[pdbfnum][s];
107 if (mapping[m].getSequence() == seq
108 && (sp = al.findIndex(seq)) > -1)
110 SequenceI asp = al.getSequenceAt(sp);
111 for (int r = 0; r < asp.getLength(); r++)
113 // no mapping to gaps in sequence
114 if (Comparison.isGap(asp.getCharAt(r)))
118 int pos = mapping[m].getPDBResNum(asp.findPosition(r));
120 if (pos < 1 || pos == lastPos)
125 Color colour = sr.getResidueColour(seq, r, finder);
128 * darker colour for hidden regions
130 if (!cs.isVisible(r))
135 final String chain = mapping[m].getChain();
138 * Just keep incrementing the end position for this colour range
139 * _unless_ colour, PDB model or chain has changed, or there is a
140 * gap in the mapped residue sequence
142 final boolean newColour = !colour.equals(lastColour);
143 final boolean nonContig = lastPos + 1 != pos;
144 final boolean newChain = !chain.equals(lastChain);
145 if (newColour || nonContig || newChain)
149 addAtomSpecRange(colourMap, lastColour, modelNumber,
150 startPos, lastPos, lastChain);
158 // final colour range
159 if (lastColour != null)
161 addAtomSpecRange(colourMap, lastColour, modelNumber, startPos,
173 * Helper method to add one contiguous range to the AtomSpec model for the given
174 * value (creating the model if necessary). As used by Jalview, {@code value} is
176 * <li>a colour, when building a 'colour structure by sequence' command</li>
177 * <li>a feature value, when building a 'set Chimera attributes from features'
188 public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
190 int model, int startPos, int endPos, String chain)
193 * Get/initialize map of data for the colour
195 AtomSpecModel atomSpec = map.get(value);
196 if (atomSpec == null)
198 atomSpec = new AtomSpecModel();
199 map.put(value, atomSpec);
202 atomSpec.addRange(model, startPos, endPos, chain);
206 * Returns a colour formatted suitable for use in viewer command syntax
211 protected abstract String getColourString(Color colour);
214 * Traverse the map of colours/models/chains/positions to construct a list of
215 * 'color' commands (one per distinct colour used). The format of each command
216 * is specific to the structure viewer.
221 public List<String> buildColourCommands(
222 Map<Object, AtomSpecModel> colourMap)
225 * This version concatenates all commands into a single String (semi-colon
226 * delimited). If length limit issues arise, refactor to return one color
227 * command per colour.
229 List<String> commands = new ArrayList<>();
230 StringBuilder sb = new StringBuilder(256);
231 boolean firstColour = true;
232 for (Object key : colourMap.keySet())
234 Color colour = (Color) key;
237 sb.append(getCommandSeparator()).append(" ");
240 final AtomSpecModel colourData = colourMap.get(colour);
241 sb.append(getColourCommand(colourData, colour));
243 commands.add(sb.toString());
248 * Returns a command to colour the atoms represented by {@code atomSpecModel}
249 * with the colour specified by {@code colourCode}.
251 * @param atomSpecModel
255 protected String getColourCommand(AtomSpecModel atomSpecModel, Color colour)
257 String atomSpec = getAtomSpec(atomSpecModel, false);
258 return getColourCommand(atomSpec, colour);
262 * Returns a command to colour the atoms described (in viewer command syntax)
263 * by {@code atomSpec} with the colour specified by {@code colourCode}
269 protected abstract String getColourCommand(String atomSpec, Color colour);
272 public String colourByResidues(Map<String, Color> colours)
274 StringBuilder cmd = new StringBuilder(12 * colours.size());
276 for (Entry<String, Color> entry : colours.entrySet())
278 String residue = entry.getKey();
279 String atomSpec = getResidueSpec(residue);
280 cmd.append(getColourCommand(atomSpec, entry.getValue()));
281 cmd.append(getCommandSeparator());
283 return cmd.toString();
287 * Helper method to append one start-end range to an atomspec string
293 * @param firstPositionForModel
295 protected void appendRange(StringBuilder sb, int start, int end,
296 String chain, boolean firstPositionForModel, boolean isChimeraX)
298 if (!firstPositionForModel)
308 sb.append(start).append("-").append(end);
314 if (!" ".equals(chain))
322 * Returns the atom specifier meaning all occurrences of the given residue
327 protected abstract String getResidueSpec(String residue);