JAL-2422 pull-up refactoring of structure commands (continued)
[jalview.git] / src / jalview / ext / jmol / JmolCommands.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 package jalview.ext.jmol;
22
23 import jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.FeatureRenderer;
26 import jalview.api.SequenceRenderer;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.HiddenColumns;
29 import jalview.datamodel.SequenceI;
30 import jalview.renderer.seqfeatures.FeatureColourFinder;
31 import jalview.structure.StructureCommandsBase;
32 import jalview.structure.StructureMapping;
33 import jalview.structure.StructureSelectionManager;
34 import jalview.util.Comparison;
35
36 import java.awt.Color;
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Map.Entry;
41
42 /**
43  * Routines for generating Jmol commands for Jalview/Jmol binding
44  * 
45  * @author JimP
46  * 
47  */
48 public class JmolCommands extends StructureCommandsBase
49 {
50   private static final String CMD_COLOUR_BY_CHARGE = "select *;color white;select ASP,GLU;color red;"
51           + "select LYS,ARG;color blue;select CYS;color yellow";
52
53   private static final String CMD_COLOUR_BY_CHAIN = "select *;color chain";
54
55   private static String formatRGB(Color c) {
56     return c == null ? null
57             : String.format("[%d,%d,%d]", c.getRed(), c.getGreen(),
58                     c.getBlue());
59   }
60
61   @Override
62   public String[] colourBySequence(
63           StructureSelectionManager ssm, String[] files,
64           SequenceI[][] sequence, SequenceRenderer sr,
65           AlignmentViewPanel viewPanel)
66   {
67     // TODO refactor to call buildColoursMap() first...
68
69     FeatureRenderer fr = viewPanel.getFeatureRenderer();
70     FeatureColourFinder finder = new FeatureColourFinder(fr);
71     AlignViewportI viewport = viewPanel.getAlignViewport();
72     HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
73     AlignmentI al = viewport.getAlignment();
74     List<String> cset = new ArrayList<>();
75
76     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
77     {
78       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
79       StringBuilder command = new StringBuilder(128);
80       List<String> str = new ArrayList<>();
81
82       if (mapping == null || mapping.length < 1)
83       {
84         continue;
85       }
86
87       for (int s = 0; s < sequence[pdbfnum].length; s++)
88       {
89         for (int sp, m = 0; m < mapping.length; m++)
90         {
91           if (mapping[m].getSequence() == sequence[pdbfnum][s]
92                   && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
93           {
94             int lastPos = StructureMapping.UNASSIGNED_VALUE;
95             SequenceI asp = al.getSequenceAt(sp);
96             for (int r = 0; r < asp.getLength(); r++)
97             {
98               // no mapping to gaps in sequence
99               if (Comparison.isGap(asp.getCharAt(r)))
100               {
101                 continue;
102               }
103               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
104
105               if (pos == lastPos)
106               {
107                 continue;
108               }
109               if (pos == StructureMapping.UNASSIGNED_VALUE)
110               {
111                 // terminate current colour op
112                 if (command.length() > 0
113                         && command.charAt(command.length() - 1) != ';')
114                 {
115                   command.append(";");
116                 }
117                 // reset lastPos
118                 lastPos = StructureMapping.UNASSIGNED_VALUE;
119                 continue;
120               }
121
122               lastPos = pos;
123
124               Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
125                       finder);
126
127               /*
128                * shade hidden regions darker
129                */
130               if (!cs.isVisible(r))
131               {
132                 col = Color.GRAY;
133               }
134
135               String newSelcom = (mapping[m].getChain() != " "
136                       ? ":" + mapping[m].getChain()
137                       : "") + "/" + (pdbfnum + 1) + ".1" + ";color["
138                       + col.getRed() + "," + col.getGreen() + ","
139                       + col.getBlue() + "]";
140               if (command.length() > newSelcom.length() && command
141                       .substring(command.length() - newSelcom.length())
142                       .equals(newSelcom))
143               {
144                 command = JmolCommands.condenseCommand(command, pos);
145                 continue;
146               }
147               // TODO: deal with case when buffer is too large for Jmol to parse
148               // - execute command and flush
149
150               if (command.length() > 0
151                       && command.charAt(command.length() - 1) != ';')
152               {
153                 command.append(";");
154               }
155
156               if (command.length() > 51200)
157               {
158                 // add another chunk
159                 str.add(command.toString());
160                 command.setLength(0);
161               }
162               command.append("select " + pos);
163               command.append(newSelcom);
164             }
165             // break;
166           }
167         }
168       }
169       {
170         // add final chunk
171         str.add(command.toString());
172         command.setLength(0);
173       }
174       cset.addAll(str);
175
176     }
177     return cset.toArray(new String[cset.size()]);
178   }
179
180   public static StringBuilder condenseCommand(StringBuilder command,
181           int pos)
182   {
183
184     // work back to last 'select'
185     int p = command.length(), q = p;
186     do
187     {
188       p -= 6;
189       if (p < 1)
190       {
191         p = 0;
192       }
193       ;
194     } while ((q = command.indexOf("select", p)) == -1 && p > 0);
195
196     StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
197
198     command = command.delete(0, q + 7);
199
200     String start;
201
202     if (command.indexOf("-") > -1)
203     {
204       start = command.substring(0, command.indexOf("-"));
205     }
206     else
207     {
208       start = command.substring(0, command.indexOf(":"));
209     }
210
211     sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
212
213     return sb;
214   }
215
216   @Override
217   public String colourByChain()
218   {
219     return CMD_COLOUR_BY_CHAIN;
220   }
221
222   @Override
223   public String colourByCharge()
224   {
225     return CMD_COLOUR_BY_CHARGE;
226   }
227
228   @Override
229   public String colourByResidues(Map<String, Color> colours)
230   {
231     StringBuilder cmd = new StringBuilder(128);
232     cmd.append("select *;color white;");
233
234     /*
235      * concatenate commands like
236      * select VAL;color[100,215,55]
237      */
238     for (Entry<String, Color> entry : colours.entrySet())
239     {
240       Color col = entry.getValue();
241       String resCode = entry.getKey();
242       cmd.append("select ").append(resCode).append(";");
243       cmd.append("color[");
244       cmd.append(formatRGB(col));
245       cmd.append("];");
246     }
247
248     return cmd.toString();
249   }
250
251   @Override
252   public String setBackgroundColour(Color col)
253   {
254     return "background " + formatRGB(col);
255   }
256
257   @Override
258   public String focusView()
259   {
260     return "zoom 0";
261   }
262
263   @Override
264   public String showChains(List<String> toShow)
265   {
266     StringBuilder atomSpec = new StringBuilder(128);
267     boolean first = true;
268     for (String chain : toShow)
269     {
270       String[] tokens = chain.split(":");
271       if (tokens.length == 2)
272       {
273         if (!first)
274         {
275           atomSpec.append(" or ");
276         }
277         first = false;
278         atomSpec.append(":").append(tokens[1]).append(" /").append(tokens[0]);
279       }
280     }
281
282     String spec = atomSpec.toString();
283     String command = "select *;restrict " + spec + ";cartoon;center "
284             + spec;
285     return command;
286   }
287
288 }