JAL-3390 redraw structures if alignment only and hidden regions change
[jalview.git] / src / jalview / ext / rbvi / chimera / JalviewChimeraBinding.java
index 00446f2..216320f 100644 (file)
@@ -20,8 +20,8 @@
  */
 package jalview.ext.rbvi.chimera;
 
+import jalview.api.AlignViewportI;
 import jalview.api.AlignmentViewPanel;
-import jalview.api.SequenceRenderer;
 import jalview.api.structures.JalviewStructureDisplayI;
 import jalview.bin.Cache;
 import jalview.datamodel.AlignmentI;
@@ -36,6 +36,7 @@ import jalview.io.DataSourceType;
 import jalview.schemes.ColourSchemeI;
 import jalview.schemes.ResidueProperties;
 import jalview.structure.AtomSpec;
+import jalview.structure.StructureMapping;
 import jalview.structure.StructureMappingcommandSet;
 import jalview.structure.StructureSelectionManager;
 import jalview.structures.models.AAStructureBindingModel;
@@ -51,6 +52,7 @@ import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.Collections;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -76,9 +78,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   private static final String ALPHACARBON = "CA";
 
-  private List<String> chainNames = new ArrayList<String>();
+  private List<String> chainNames = new ArrayList<>();
 
-  private Hashtable<String, String> chainFile = new Hashtable<String, String>();
+  private Hashtable<String, String> chainFile = new Hashtable<>();
 
   /*
    * Object through which we talk to Chimera
@@ -106,7 +108,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   /*
    * Map of ChimeraModel objects keyed by PDB full local file name
    */
-  private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<String, List<ChimeraModel>>();
+  private Map<String, List<ChimeraModel>> chimeraMaps = new LinkedHashMap<>();
 
   String lastHighlightCommand;
 
@@ -133,7 +135,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     String file = pe.getFile();
     try
     {
-      List<ChimeraModel> modelsToMap = new ArrayList<ChimeraModel>();
+      List<ChimeraModel> modelsToMap = new ArrayList<>();
       List<ChimeraModel> oldList = viewer.getModelList();
       boolean alreadyOpen = false;
 
@@ -248,43 +250,6 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   }
 
   /**
-   * Tells Chimera to display only the specified chains
-   * 
-   * @param toshow
-   */
-  public void showChains(List<String> toshow)
-  {
-    /*
-     * Construct a chimera command like
-     * 
-     * ~display #*;~ribbon #*;ribbon :.A,:.B
-     */
-    StringBuilder cmd = new StringBuilder(64);
-    boolean first = true;
-    for (String chain : toshow)
-    {
-      int modelNumber = getModelNoForChain(chain);
-      String showChainCmd = modelNumber == -1 ? ""
-              : modelNumber + ":." + chain.split(":")[1];
-      if (!first)
-      {
-        cmd.append(",");
-      }
-      cmd.append(showChainCmd);
-      first = false;
-    }
-
-    /*
-     * could append ";focus" to this command to resize the display to fill the
-     * window, but it looks more helpful not to (easier to relate chains to the
-     * whole)
-     */
-    final String command = "~display #*; ~ribbon #*; ribbon :"
-            + cmd.toString();
-    sendChimeraCommand(command, false);
-  }
-
-  /**
    * Close down the Jalview viewer and listener, and (optionally) the associated
    * Chimera window.
    */
@@ -519,8 +484,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
           System.out.println(
                   "Superimpose command(s):\n" + command.toString());
         }
-        allComs.append("~display all; chain @CA|P; ribbon ")
-                .append(selectioncom.toString())
+        allComs/*.append("~display all; chain @CA|P; ribbon ")
+                .append(selectioncom.toString())*/
                 .append(";" + command.toString());
       }
     }
@@ -537,13 +502,24 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       {
         System.out.println("Select regions:\n" + selectioncom.toString());
       }
