Merge branch 'feature/JAL-3551Pymol' into develop
[jalview.git] / src / jalview / ext / jmol / JmolCommands.java
index 6f682be..085fbd5 100644 (file)
  */
 package jalview.ext.jmol;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
 import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
@@ -29,15 +35,13 @@ import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceI;
 import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.AtomSpecModel;
+import jalview.structure.StructureCommand;
+import jalview.structure.StructureCommandI;
 import jalview.structure.StructureCommandsBase;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureSelectionManager;
 import jalview.util.Comparison;
-
-import java.awt.Color;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import jalview.util.Platform;
 
 /**
  * Routines for generating Jmol commands for Jalview/Jmol binding
@@ -47,10 +51,20 @@ import java.util.Map;
  */
 public class JmolCommands extends StructureCommandsBase
 {
-  private static final String CMD_COLOUR_BY_CHARGE = "select *;color white;select ASP,GLU;color red;"
-          + "select LYS,ARG;color blue;select CYS;color yellow";
+  private static final StructureCommand SHOW_BACKBONE = new StructureCommand(
+          "select *; cartoons off; backbone");
+
+  private static final StructureCommand FOCUS_VIEW = new StructureCommand("zoom 0");
 
-  private static final String CMD_COLOUR_BY_CHAIN = "select *;color chain";
+  private static final StructureCommand COLOUR_ALL_WHITE = new StructureCommand(
+          "select *;color white;");
+
+  private static final StructureCommandI COLOUR_BY_CHARGE = new StructureCommand(
+          "select *;color white;select ASP,GLU;color red;"
+                  + "select LYS,ARG;color blue;select CYS;color yellow");
+
+  private static final StructureCommandI COLOUR_BY_CHAIN = new StructureCommand(
+          "select *;color chain");
 
   private static final String PIPE = "|";
 
@@ -85,196 +99,40 @@ public class JmolCommands extends StructureCommandsBase
                     c.getBlue());
   }
 
-
-  public String[] colourBySequence(StructureSelectionManager ssm,
-          String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr,
-          AlignmentViewPanel viewPanel)
-  {
-    // TODO delete method
-
-    FeatureRenderer fr = viewPanel.getFeatureRenderer();
-    FeatureColourFinder finder = new FeatureColourFinder(fr);
-    AlignViewportI viewport = viewPanel.getAlignViewport();
-    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
-    AlignmentI al = viewport.getAlignment();
-    List<String> cset = new ArrayList<>();
-
-    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
-    {
-      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
-      StringBuilder command = new StringBuilder(128);
-      List<String> str = new ArrayList<>();
-
-      if (mapping == null || mapping.length < 1)
-      {
-        continue;
-      }
-
-      for (int s = 0; s < sequence[pdbfnum].length; s++)
-      {
-        for (int sp, m = 0; m < mapping.length; m++)
-        {
-          if (mapping[m].getSequence() == sequence[pdbfnum][s]
-                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
-          {
-            int lastPos = StructureMapping.UNASSIGNED_VALUE;
-            SequenceI asp = al.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 == lastPos)
-              {
-                continue;
-              }
-              if (pos == StructureMapping.UNASSIGNED_VALUE)
-              {
-                // terminate current colour op
-                if (command.length() > 0
-                        && command.charAt(command.length() - 1) != ';')
-                {
-                  command.append(";");
-                }
-                // reset lastPos
-                lastPos = StructureMapping.UNASSIGNED_VALUE;
-                continue;
-              }
-
-              lastPos = pos;
-
-              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
-                      finder);
-
-              /*
-               * shade hidden regions darker
-               */
-              if (!cs.isVisible(r))
-              {
-                col = Color.GRAY;
-              }
-
-              String newSelcom = (mapping[m].getChain() != " "
-                      ? ":" + mapping[m].getChain()
-                      : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
-                      + getColourString(col);
-              if (command.length() > newSelcom.length() && command
-                      .substring(command.length() - newSelcom.length())
-                      .equals(newSelcom))
-              {
-                command = JmolCommands.condenseCommand(command, pos);
-                continue;
-              }
-              // TODO: deal with case when buffer is too large for Jmol to parse
-              // - execute command and flush
-
-              if (command.length() > 0
-                      && command.charAt(command.length() - 1) != ';')
-              {
-                command.append(";");
-              }
-
-              if (command.length() > 51200)
-              {
-                // add another chunk
-                str.add(command.toString());
-                command.setLength(0);
-              }
-              command.append("select " + pos);
-              command.append(newSelcom);
-            }
-            // break;
-          }
-        }
-      }
-      {
-        // add final chunk
-        str.add(command.toString());
-        command.setLength(0);
-      }
-      cset.addAll(str);
-
-    }
-    return cset.toArray(new String[cset.size()]);
-  }
-
-  public static StringBuilder condenseCommand(StringBuilder command,
-          int pos)
-  {
-
-    // work back to last 'select'
-    int p = command.length(), q = p;
-    do
-    {
-      p -= 6;
-      if (p < 1)
-      {
-        p = 0;
-      }
-      ;
-    } while ((q = command.indexOf("select", p)) == -1 && p > 0);
-
-    StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
-
-    command = command.delete(0, q + 7);
-
-    String start;
-
-    if (command.indexOf("-") > -1)
-    {
-      start = command.substring(0, command.indexOf("-"));
-    }
-    else
-    {
-      start = command.substring(0, command.indexOf(":"));
-    }
-
-    sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
-
-    return sb;
-  }
-
   @Override
