JAL-3390 first pass refactoring for JalviewJmolBinding.showStructures
[jalview.git] / src / jalview / ext / jmol / JmolCommands.java
index d5676c5..e3625fa 100644 (file)
  */
 package jalview.ext.jmol;
 
+import jalview.api.AlignViewportI;
+import jalview.api.AlignmentViewPanel;
 import jalview.api.FeatureRenderer;
 import jalview.api.SequenceRenderer;
 import jalview.datamodel.AlignmentI;
+import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.SequenceI;
+import jalview.ext.rbvi.chimera.AtomSpecModel;
+import jalview.renderer.seqfeatures.FeatureColourFinder;
 import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
+import jalview.structures.models.AAStructureBindingModel;
 
 import java.awt.Color;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Routines for generating Jmol commands for Jalview/Jmol binding another
@@ -41,6 +49,8 @@ import java.util.ArrayList;
 public class JmolCommands
 {
 
+  private static final String COMMA = ",";
+
   /**
    * Jmol utility which constructs the commands to colour chains by the given
    * alignment
@@ -50,33 +60,46 @@ public class JmolCommands
    */
   public static StructureMappingcommandSet[] getColourBySequenceCommand(
           StructureSelectionManager ssm, String[] files,
-          SequenceI[][] sequence, SequenceRenderer sr, FeatureRenderer fr,
-          AlignmentI alignment)
+          AAStructureBindingModel binding, AlignmentViewPanel viewPanel)
   {
+    SequenceRenderer sr = binding.getSequenceRenderer(viewPanel);
+    SequenceI[][] sequence = binding.getSequence();
+    return getColourBySequenceCommand(ssm, files, sequence, sr, viewPanel);
+  }
 
-    ArrayList<StructureMappingcommandSet> cset = new ArrayList<StructureMappingcommandSet>();
+  public static StructureMappingcommandSet[] getColourBySequenceCommand(
+          StructureSelectionManager ssm, String[] files,
+          SequenceI[][] sequence, SequenceRenderer sr,
+          AlignmentViewPanel viewPanel)
+  {
+    FeatureRenderer fr = viewPanel.getFeatureRenderer();
+    FeatureColourFinder finder = new FeatureColourFinder(fr);
+    AlignViewportI viewport = viewPanel.getAlignViewport();
+    HiddenColumns cs = viewport.getAlignment().getHiddenColumns();
+    AlignmentI al = viewport.getAlignment();
+    List<StructureMappingcommandSet> cset = new ArrayList<>();
 
     for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
     {
       StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]);
       StringBuffer command = new StringBuffer();
       StructureMappingcommandSet smc;
-      ArrayList<String> str = new ArrayList<String>();
+      ArrayList<String> str = new ArrayList<>();
 
       if (mapping == null || mapping.length < 1)
       {
         continue;
       }
 
-      int lastPos = -1;
       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 = alignment.findIndex(sequence[pdbfnum][s])) > -1)
+                  && (sp = al.findIndex(sequence[pdbfnum][s])) > -1)
           {
-            SequenceI asp = alignment.getSequenceAt(sp);
+            int lastPos = StructureMapping.UNASSIGNED_VALUE;
+            SequenceI asp = al.getSequenceAt(sp);
             for (int r = 0; r < asp.getLength(); r++)
             {
               // no mapping to gaps in sequence
@@ -86,34 +109,44 @@ public class JmolCommands
               }
               int pos = mapping[m].getPDBResNum(asp.findPosition(r));
 
-              if (pos < 1 || pos == lastPos)
+              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.getResidueBoxColour(sequence[pdbfnum][s], r);
+              Color col = sr.getResidueColour(sequence[pdbfnum][s], r,
+                      finder);
 
-              if (fr != null)
+              /*
+               * shade hidden regions darker
+               */
+              if (!cs.isVisible(r))
               {
-                col = fr.findFeatureColour(col, sequence[pdbfnum][s], r);
+                col = Color.GRAY;
               }
-              String newSelcom = (mapping[m].getChain() != " " ? ":"
-                      + mapping[m].getChain() : "")
-                      + "/"
-                      + (pdbfnum + 1)
-                      + ".1"
-                      + ";color["
-                      + col.getRed()
-                      + ","
-                      + col.getGreen()
-                      + ","
+
+              String newSelcom = (mapping[m].getChain() != " "
+                      ? ":" + mapping[m].getChain()
+                      : "") + "/" + (pdbfnum + 1) + ".1" + ";color["
+                      + col.getRed() + COMMA + col.getGreen() + COMMA
                       + col.getBlue() + "]";
-              if (command.length() > newSelcom.length()
-                      && command.substring(
-                              command.length() - newSelcom.length())
-                              .equals(newSelcom))
+              if (command.length() > newSelcom.length() && command
+                      .substring(command.length() - newSelcom.length())
+                      .equals(newSelcom))
               {
                 command = JmolCommands.condenseCommand(command, pos);
                 continue;
@@ -121,7 +154,12 @@ public class JmolCommands
               // TODO: deal with case when buffer is too large for Jmol to parse
               // - execute command and flush
 
-              command.append(";");
+              if (command.length() > 0
+                      && command.charAt(command.length() - 1) != ';')
+              {
+                command.append(";");
+              }
+
               if (command.length() > 51200)
               {
                 // add another chunk
@@ -183,4 +221,77 @@ public class JmolCommands
     return sb;
   }
 
+  /**
+   * Answers a Jmol 'color' command to colour residues as described by the given
+   * map of {@code <Color, AtomSpecModel>}
+   * 
+   * @param map
+   * @return
+   */
+  public static String[] getColourBySequenceCommand(
+          Map<Object, AtomSpecModel> map)
+  {
+    String[] cmds = new String[map.keySet().size()];
+
+    int i = 0;
+    for (Object o : map.keySet())
+    {
+      StringBuilder cmd = new StringBuilder(128);
+      Color c = (Color) o;
+      String atomSpec = getAtomSpec(map.get(o));
+      cmd.append("select ").append(atomSpec).append(";color[")
+              .append(c.getRed()).append(COMMA).append(c.getGreen())
+              .append(COMMA).append(c.getBlue()).append("];");
+      cmds[i] = cmd.toString();
+      i++;
+    }
+
+    return cmds;
+  }
+
+  /**
+   * Builds a Jmol syntax selection expression from the given model, for example
+   * 
+   * <pre>
+   * 61-64,70:A/1.1,12-25,41-44:B/1.1,12:A/2.1
+   * for model 1, chain A, residues 61-64 and 70, chain B residues 12-25 and 41-44, model 2 chain A residue 12
+   * </pre>
+   * 
+   * @param atomSpecModel
+   * @return
+   */
+  public static String getAtomSpec(AtomSpecModel atomSpecModel)
+  {
+    StringBuilder sb = new StringBuilder(64);
+    for (int model : atomSpecModel.getModels())
+    {
+      for (String chain : atomSpecModel.getChains(model))
+      {
+        if (sb.length() > 0)
+        {
+          sb.append(COMMA);
+        }
+        boolean firstRange = true;
+        for (int[] range : atomSpecModel.getRanges(model, chain))
+        {
+          if (!firstRange)
+          {
+            sb.append(COMMA);
+          }
+          firstRange = false;
+          sb.append(range[0]);
+          if (range[1] != range[0])
+          {
+            sb.append("-").append(range[1]);
+          }
+        }
+        sb.append(":").append(chain).append("/")
+                .append(String.valueOf(model + 1))
+                .append(".1");
+      }
+    }
+
+    return sb.toString();
+  }
+
 }