-      allComs.append("; ~display all; chain @CA|P; ribbon ")
-              .append(selectioncom.toString()).append("; focus");
+      allComs.append("; ~display "); // all");
+      if (!isShowAlignmentOnly())
+      {
+        allComs.append("; ribbon; chain @CA|P");
+      }
+      else
+      {
+        allComs.append("; ~ribbon");
+      }
+      allComs.append("; ribbon ").append(selectioncom.toString())
+              .append("; focus");
       List<String> chimeraReplies = sendChimeraCommand(allComs.toString(),
               true);
       for (String reply : chimeraReplies)
       {
-        if (reply.toLowerCase().contains("unequal numbers of atoms"))
+        String lowerCase = reply.toLowerCase();
+        if (lowerCase.contains("unequal numbers of atoms")
+                || lowerCase.contains("at least"))
         {
           error = reply;
         }
@@ -690,16 +666,15 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
 
   /**
    * @param files
-   * @param sr
    * @param viewPanel
    * @return
    */
   @Override
   protected StructureMappingcommandSet[] getColourBySequenceCommands(
-          String[] files, SequenceRenderer sr, AlignmentViewPanel viewPanel)
+          String[] files, AlignmentViewPanel viewPanel)
   {
     return ChimeraCommands.getColourBySequenceCommand(getSsm(), files,
-            getSequence(), sr, viewPanel);
+            this, viewPanel);
   }
 
   /**
@@ -857,7 +832,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
   protected List<AtomSpec> convertStructureResiduesToAlignment(
           List<String> structureSelection)
   {
-    List<AtomSpec> atomSpecs = new ArrayList<AtomSpec>();
+    List<AtomSpec> atomSpecs = new ArrayList<>();
     for (String atomSpec : structureSelection)
     {
       try
@@ -924,7 +899,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
       return;
     }
 
-    // Chimera expects RBG values in the range 0-1
+    // Chimera expects RGB values in the range 0-1
     final double normalise = 255D;
     viewerCommandHistory(false);
     StringBuilder command = new StringBuilder(128);
@@ -937,9 +912,11 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
               ? ResidueProperties.getSingleCharacterCode(resName)
               : resName.charAt(0);
       Color col = cs.findColour(res, 0, null, null, 0f);
-      command.append("color " + col.getRed() / normalise + ","
-              + col.getGreen() / normalise + "," + col.getBlue() / normalise
-              + " ::" + resName + ";");
+      command.append("color ")
+              .append(String.valueOf(col.getRed() / normalise)).append(",")
+              .append(String.valueOf(col.getGreen() / normalise))
+              .append(",").append(String.valueOf(col.getBlue() / normalise))
+              .append(" ::").append(resName).append(";");
     }
 
     sendAsynchronousCommand(command.toString(), COLOURING_CHIMERA);
@@ -1308,4 +1285,113 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel
     }
     return -1;
   }
+
+  @Override
+  public void showStructures(AlignViewportI av, boolean refocus)
+  {
+    StringBuilder cmd = new StringBuilder(128);
+    cmd.append("~display; ~ribbon;");
+    String atomSpec = getMappedResidues(av);
+    cmd.append("ribbon ").append(atomSpec);
+    if (!isShowAlignmentOnly())
+    {
+      cmd.append("chain @CA|P; ribbon");
+    }
+    if (refocus)
+    {
+      cmd.append("; focus");
+    }
+    sendChimeraCommand(cmd.toString(), false);
+  }
+
+  /**
+   * Builds a Chimera atomSpec of residues mapped from sequences, of the format
+   * (#model:residues.chain)
+   * 
+   * <pre>
+   * #0:2-94.A | #1:1-93.C | #2:1-93.A
+   * </pre>
+   * 
+   * Only residues visible in the alignment are included, that is, hidden columns
+   * and sequences are excluded.
+   * 
+   * @param av
+   * @return
+   */
+  private String getMappedResidues(AlignViewportI av)
+  {
+    AlignmentI alignment = av.getAlignment();
+    final int width = alignment.getWidth();
+  
+    String[] files = getStructureFiles();
+
+    StringBuilder atomSpec = new StringBuilder(256);
+
+    for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++)
+    {
+      StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]);
+
+      /*
+       * Find the first mapped sequence (if any) for this PDB entry which is in
+       * the alignment
+       */
+      final int seqCountForPdbFile = getSequence()[pdbfnum].length;
+      for (int s = 0; s < seqCountForPdbFile; s++)
+      {
+        for (StructureMapping mapping : mappings)
+        {
+          final SequenceI theSequence = getSequence()[pdbfnum][s];
+          if (mapping.getSequence() == theSequence
+                  && alignment.findIndex(theSequence) > -1)
+          {
+            String chainCd = mapping.getChain();
+            if (!isShowChain(mapping.getPdbId(), chainCd))
+            {
+              continue;
+            }
+            Iterator<int[]> visible;
+            if (isShowAlignmentOnly() && isHideHiddenRegions())
+            {
+              visible = alignment.getHiddenColumns()
+                    .getVisContigsIterator(0, width, true);
+            }
+            else
+            {
+              visible = Collections.singletonList(new int[] { 0, width })
+                      .iterator();
+            }
+            while (visible.hasNext())
+            {
+              int[] visibleRegion = visible.next();
+              int seqStartPos = theSequence.findPosition(visibleRegion[0]);
+              int seqEndPos = theSequence.findPosition(visibleRegion[1]);
+              List<int[]> residueRanges = mapping
+                      .getPDBResNumRanges(seqStartPos, seqEndPos);
+              if (!residueRanges.isEmpty())
+              {
+                if (atomSpec.length() > 0)
+                {
+                  atomSpec.append("| ");
+                }
+                atomSpec.append(getModelSpec(pdbfnum)).append(":");
+                boolean first = true;
+                for (int[] range : residueRanges)
+                {
+                  if (!first)
+                  {
+                    atomSpec.append(",");
+                  }
+                  first = false;
+                  atomSpec.append(range[0]).append("-").append(range[1]);
+                  atomSpec.append(".").append(chainCd);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    return atomSpec.toString();
+  }
 }