-  public String colourByChain()
+  public StructureCommandI colourByChain()
   {
-    return CMD_COLOUR_BY_CHAIN;
+    return COLOUR_BY_CHAIN;
   }
 
   @Override
-  public String colourByCharge()
+  public List<StructureCommandI> colourByCharge()
   {
-    return CMD_COLOUR_BY_CHARGE;
+    return Arrays.asList(COLOUR_BY_CHARGE);
   }
 
   @Override
-  public String colourByResidues(Map<String, Color> colours)
+  public List<StructureCommandI> colourByResidues(Map<String, Color> colours)
   {
-    StringBuilder cmd = new StringBuilder(128);
-    cmd.append("select *;color white;");
-    cmd.append(super.colourByResidues(colours));
-
-    return cmd.toString();
+    List<StructureCommandI> cmds = super.colourByResidues(colours);
+    cmds.add(0, COLOUR_ALL_WHITE);
+    return cmds;
   }
 
   @Override
-  public String setBackgroundColour(Color col)
+  public StructureCommandI setBackgroundColour(Color col)
   {
-    return "background " + getColourString(col);
+    return new StructureCommand("background " + getColourString(col));
   }
 
   @Override
-  public String focusView()
+  public StructureCommandI focusView()
   {
-    return "zoom 0";
+    return FOCUS_VIEW;
   }
 
   @Override
-  public String showChains(List<String> toShow)
+  public List<StructureCommandI> showChains(List<String> toShow)
   {
     StringBuilder atomSpec = new StringBuilder(128);
     boolean first = true;
@@ -295,7 +153,7 @@ public class JmolCommands extends StructureCommandsBase
     String spec = atomSpec.toString();
     String command = "select *;restrict " + spec + ";cartoon;center "
             + spec;
-    return command;
+    return Arrays.asList(new StructureCommand(command));
   }
 
   /**
@@ -319,13 +177,13 @@ public class JmolCommands extends StructureCommandsBase
    * @see https://chemapps.stolaf.edu/jmol/docs/#compare
    */
   @Override
-  public String superposeStructures(AtomSpecModel refAtoms,
+  public List<StructureCommandI> superposeStructures(AtomSpecModel refAtoms,
           AtomSpecModel atomSpec)
   {
     StringBuilder sb = new StringBuilder(64);
-    int refModel = refAtoms.getModels().iterator().next();
-    int model2 = atomSpec.getModels().iterator().next();
-    sb.append(String.format("compare {%d.1} {%d.1}", model2, refModel));
+    String refModel = refAtoms.getModels().iterator().next();
+    String model2 = atomSpec.getModels().iterator().next();
+    sb.append(String.format("compare {%s.1} {%s.1}", model2, refModel));
     sb.append(" SUBSET {(*.CA | *.P) and conformation=1} ATOMS {");
 
     /*
@@ -344,36 +202,35 @@ public class JmolCommands extends StructureCommandsBase
     sb.append(getAtomSpec(refAtoms, false)).append(getCommandSeparator())
             .append("cartoons");
 
-    return sb.toString();
+    return Arrays.asList(new StructureCommand(sb.toString()));
   }
 
   @Override
-  public String openCommandFile(String path)
+  public StructureCommandI openCommandFile(String path)
   {
     /*
      * https://chemapps.stolaf.edu/jmol/docs/#script
      * not currently used in Jalview
      */
-    return "script " + path;
+    return new StructureCommand("script " + path);
   }
 
   @Override
-  public String saveSession(String filepath)
+  public StructureCommandI saveSession(String filepath)
   {
     /*
-     * https://chemapps.stolaf.edu/jmol/docs/#write
-     * not currently used in Jalview
+     * https://chemapps.stolaf.edu/jmol/docs/#writemodel
      */
-    return "write \"" + filepath + "\"";
+    return new StructureCommand("write STATE \"" + filepath + "\"");
   }
 
   @Override
-  protected String getColourCommand(String atomSpec, Color colour)
+  protected StructureCommandI colourResidues(String atomSpec, Color colour)
   {
     StringBuilder sb = new StringBuilder(atomSpec.length()+20);
     sb.append("select ").append(atomSpec).append(getCommandSeparator())
             .append("color").append(getColourString(colour));
-    return sb.toString();
+    return new StructureCommand(sb.toString());
   }
 
   @Override
@@ -398,7 +255,7 @@ public class JmolCommands extends StructureCommandsBase
     StringBuilder sb = new StringBuilder(128);
 
     boolean first = true;
-    for (int modelNo : model.getModels())
+    for (String modelNo : model.getModels())
     {
       for (String chain : model.getChains(modelNo))
       {
@@ -427,8 +284,195 @@ public class JmolCommands extends StructureCommandsBase
   }
 
   @Override
-  public String showBackbone()
+  public List<StructureCommandI> showBackbone()
+  {
+    return Arrays.asList(SHOW_BACKBONE);
+  }
+
+  @Override
+  public StructureCommandI loadFile(String file)
+  {
+    // https://chemapps.stolaf.edu/jmol/docs/#loadfiles
+    return new StructureCommand("load FILES \"" + 
+            Platform.escapeBackslashes(file) + "\"");
+  }
+
+  /**
+   * Obsolete method, only referenced from
+   * jalview.javascript.MouseOverStructureListener
+   * 
+   * @param ssm
+   * @param files
+   * @param sequence
+   * @param sr
+   * @param viewPanel
+   * @return
+   */
+  @Deprecated
+  public String[] colourBySequence(StructureSelectionManager ssm,
+          String[] files, SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
+  {
+    // TODO delete method
+
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
+    AlignmentI al = viewport.getAlignment();
+    List<String> cset = new ArrayList<>();
+
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
+      StringBuilder command = new StringBuilder(128);
+      List<String> str = new ArrayList<>();
+
+      if (mapping == null || mapping.length < 1)
+      {
+        continue;
+      }
+
+      for (int s = 0; s < sequence[pdbfnum].length; s++)
+      {
+        for (int sp, m = 0; m < mapping.length; m++)
+        {
+          if (mapping[m].getSequence() == sequence[pdbfnum][s]
+                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
+          {
+            int lastPos = StructureMapping.UNASSIGNED_VALUE;
+            SequenceI asp = al.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 == lastPos)
+              {
+                continue;
+              }
+              if (pos == StructureMapping.UNASSIGNED_VALUE)
+              {
+                // terminate current colour op
+                if (command.length() > 0
+                        && command.charAt(command.length() - 1) != ';')
+                {
+                  command.append(";");
+                }
+                // reset lastPos
+                lastPos = StructureMapping.UNASSIGNED_VALUE;
+                continue;
+              }
+
+              lastPos = pos;
+
+              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                      finder);
+
+              /*
+               * shade hidden regions darker
+               */
+              if (!cs.isVisible(r))
+              {
+                col = Color.GRAY;
+              }
+
+              String newSelcom = (mapping[m].getChain() != " "
+                      ? ":" + mapping[m].getChain()
+                      : "") + "/" + (pdbfnum + 1) + ".1" + ";color"
+                      + getColourString(col);
+              if (command.length() > newSelcom.length() && command
+                      .substring(command.length() - newSelcom.length())
+                      .equals(newSelcom))
+              {
+                command = JmolCommands.condenseCommand(command, pos);
+                continue;
+              }
+              // TODO: deal with case when buffer is too large for Jmol to parse
+              // - execute command and flush
+
+              if (command.length() > 0
+                      && command.charAt(command.length() - 1) != ';')
+              {
+                command.append(";");
+              }
+
+              if (command.length() > 51200)
+              {
+                // add another chunk
+                str.add(command.toString());
+                command.setLength(0);
+              }
+              command.append("select " + pos);
+              command.append(newSelcom);
+            }
+            // break;
+          }
+        }
+      }
+      {
+        // add final chunk
+        str.add(command.toString());
+        command.setLength(0);
+      }
+      cset.addAll(str);
+
+    }
+    return cset.toArray(new String[cset.size()]);
+  }
+
+  /**
+   * Helper method
+   * 
+   * @param command
+   * @param pos
+   * @return
+   */
+  @Deprecated
+  private static StringBuilder condenseCommand(
+          StringBuilder command,
+          int pos)
+  {
+
+    // work back to last 'select'
+    int p = command.length(), q = p;
+    do
+    {
+      p -= 6;
+      if (p < 1)
+      {
+        p = 0;
+      }
+      ;
+    } while ((q = command.indexOf("select", p)) == -1 && p > 0);
+
+    StringBuilder sb = new StringBuilder(command.substring(0, q + 7));
+
+    command = command.delete(0, q + 7);
+
+    String start;
+
+    if (command.indexOf("-") > -1)
+    {
+      start = command.substring(0, command.indexOf("-"));
+    }
+    else
+    {
+      start = command.substring(0, command.indexOf(":"));
+    }
+
+    sb.append(start + "-" + pos + command.substring(command.indexOf(":")));
+
+    return sb;
+  }
+
+  @Override
+  public StructureCommandI openSession(String filepath)
   {
-    return "select *; cartoons off; backbone";
+    return loadFile(filepath);
   }
 }