Merge branch 'develop' into releases/Release_2_11_3_Branch
authorJames Procter <j.procter@dundee.ac.uk>
Thu, 5 Oct 2023 07:05:32 +0000 (08:05 +0100)
committerJames Procter <j.procter@dundee.ac.uk>
Thu, 5 Oct 2023 07:05:32 +0000 (08:05 +0100)
49 files changed:
build.gradle
gradle.properties
help/help/html/colourSchemes/annotationColouring.html
help/help/html/features/annotationsubmenucolbyannot.png [new file with mode: 0644]
help/help/html/features/clarguments-basic.html
help/help/html/features/paematrices.html
help/help/html/features/search.html
help/help/html/features/search.png
help/help/html/structures/epas1_annotdetail.png [new file with mode: 0644]
help/help/html/structures/epas1_pae_ebiaf.png [new file with mode: 0644]
help/markdown/releases/release-2_11_3_0.md
help/markdown/whatsnew/whatsnew-2_11_3_0.md
src/jalview/bin/Commands.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/ContactListImpl.java
src/jalview/datamodel/SearchResultMatchI.java
src/jalview/datamodel/SearchResults.java
src/jalview/datamodel/SearchResultsI.java
src/jalview/gui/AlignmentPanel.java
src/jalview/gui/AnnotationLabels.java
src/jalview/gui/AnnotationPanel.java
src/jalview/gui/IdCanvas.java
src/jalview/gui/SplashScreen.java
src/jalview/renderer/ContactGeometry.java
src/jalview/renderer/ContactMapRenderer.java
src/jalview/structure/StructureSelectionManager.java
src/jalview/util/Format.java
test/jalview/bin/CommandsTest.java
test/jalview/bin/HiDPISettingTest1.java
test/jalview/datamodel/PAEContactMatrixTest.java
test/jalview/datamodel/SearchResultsTest.java
test/jalview/ext/jmol/JmolViewerTest.java
test/jalview/ext/rbvi/chimera/JalviewChimeraView.java
test/jalview/gui/AlignFrameTest.java
test/jalview/gui/AlignViewportTest.java
test/jalview/gui/AlignmentPanelTest.java
test/jalview/gui/DesktopTests.java
test/jalview/gui/FeatureSettingsTest.java
test/jalview/gui/FreeUpMemoryTest.java
test/jalview/gui/QuitHandlerTest.java
test/jalview/gui/ScalePanelTest.java
test/jalview/gui/SeqPanelTest.java
test/jalview/gui/SequenceRendererTest.java
test/jalview/io/AnnotatedPDBFileInputTest.java
test/jalview/io/JalviewExportPropertiesTests.java
test/jalview/io/cache/JvCacheableInputBoxTest.java
test/jalview/renderer/ContactGeometryTest.java [new file with mode: 0644]
test/jalview/renderer/seqfeatures/FeatureRendererTest.java
test/jalview/schemes/ColourSchemesTest.java

index c4ed582..8ac2757 100644 (file)
@@ -1783,6 +1783,27 @@ task testTask1(type: Test) {
   }
 }
 
+task testTask2(type: Test) {
+  group = "Verification"
+  description = "Tests that need to be isolated from the main test run"
+  useTestNG() {
+    includeGroups name
+    excludeGroups testng_excluded_groups.split(",")
+    preserveOrder true
+    useDefaultListeners=true
+  }
+}
+task testTask3(type: Test) {
+  group = "Verification"
+  description = "Tests that need to be isolated from the main test run"
+  useTestNG() {
+    includeGroups name
+    excludeGroups testng_excluded_groups.split(",")
+    preserveOrder true
+    useDefaultListeners=true
+  }
+}
+
 /* insert more testTaskNs here -- change N to next digit or other string */
 /*
 task testTaskN(type: Test) {
index 547304f..c2e217f 100644 (file)
@@ -177,7 +177,7 @@ imagemagick_convert = ~/buildtools/imagemagick/bin/convert
 
 bamboo_channelbase = https://builds.jalview.org/browse
 bamboo_planKey = 
-bamboo_getdown_channel_suffix = /latest/artifact/shared/getdown-channel
+bamboo_getdown_channel_suffix = /latest/artifact/shared/getdown-build-for-website/default
 
 eclipse_codestyle_file = utils/eclipse/JalviewCodeStyle.xml
 eclipse_extra_jdt_prefs_file = utils/eclipse/org.eclipse.jdt.core.jalview.prefs
index 2272503..c9221a1 100755 (executable)
   <p>Jalview allows the columns of an alignment to be coloured using
     any numerical annotation rows added to that alignment.</p>
   Select &quot;Colour&quot;
-  <strong>&#8594;</strong> &quot;.. by Annotation&quot; to bring up the
+  <strong>&#8594;</strong> &quot;by Annotation...&quot; to bring up the
   Colour by Annotation settings window.
   <br>
+       <br>You can also apply Colour by annotation for a particular annotation
+       (or per-sequence set) by right-clicking an annotation row's label and selecting &quot;Colour by Annotation...&quot; or &quot;Colour by Annotation (per Sequence)...&quot; from the 
+       <strong>Annotation label popup menu</strong>. (<em>Since Jalview 2.11.3</em>)
+       <br>
+  <div align="center">
+  <img src="../features/annotationsubmenucolbyannot.png" width="414" height="266">
+  </div>
+  <p><strong>The Colour by Annotation Settings Window</strong></p>
   <br>
   <div align="center">
     <img src="annotationColourSetting.gif" width="471" height="256">
diff --git a/help/help/html/features/annotationsubmenucolbyannot.png b/help/help/html/features/annotationsubmenucolbyannot.png
new file mode 100644 (file)
index 0000000..22f69d9
Binary files /dev/null and b/help/help/html/features/annotationsubmenucolbyannot.png differ
index c82cb20..238366c 100644 (file)
   <br/>
   The value can also be specified as a sub-value:
   <pre>
-  jalview --open examples/uniref50.fa --structure [seqid=FER1+SPIOL,structureviewer=jmol,tempfac=plddt]examples/AlphaFold/AF-P00221-F1-model_v4.pdb
+  jalview --open examples/uniref50.fa --structure [seqid=FER1_SPIOL,structureviewer=jmol,tempfac=plddt]examples/AlphaFold/AF-P00221-F1-model_v4.pdb
   </pre>
   which is equivalent to
   <pre>
-  jalview --open examples/uniref50.fa --structure examples/AlphaFold/AF-P00221-F1-model_v4.pdb --tempfac plddt --seqid FER1+SPIOL
+  jalview --open examples/uniref50.fa --structure examples/AlphaFold/AF-P00221-F1-model_v4.pdb --tempfac plddt --seqid FER1_SPIOL
    --structureviewer jmol
   </pre>
 
index cebc16b..8d180ee 100644 (file)
                        Jalview</strong>
        </p>
 
-       <p>Predicted Alignment Error matrices are produced by deep-learning
-               based 3D-structure prediction pipelines such as AlphaFold.</p>
+       <p>Predicted Alignment Error (PAE) matrices are produced by
+               deep-learning based 3D-structure prediction pipelines such as
+               AlphaFold. They reflect how reliably two parts of a model have been
+               positioned in space, by giving for each residue the likely error (in
+               &Aring;ngstroms) between that residue and every other modelled
+               position the pair of residues' real relative position, if the model
+               and real 3D structure were superimposed at that residue.</p>
+       <p>
+               Jalview visualises PAE matrices as an alignment annotation track,
+               shaded from dark green to white, similar to the encoding used on the
+               EBI-AlphaFold website (see <a
+                       href="https://alphafold.ebi.ac.uk/entry/O04090">O04090 3D model</a>
+               at EBI-AlphaFoldDB).
+       </p>
+       <div style="display:flex; flex-wrap:wrap;"align="center" width="100%">
+       <figure><img src="../structures/epas1_annotdetail.png" height="300"/><figcaption>Alignment of EPAS1 homologs from Human, Rat and Cow, with predicted alignment error shown in Jalview</figcaption></figure>
+       <figure><img src="../structures/epas1_pae_ebiaf.png" height="300"/><figcaption>Predicted Alignment Error Matrix<br/>from <a href="https://alphafold.ebi.ac.uk/entry/Q99814">https://alphafold.ebi.ac.uk/entry/Q99814</a></figcaption></figure>
+       </div>
+       <p>
+               <strong>Importing PAE Matrices</strong>
+       </p>
        <p>
                Jalview retrieves PAE matrices when importing predicted 3D structures
                from the EBI-AlphaFold database via <a
                        href="../features/structurechooser.html">Jalview's structure
-                       chooser</a> GUI.
+                       chooser</a> GUI. If you have produced your own models and accompanying
+               PAE matrices using a pipeline such as ColabFold, then you can load
+               them both together via the
+               <a href="../features/structurechooser.html#loadpdbfile">Load PDB
+                       File</a> dropdown menu in the 3D structure chooser, providing it is
+               in a
+               <a href="../io/paematrixformat.html">supported PAE format</a>.
        </p>
        <p>
-               If you have produced your own models using a pipeline such as
-               ColabFold, then you can load it via the 'Add PAE matrix file' button
-               in the <a href="../features/structurechooser.html#loadpdbfile">Load
-                       PDB File</a> dropdown menu in the 3D structure chooser, providing it is
-               in a <a href="../io/paematrixformat.html">supported PAE format</a>. ,
-               or the <a href="../features/clarguments-basic.html">Command Line
-                       Interface</a>. See <a href="../features/paematrices.html">Working
-                       with PAE Matrices</a> for information on how they are visualised and
-               analysed in Jalview.
+               The <a href="../features/clarguments-basic.html">Command Line
+                       Interface</a> also provides a options for importing PAE matrices along
+               side models, enabling the automated production of alignment figures
+               annotated with PAE matrices and PLDDT scores.
        </p>
+       <p>
+               <strong>Showing PAE Matrix Annotations </strong>
        </p>
        <p>
-               An additional <em>Predicted Alignment Error</em> file can also be
-               provided when importing 3D structure data. Jalview supports import of
-               PAE Matrices provided as <a href="../io/paematrixformat.html">AlphaFold
-                       format JSON files</a> - which are also produced by ColabFold. See <a
-                       href="paematrices.html">Working with PAE Matrices</a> for details on
-               what Jalview allows you to do with associated PAE matrix data.
+               When viewing 3D structures from the EBI-AlphaFold database or local 3D
+               structures with an associated PAE file, the PAE is imported as <i>Reference
+                       Annotation</i>, which is not always automatically added to the alignment
+               view.
        </p>
+       <p>To show the PAE, right click the sequence and locate the 'Add
+               Reference Annotation' entry in the Sequence ID submenu, or select all
+               sequences and locate the option in the Selection submenu. You can do
+               this in any alignment window (or view) where a sequence with
+               associated PAE data appears.</p>
+       <p></p>
        <p>
-               <em>The Structure Chooser interface was introduced in Jalview
-                       2.9. </em>
+               <em>Support for visualision and analysis of predicted alignment
+                       error matrices was added in Jalview 2.11.3. </em>
        </p>
 </body>
 </html>
index d559db8..6889d84 100755 (executable)
@@ -36,7 +36,7 @@ td {
   </p>
   <p>The search box is displayed by pressing Control and F or
     selecting &quot;Find...&quot; from the &quot;Search&quot; menu.</p>
-  <img src="search.png" width="400" height="152">
+  <img src="search.png"  height="152">
   <p>&quot;Find next&quot; will find the next occurrence of the
     query and adjust the alignment window view to show it, and
     &quot;Find all&quot; highlights all matches for a query. The
@@ -46,18 +46,24 @@ td {
   <ul>
     <li>The search uses regular expressions. (understands a mixture
       of posix and perl style regex - see below for a summary)</li>
-    <li>Gaps are ignored when matching the query to the sequences
-      in the alignment.</li>
-    <li>Hidden columns can optionally be ignored (<em>since Jalview 2.11</em>)</li>
-    <li>The search is applied to both sequences and their IDs. It can
-      optionally also be applied to the description string (<em>since Jalview
-        2.10</em>), and sequence feature descriptions (<em>since Jalview 2.11.2.5</em>).
-    </li>
-    <li>If a region is selected, then search will <strong>only</strong>
+               <li>If a region is selected, then search will <strong>only</strong>
       be performed on that region.<br />
     <em>Tip: to quickly clear the current selection, click the
         alignment view you wish to search, then press 'Escape'.</em>
     </li>
+    <li>Gaps are ignored when matching the query to the sequences
+      in the alignment.</li>
+    <li>Hidden columns can optionally be ignored (<em>since Jalview 2.11</em>)</li>
+               <li>The search is applied to both sequences and their IDs. <br />
+                       Check boxes also enable searching of:
+                       <ul>
+                               <li><strong>Sequence description</strong> (<em>since Jalview 2.10</em>)
+                               </li>
+                               <li><strong>Sequence Feature</strong> type and description for currently displayed features (<em>since Jalview
+                                               2.11.3.0</em>)
+                               </li>
+                       </ul>
+               </li>
     <li>Tick the &quot;Match Case&quot; box to perform a case
       sensitive search.</li>
     <li>To access a <a href="#queryhistory">previously used
@@ -87,9 +93,11 @@ td {
   <strong>Copying highlighted regions to a new alignment</strong>
   </p>
   <p>
-    You can copy the currently highlighted matching regions of sequences to the clipboard with alt-Command-C.    
+       Press <strong>Copy</strong> button or type Ctrl (Cmd on OSX) + Shift +
+       C to copy highlighted search results to the clipboard, enabling them
+       to be pasted to a new alignment (via Shift+Ctrl (or Cmd) + V).
   </p>
-  <p>
+       <p>
 
     <strong>A quick Regular Expression Guide</strong>
   </p>
index 47c18f4..9178b9a 100644 (file)
Binary files a/help/help/html/features/search.png and b/help/help/html/features/search.png differ
diff --git a/help/help/html/structures/epas1_annotdetail.png b/help/help/html/structures/epas1_annotdetail.png
new file mode 100644 (file)
index 0000000..45118a0
Binary files /dev/null and b/help/help/html/structures/epas1_annotdetail.png differ
diff --git a/help/help/html/structures/epas1_pae_ebiaf.png b/help/help/html/structures/epas1_pae_ebiaf.png
new file mode 100644 (file)
index 0000000..b7b4bf6
Binary files /dev/null and b/help/help/html/structures/epas1_pae_ebiaf.png differ
index f1b2edc..4904c4c 100644 (file)
@@ -13,7 +13,7 @@ channel: "release"
 - <!-- JAL-4019 --> Ambiguous Base Colourscheme
 - <!-- JAL-4061 --> Find can search sequence features' type and description
 - <!-- JAL-4062 --> Hold down Shift + CMD/CTRL C to copy highlighted regions as new sequences
-- <!-- JAL-1556 --> Quickly enable select and/or colour by for displayed annotation row via its popup menu
+- <!-- JAL-1556 --> Quickly enable select and/or colour by for displayed annotation row via annotation panel popup menu
 - <!-- JAL-4094 --> Shift+Click+Drag to adjust height of all annotation tracks of same type
 - <!-- JAL-4190 --> Pressing escape in tree panel clears any current selection
 
@@ -122,6 +122,7 @@ known issue ? <!-- JAL-4127 --> 'Reload' for a jalview project results in all wi
 - <!-- JAL-4165 --> Missing last letter when copying consensus sequence from alignment if first column is hidden
 - <!-- JAL-4261 --> Last sequence ID in alignment not shown and annotation labels are misaligned in HTML export
 - <!-- JAL-3024 --> Files opened via command line with a relative path are added as relative paths to Recent files list (since 2.0.x)
+- <!-- JAL-4291 --> Test coverage for ID width adjustment disabled pending fix for new annotation label geometry and width calculation
 
 
 
index dbafc08..cf91e3a 100644 (file)
@@ -17,7 +17,7 @@ Jalview 2's original command line interface (CLI) allowed alignments to be impor
   * 3D structure data shown in Jmol can be exported as PNG
   * Scale factors and dimensions can be specified, allowing high resolution image exports
 
-In addition to these new functionality, next generation CLI operations can be applied to a range of files and directories through the use of wild-cards. This allows faster and more efficient processing of large numbers of alignments. It also facilitates modularisation of operations through the use of ['command-line argument files'](../features/clarguments-argfiles.html). 
+In addition to these new functionality, next generation CLI operations can be applied to a range of files and directories through the use of wild-cards. This allows faster and more efficient processing of large numbers of alignments. It also facilitates modularisation of operations through the use of ['command-line argument files'](features/clarguments-argfiles.html). 
 
 All new CLI parameters are preceded by '--', as opposed to an optional single '-' which was used in Jalview's old CLI. Old style arguments can still be used, so existing scripts will still work as before, but old-style operations cannot be mixed with new operations in the same CLI call. Use of old command line operations will also raise a warning, and we plan to completely remove support in Jalview's next major release (2.12).
 
index 1d65fc6..5d3b50d 100644 (file)
@@ -769,7 +769,7 @@ public class Commands
                   AppJmol jmol = (AppJmol) sview;
                   jmol.makePDBImage(structureImageFile, imageType, renderer,
                           userBis);
-                  Console.debug("Finished Rendering image to "
+                  Console.info("Exported structure image to "
                           + structureImageFile);
 
                   // RESTORE SESSION AFTER EXPORT IF NEED BE
index 514a326..aef2038 100755 (executable)
@@ -2069,6 +2069,10 @@ public class Alignment implements AlignmentI, AutoCloseable
   @Override
   public ContactListI getContactListFor(AlignmentAnnotation _aa, int column)
   {
+    if (_aa.annotations==null || column>=_aa.annotations.length || column<0)
+    {
+      return null;
+    }
     ContactListI cl = cmholder.getContactListFor(_aa, column);
     if (cl == null && _aa.groupRef != null)
     {
index bb31c5d..7058ac7 100644 (file)
@@ -88,7 +88,7 @@ public class ContactListImpl implements ContactListI
         }
       }
     }
-    if (tot > 0)
+    if (tot > 0 && to_column>from_column)
     {
       cr.setMean(tot / (1 + to_column - from_column));
     }
index 661ad6c..dedb960 100644 (file)
@@ -57,4 +57,13 @@ public interface SearchResultMatchI
    * @return
    */
   boolean contains(SequenceI seq, int start, int end);
+
+  /**
+   *
+   * @param seq
+   * @param from - first position to highlight
+   * @param to - last position to highlight (assumed higher than from)
+   * @return true iff from-to intersects or marks positions either side of start/end
+   */
+  boolean adjacent(SequenceI seq, int from, int to);
 }
\ No newline at end of file
index 8d6cbb1..546dd6f 100755 (executable)
@@ -35,13 +35,13 @@ public class SearchResults implements SearchResultsI
 {
   private int count;
 
-  private List<SearchResultMatchI> matches = new ArrayList<>();
+  private ArrayList<SearchResultMatchI> matches = new ArrayList<>();
 
   /**
    * One match consists of a sequence reference, start and end positions.
    * Discontiguous ranges in a sequence require two or more Match objects.
    */
-  public class Match implements SearchResultMatchI
+  public class Match implements SearchResultMatchI, Comparable<SearchResultMatchI>
   {
     final SequenceI sequence;
 
@@ -160,6 +160,39 @@ public class SearchResults implements SearchResultsI
     {
       return (sequence == seq && start <= from && end >= to);
     }
+    @Override
+    public boolean adjacent(SequenceI seq, int from, int to)
+    {
+      return (sequence == seq && ((start <= from && end >= to) || (from<=(end+1) && to >=(end+1)) || (from<=(start-1) && to>=(start-1))));
+    }
+
+    @Override
+    public int compareTo(SearchResultMatchI o)
+    {
+      if (start<o.getStart())
+      {
+        return -1;
+      }
+      if (start > o.getStart())
+      {
+        return +1;
+      }
+      if (end < o.getEnd())
+      {
+        return -1;
+      }
+      if (end > o.getEnd())
+      {
+        return +1;
+      }
+      if (sequence!=o.getSequence())
+      {
+        int hashc =sequence.hashCode(),oseq=o.getSequence().hashCode();
+        return (hashc < oseq) ? -1 : 1;
+      }
+      return 0;
+    }
+    
   }
 
   @Override
@@ -191,8 +224,56 @@ public class SearchResults implements SearchResultsI
       count = beforeCount + 1;
     }
   }
+  
 
   @Override
+  public boolean appendResult(SequenceI sequence, int start, int end)
+  {
+
+    Match m = new Match(sequence, start, end);
+    
+    boolean appending=false;
+    
+    // we dynamically maintain an interval to add as we test each range in the list
+    
+    int cstart=start,cend=end;
+    List<SearchResultMatchI> toRemove=new ArrayList<>();
+    for (SearchResultMatchI thatm:matches)
+    {
+      if (thatm.getSequence()==sequence)
+      {
+        if (thatm.contains(sequence,cstart,cend))
+        {
+          // found a match containing the current range. nothing else to do except report if we operated on the list
+          return appending;
+        }
+        if (thatm.adjacent(sequence, cstart, cend))
+        {
+          // update the match to add with the adjacent start/end
+          start = Math.min(m.start, thatm.getStart());
+          end = Math.max(m.end, thatm.getEnd());
+          // and check if we keep or remove the old one
+          if (thatm.getStart()!=start || thatm.getEnd()!=end)
+          { 
+            toRemove.add(thatm);
+            count--;
+            cstart = start;
+            cend = end;
+            appending=true;
+          } else {
+            return false;
+          }
+        }
+      }
+    }
+    matches.removeAll(toRemove);
+    {
+      matches.add(new Match(sequence,cstart,cend));
+      count++;
+    }
+    return appending;
+  }
+  @Override
   public boolean involvesSequence(SequenceI sequence)
   {
     final int start = sequence.getStart();
@@ -393,4 +474,5 @@ public class SearchResults implements SearchResultsI
     }
     return seqs;
   }
+
 }
index 7946824..d682de1 100644 (file)
@@ -51,6 +51,19 @@ public interface SearchResultsI
    */
   void addResult(SequenceI seq, int[] positions);
 
+
+  /**
+   * Adds the given start/end region to this search result. If sequence already
+   * has a search result and the range is adjacent to already highlighted
+   * positions, they will be merged
+   * 
+   * @param sequence
+   * @param start
+   * @param end
+   * @return true if an existing range was updated with this one
+   */
+  boolean appendResult(SequenceI sequence, int start, int end);
+
   /**
    * adds all match results in the argument to this set
    * 
index 7ed5227..3127731 100644 (file)
@@ -302,7 +302,11 @@ public class AlignmentPanel extends GAlignmentPanel implements
 
   /**
    * Calculate the width of the alignment labels based on the displayed names
-   * and any bounds on label width set in preferences.
+   * and any bounds on label width set in preferences. Also includes annotations
+   * not actually visible.
+   * 
+   * FIXME JAL-244 JAL-4091 - doesn't include sequence associated annotation
+   * label decorators and only called during tests
    * 
    * @param maxwidth
    *          -1 or maximum width allowed for IdWidth
@@ -313,7 +317,17 @@ public class AlignmentPanel extends GAlignmentPanel implements
   {
     return calculateIdWidth(maxwidth, true, false);
   }
-
+  /**
+   * Calculate the width of the alignment labels based on the displayed names
+   * and any bounds on label width set in preferences.
+   * 
+   * @param maxwidth
+   *          -1 or maximum width allowed for IdWidth
+   * @param includeAnnotations - when true includes width of any additional marks in annotation id panel 
+   * @param visibleOnly -  
+   * @return Dimension giving the maximum width of the alignment label panel
+   *         that should be used.
+   */
   public Dimension calculateIdWidth(int maxwidth,
           boolean includeAnnotations, boolean visibleOnly)
   {
@@ -338,29 +352,10 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // Also check annotation label widths
     if (includeAnnotations && al.getAlignmentAnnotation() != null)
     {
-      if (Jalview.isHeadlessMode())
-      {
-        AnnotationLabels aal = getAlabels();
-        int stringWidth = aal.drawLabels(null, false, idWidth, false, false,
-                fm);
-        idWidth = Math.max(idWidth, stringWidth);
-      }
-      else
-      {
-        fm = c.getFontMetrics(getAlabels().getFont());
-
-        for (i = 0; i < al.getAlignmentAnnotation().length; i++)
-        {
-          AlignmentAnnotation aa = al.getAlignmentAnnotation()[i];
-          if (visibleOnly && !aa.visible)
-          {
-            continue;
-          }
-          String label = aa.label;
-          int stringWidth = fm.stringWidth(label);
-          idWidth = Math.max(idWidth, stringWidth);
-        }
-      }
+      AnnotationLabels aal = getAlabels();
+      int stringWidth = aal.drawLabels(null, false, idWidth, false, false,
+              fm, !visibleOnly);
+      idWidth = Math.max(idWidth, stringWidth);
     }
 
     int w = maxwidth < 0 ? idWidth : Math.min(maxwidth, idWidth);
@@ -998,8 +993,16 @@ public class AlignmentPanel extends GAlignmentPanel implements
           Graphics idGraphics, Graphics alignmentGraphics)
           throws PrinterException
   {
-    final int idWidth = getVisibleIdWidth(false);
-
+    final int idWidth, idWidthForGui;
+    // otherwise calculate it
+    idWidth = getVisibleIdWidth(false);
+//    if (getIdPanel()!=null && getIdPanel().getWidth()>0)
+//    {
+//      // use the current IdPanel's width, if its set and non-zero
+//      idWidthForGui = getIdPanel().getWidth();
+//    } else {
+//      idWidthForGui=0;
+//    }
     /*
      * Get the horizontal offset to where we draw the sequences.
      * This is idWidth if using a single Graphics context, else zero.
@@ -1048,6 +1051,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     }
     final int alignmentDrawnHeight = (endSeq - startSeq) * charHeight + 3;
 
+    alignmentGraphics.setColor(Color.white);
+    alignmentGraphics.fillRect(0, 0, pageWidth, pageHeight+scaleHeight);
+
     /*
      * draw the Scale at horizontal offset, then reset to top left (0, 0)
      */
@@ -1064,8 +1070,9 @@ public class AlignmentPanel extends GAlignmentPanel implements
     IdCanvas idCanvas = getIdPanel().getIdCanvas();
     List<SequenceI> selection = av.getSelectionGroup() == null ? null
             : av.getSelectionGroup().getSequences(null);
+    
     idCanvas.drawIds((Graphics2D) idGraphics, av, startSeq, endSeq - 1,
-            selection, false);
+            selection, false,idWidth);
 
     idGraphics.setFont(av.getFont());
     idGraphics.translate(0, -scaleHeight);
@@ -1212,7 +1219,7 @@ public class AlignmentPanel extends GAlignmentPanel implements
     // see if rendering offscreen - check preferences and calc width accordingly
     if (!onscreen && Cache.getDefault("FIGURE_AUTOIDWIDTH", false))
     {
-      return calculateIdWidth(-1).width;
+      return calculateIdWidth(-1,true,true).width;
     }
     Integer idwidth = onscreen ? null
             : Cache.getIntegerProperty("FIGURE_FIXEDIDWIDTH");
index d9d6b9f..a3f7e00 100755 (executable)
@@ -1211,7 +1211,7 @@ public class AnnotationLabels extends JPanel
         Graphics2D g2d = (Graphics2D) g;
         Graphics dummy = g2d.create();
         int newAnnotationIdWidth = drawLabels(dummy, clip, width, false, forGUI,
-                null);
+                null, false);
         dummy.dispose();
         Dimension d = ap.calculateDefaultAlignmentIdWidth();
         int alignmentIdWidth = d.width;
@@ -1245,10 +1245,10 @@ public class AnnotationLabels extends JPanel
     }
     else
     {
-      int newAnnotationIdWidth = drawLabels(g, clip, width, false, forGUI, null);
+      int newAnnotationIdWidth = drawLabels(g, clip, width, false, forGUI, null, false);
       width = Math.max(newAnnotationIdWidth, givenWidth);
     }
-    drawLabels(g, clip, width, true, forGUI, null);
+    drawLabels(g, clip, width, true, forGUI, null, false);
   }
 
   /**
@@ -1257,8 +1257,6 @@ public class AnnotationLabels extends JPanel
    * occur, but the widest label width will be returned. If g is null then
    * fmetrics must be supplied.
    * 
-   * Returns the width of the annotation labels.
-   * 
    * @param g
    *          Graphics2D instance (used for rendering and font scaling if no fmetrics supplied) 
    * @param clip
@@ -1270,9 +1268,11 @@ public class AnnotationLabels extends JPanel
    * @param forGUI - when false, GUI relevant marks like indicators for dragging annotation panel height are not rendered
    * @param fmetrics
    *          FontMetrics if Graphics object g is null
+   * @param includeHidden - when true returned width includes labels in hidden row width calculation 
+   * @return the width of the annotation labels.
    */
   public int drawLabels(Graphics g0, boolean clip, int width,
-          boolean actuallyDraw, boolean forGUI, FontMetrics fmetrics)
+          boolean actuallyDraw, boolean forGUI, FontMetrics fmetrics, boolean includeHidden)
   {
     if (clip)
     {
@@ -1361,7 +1361,7 @@ public class AnnotationLabels extends JPanel
       for (int i = 0; i < aa.length; i++)
       {
         visible = true;
-        if (!aa[i].visible)
+        if (!aa[i].visible && !includeHidden)
         {
           hasHiddenRows = true;
           continue;
@@ -1369,7 +1369,7 @@ public class AnnotationLabels extends JPanel
         olY = y;
         // look ahead to next annotation
         for (nexAA = i + 1; nexAA < aa.length
-                && !aa[nexAA].visible; nexAA++)
+                && (!aa[nexAA].visible && includeHidden); nexAA++)
           ;
         y += aa[i].height;
         if (clip)
index 8a957bc..6c9cb6c 100755 (executable)
@@ -68,6 +68,7 @@ import jalview.renderer.AwtRenderPanelI;
 import jalview.renderer.ContactGeometry;
 import jalview.schemes.ResidueProperties;
 import jalview.util.Comparison;
+import jalview.util.Format;
 import jalview.util.MessageManager;
 import jalview.util.Platform;
 import jalview.viewmodel.ViewportListenerI;
@@ -630,7 +631,12 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
       return false;
     }
     int yOffset = rowIndex[1];
-
+    AlignmentAnnotation[] allAnnotation = av.getAlignment()
+            .getAlignmentAnnotation();
+    if (allAnnotation==null || rowIndex[0]<0 || rowIndex[0]>=allAnnotation.length)
+    {
+      return false;
+    }
     AlignmentAnnotation clicked = av.getAlignment()
             .getAlignmentAnnotation()[rowIndex[0]];
     if (clicked.graph != AlignmentAnnotation.CONTACT_MAP)
@@ -647,144 +653,152 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
     {
       ContactGeometry cXcgeom = new ContactGeometry(forCurrentX,
               clicked.graphHeight);
-      ContactGeometry.contactInterval cXci = cXcgeom.mapFor(yOffset,
-              yOffset);
-      /**
-       * start and end range corresponding to the row range under the mouse at
-       * column currentX
-       */
-      int fr, to;
-      fr = Math.min(cXci.cStart, cXci.cEnd);
-      to = Math.max(cXci.cStart, cXci.cEnd);
-
-      // double click selects the whole group
-      if (evt.getClickCount() == 2)
+      ContactGeometry.contactInterval cXci = cXcgeom.mapFor(yOffset);
+      if (cXci != null)
       {
-        ContactMatrixI matrix = av.getContactMatrix(clicked);
+        /**
+         * start and end range corresponding to the row range under the mouse at
+         * column currentX
+         */
+        int fr, to;
+        fr = Math.min(cXci.cStart, cXci.cEnd);
+        to = Math.max(cXci.cStart, cXci.cEnd);
 
-        if (matrix != null)
+        // double click selects the whole group
+        if (evt.getClickCount() == 2)
         {
-          // simplest approach is to select all group containing column
-          if (matrix.hasGroups())
+          ContactMatrixI matrix = av.getContactMatrix(clicked);
+
+          if (matrix != null)
           {
-            SequenceI rseq = clicked.sequenceRef;
-            BitSet grp = new BitSet();
-            grp.or(matrix.getGroupsFor(currentX));
-            // TODO: cXci needs to be mapped to real groups
-            for (int c = fr; c <= to; c++)
+            // simplest approach is to select all group containing column
+            if (matrix.hasGroups())
             {
-              BitSet additionalGrp = matrix.getGroupsFor(c);
-              grp.or(additionalGrp);
-            }
+              SequenceI rseq = clicked.sequenceRef;
+              BitSet grp = new BitSet();
+              grp.or(matrix.getGroupsFor(currentX));
+              // TODO: cXci needs to be mapped to real groups
+              for (int c = fr; c <= to; c++)
+              {
+                BitSet additionalGrp = matrix.getGroupsFor(c);
+                grp.or(additionalGrp);
+              }
 
-            HiddenColumns hc = av.getAlignment().getHiddenColumns();
-            ColumnSelection cs = av.getColumnSelection();
-            
-            for (int p=grp.nextSetBit(0); p >= 0; p = grp
-                    .nextSetBit(p + 1))
-            {
-              if (matrix instanceof MappableContactMatrixI)
+              HiddenColumns hc = av.getAlignment().getHiddenColumns();
+              ColumnSelection cs = av.getColumnSelection();
+
+              for (int p = grp.nextSetBit(0); p >= 0; p = grp
+                      .nextSetBit(p + 1))
               {
-                // find the end of this run of set bits
-                int nextp = grp.nextClearBit(p)-1;
-                int[] pos = ((MappableContactMatrixI)matrix).getMappedPositionsFor(rseq, p,nextp);
-                p=nextp;
-                
-                if (pos!=null)
+                if (matrix instanceof MappableContactMatrixI)
                 {
-                  for (int pos_p = pos[0];pos_p<=pos[1];pos_p++)
+                  // find the end of this run of set bits
+                  int nextp = grp.nextClearBit(p) - 1;
+                  int[] pos = ((MappableContactMatrixI) matrix)
+                          .getMappedPositionsFor(rseq, p, nextp);
+                  p = nextp;
+
+                  if (pos != null)
                   {
-                    int col = rseq.findIndex(pos_p)-1;
-                    if (col>=0 && (!av.hasHiddenColumns() || hc.isVisible(col)))
+                    for (int pos_p = pos[0]; pos_p <= pos[1]; pos_p++)
                     {
-                      cs.addElement(col);
+                      int col = rseq.findIndex(pos_p) - 1;
+                      if (col >= 0 && (!av.hasHiddenColumns()
+                              || hc.isVisible(col)))
+                      {
+                        cs.addElement(col);
+                      }
                     }
                   }
                 }
-              } else {
-                int offp = (rseq != null)
-                        ? rseq.findIndex(rseq.getStart() - 1 + p)
-                        : p;
-  
-                if (!av.hasHiddenColumns() || hc.isVisible(offp))
+                else
                 {
-                  cs.addElement(offp);
+                  int offp = (rseq != null)
+                          ? rseq.findIndex(rseq.getStart() - 1 + p)
+                          : p;
+
+                  if (!av.hasHiddenColumns() || hc.isVisible(offp))
+                  {
+                    cs.addElement(offp);
+                  }
                 }
               }
             }
-          }
-          // possible alternative for interactive selection - threshold
-          // gives 'ceiling' for forming a cluster
-          // when a row+column is selected, farthest common ancestor less
-          // than thr is used to compute cluster
+            // possible alternative for interactive selection - threshold
+            // gives 'ceiling' for forming a cluster
+            // when a row+column is selected, farthest common ancestor less
+            // than thr is used to compute cluster
 
-        }
-      }
-      else
-      {
-        // select corresponding range in segment under mouse
-        {
-          int[] rng = forCurrentX.getMappedPositionsFor(fr, to);
-          if (rng != null)
-          {
-            av.getColumnSelection().addRangeOfElements(rng, true);
           }
-          av.getColumnSelection().addElement(currentX);
         }
-        // PAE SPECIFIC
-        // and also select everything lower than the max range adjacent
-        // (kind of works)
-        if (evt.isControlDown()
-                && PAEContactMatrix.PAEMATRIX.equals(clicked.getCalcId()))
+        else
         {
-          int c = fr;
-          ContactRange cr = forCurrentX.getRangeFor(fr, to);
-          double cval;
-          // TODO: could use GraphLine instead of arbitrary picking
-          // TODO: could report mean/median/variance for partitions
-          // (contiguous selected vs unselected regions and inter-contig
-          // regions)
-          // controls feathering - what other elements in row/column
-          // should we select
-          double thresh = cr.getMean() + (cr.getMax() - cr.getMean()) * .15;
-          while (c >= 0)
+          // select corresponding range in segment under mouse
           {
-            cval = forCurrentX.getContactAt(c);
-            if (// cr.getMin() <= cval &&
-            cval <= thresh)
+            int[] rng = forCurrentX.getMappedPositionsFor(fr, to);
+            if (rng != null)
             {
-              int[] cols = forCurrentX.getMappedPositionsFor(c, c);
-              if (cols != null)
-              {
-                av.getColumnSelection().addRangeOfElements(cols, true);
-              }
-              else
-              {
-                break;
-              }
+              av.getColumnSelection().addRangeOfElements(rng, true);
             }
-            c--;
+            av.getColumnSelection().addElement(currentX);
           }
-          c = to;
-          while (c < forCurrentX.getContactHeight())
+          // PAE SPECIFIC
+          // and also select everything lower than the max range adjacent
+          // (kind of works)
+          if (evt.isControlDown()
+                  && PAEContactMatrix.PAEMATRIX.equals(clicked.getCalcId()))
           {
-            cval = forCurrentX.getContactAt(c);
-            if (// cr.getMin() <= cval &&
-            cval <= thresh)
+            int c = fr;
+            ContactRange cr = forCurrentX.getRangeFor(fr, to);
+            double cval;
+            // TODO: could use GraphLine instead of arbitrary picking
+            // TODO: could report mean/median/variance for partitions
+            // (contiguous selected vs unselected regions and inter-contig
+            // regions)
+            // controls feathering - what other elements in row/column
+            // should we select
+            double thresh = cr.getMean()
+                    + (cr.getMax() - cr.getMean()) * .15;
+            while (c >= 0)
             {
-              int[] cols = forCurrentX.getMappedPositionsFor(c, c);
-              if (cols != null)
+              cval = forCurrentX.getContactAt(c);
+              if (// cr.getMin() <= cval &&
+              cval <= thresh)
               {
-                av.getColumnSelection().addRangeOfElements(cols, true);
+                int[] cols = forCurrentX.getMappedPositionsFor(c, c);
+                if (cols != null)
+                {
+                  av.getColumnSelection().addRangeOfElements(cols, true);
+                }
+                else
+                {
+                  break;
+                }
               }
+              c--;
             }
-            else
+            c = to;
+            while (c < forCurrentX.getContactHeight())
             {
-              break;
-            }
-            c++;
+              cval = forCurrentX.getContactAt(c);
+              if (// cr.getMin() <= cval &&
+              cval <= thresh)
+              {
+                int[] cols = forCurrentX.getMappedPositionsFor(c, c);
+                if (cols != null)
+                {
+                  av.getColumnSelection().addRangeOfElements(cols, true);
+                }
+              }
+              else
+              {
+                break;
+              }
+              c++;
 
+            }
           }
+
         }
       }
     }
@@ -1075,23 +1089,21 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
 
     if (forFromX != null && forToX != null)
     {
-      ContactGeometry lastXcgeom = new ContactGeometry(forFromX,
-              cma.graphHeight);
-      ContactGeometry.contactInterval lastXci = lastXcgeom
-              .mapFor(rowIndex[1], rowIndex[1] + deltaY);
-
-      ContactGeometry cXcgeom = new ContactGeometry(forToX,
+      // FIXME will need two ContactGeometry objects when handling contact matrices with differing numbers of rows at each
+      // column
+      ContactGeometry xcgeom = new ContactGeometry(forFromX,
               cma.graphHeight);
-      ContactGeometry.contactInterval cXci = cXcgeom.mapFor(rowIndex[1],
-              rowIndex[1] + deltaY);
+      ContactGeometry.contactInterval lastXci = xcgeom
+              .mapFor(rowIndex[1]);
+      ContactGeometry.contactInterval cXci = xcgeom.mapFor(rowIndex[1] + deltaY);
 
       // mark rectangular region formed by drag
       jalview.bin.Console.trace("Matrix Selection from last(" + fromXc
               + ",[" + lastXci.cStart + "," + lastXci.cEnd + "]) to cur("
               + toXc + ",[" + cXci.cStart + "," + cXci.cEnd + "])");
       int fr, to;
-      fr = Math.min(lastXci.cStart, lastXci.cEnd);
-      to = Math.max(lastXci.cStart, lastXci.cEnd);
+      fr = Math.min(lastXci.cStart, cXci.cStart);
+      to = Math.max(lastXci.cEnd, cXci.cEnd);
       int[] mappedPos = forFromX.getMappedPositionsFor(fr, to);
       if (mappedPos != null)
       {
@@ -1109,33 +1121,7 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
           // }
           // else
           {
-            av.getColumnSelection().addElement(c);
-          }
-        }
-      }
-      // and again for most recent corner of drag
-      fr = Math.min(cXci.cStart, cXci.cEnd);
-      to = Math.max(cXci.cStart, cXci.cEnd);
-      mappedPos = forFromX.getMappedPositionsFor(fr, to);
-      if (mappedPos != null)
-      {
-        for (int pair = 0; pair < mappedPos.length; pair += 2)
-        {
-          jalview.bin.Console.trace("Marking " + fr + " to " + to
-                  + " mapping to sequence positions " + mappedPos[pair]
-                  + " to " + mappedPos[pair + 1]);
-          for (int c = mappedPos[pair]; c <= mappedPos[pair + 1]; c++)
-          {
-            // if (cma.sequenceRef != null)
-            // {
-            // int col =
-            // cma.sequenceRef.findIndex(cma.sequenceRef.getStart()+c);
-            // av.getColumnSelection().addElement(col);
-            // }
-            // else
-            {
-              av.getColumnSelection().addElement(c);
-            }
+            av.getColumnSelection().addElement(c-1);
           }
         }
       }
@@ -1311,9 +1297,11 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         ContactGeometry cgeom = new ContactGeometry(clist, ann.graphHeight);
         ContactGeometry.contactInterval ci = cgeom.mapFor(rowAndOffset);
         ContactRange cr = clist.getRangeFor(ci.cStart, ci.cEnd);
-        tooltip = "Contact from " + clist.getPosition() + ", [" + ci.cStart
-                + " - " + ci.cEnd + "]" + "<br/>Mean:" + cr.getMean();
-
+        StringBuilder tooltipb = new StringBuilder();
+        tooltipb.append("Contact from ")
+        .append(clist.getPosition()).append(", [").append(ci.cStart).append(" - ").append(ci.cEnd).append("]").append("<br/>Mean:");
+        Format.appendPercentage(tooltipb, (float)cr.getMean(),2);
+        tooltip = tooltipb.toString();
         int col = ann.sequenceRef.findPosition(column);
         int[][] highlightPos;
         int[] mappedPos = clist.getMappedPositionsFor(ci.cStart, ci.cEnd);
index aaded9e..2df84a9 100755 (executable)
@@ -199,7 +199,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
 
     gg.translate(0, transY);
 
-    drawIds(gg, av, ss, es, searchResults,true);
+    drawIds(gg, av, ss, es, searchResults, true, getWidth());
 
     gg.translate(0, -transY);
 
@@ -256,7 +256,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     gg.fillRect(0, 0, getWidth(), imgHeight);
 
     drawIds(gg, av, av.getRanges().getStartSeq(),
-            av.getRanges().getEndSeq(), searchResults,true);
+            av.getRanges().getEndSeq(), searchResults,true, getWidth());
 
     gg.dispose();
 
@@ -274,9 +274,12 @@ public class IdCanvas extends JPanel implements ViewportListenerI
    * @param startSeq
    * @param endSeq
    * @param selection
+   * @param forGUI when false rendering for print
+   * @param panelWidth width used to calculate righthand margin - usually idCanvas.getWidth()
+   * 
    */
   void drawIds(Graphics2D g, AlignViewport alignViewport,
-          final int startSeq, final int endSeq, List<SequenceI> selection, boolean forGUI)
+          final int startSeq, final int endSeq, List<SequenceI> selection, boolean forGUI, int panelWidth)
   {
     Font font = alignViewport.getFont();
     if (alignViewport.isSeqNameItalics())
@@ -309,7 +312,6 @@ public class IdCanvas extends JPanel implements ViewportListenerI
     }
 
     // Now draw the id strings
-    int panelWidth = getWidth();
     int xPos = 0;
 
     // Now draw the id strings
@@ -454,7 +456,7 @@ public class IdCanvas extends JPanel implements ViewportListenerI
         if (!manuallyAdjusted())
         {
           int getAnnotationsIdWidth = labels.drawLabels(g, false, -1, false,forGUI,
-                  null);
+                  null, false);
           thisIdWidth = idWidth < 0 ? getAnnotationsIdWidth : idWidth;
           if (thisIdWidth > getWidth)
           {
index ff409e0..52c1d18 100755 (executable)
@@ -113,8 +113,11 @@ public class SplashScreen extends JPanel
    */
   public SplashScreen(boolean isTransient)
   {
-    Desktop.instance.acquireDialogQueue();
     this.transientDialog = isTransient;
+    if (this.transientDialog)
+    {
+      Desktop.instance.acquireDialogQueue();
+    }
 
     if (Platform.isJS()) // BH 2019
     {
@@ -228,7 +231,8 @@ public class SplashScreen extends JPanel
   protected boolean refreshText()
   {
     String newtext = Desktop.instance.getAboutMessage();
-    // jalview.bin.Console.errPrintln("Text found: \n"+newtext+"\nEnd of newtext.");
+    // jalview.bin.Console.errPrintln("Text found: \n"+newtext+"\nEnd of
+    // newtext.");
     if (oldTextLength != newtext.length())
     {
       iframe.setVisible(false);
@@ -324,7 +328,6 @@ public class SplashScreen extends JPanel
     }
 
     closeSplash();
-    Desktop.instance.releaseDialogQueue();
   }
 
   /**
@@ -332,6 +335,10 @@ public class SplashScreen extends JPanel
    */
   public void closeSplash()
   {
+    if (this.transientDialog)
+    {
+      Desktop.instance.releaseDialogQueue();
+    }
     try
     {
 
index 9fd4de6..a1443c8 100644 (file)
@@ -1,6 +1,8 @@
 package jalview.renderer;
 
+import java.util.Arrays;
 import java.util.Iterator;
+import java.util.List;
 
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
@@ -19,21 +21,45 @@ public class ContactGeometry
 
   final ContactListI contacts;
 
+  /**
+   * how many pixels per contact (1..many)
+   */
   final int pixels_step;
 
+  /**
+   * how many contacts per pixel (many > 0)
+   */
   final double contacts_per_pixel;
 
+  /**
+   * number of contacts being mapped
+   */
   final int contact_height;
 
+  /**
+   * number of pixels to map contact_height to
+   */
   final int graphHeight;
 
+  /**
+   * number of contacts for each pixel_step - to last whole contact
+   */
+  final double contacts_step;
+  
+  final int lastStep;
+
+  /**
+   * Bean used to map from a range of contacts to a range of pixels
+   * @param contacts
+   * @param graphHeight Number of pixels to map given range of contacts
+   */
   public ContactGeometry(final ContactListI contacts, int graphHeight)
   {
     this.contacts = contacts;
     this.graphHeight = graphHeight;
     contact_height = contacts.getContactHeight();
     // fractional number of contacts covering each pixel
-    contacts_per_pixel = (graphHeight < 1) ? contact_height
+    contacts_per_pixel = (graphHeight <= 1) ? contact_height
             : ((double) contact_height) / ((double) graphHeight);
 
     if (contacts_per_pixel >= 1)
@@ -47,6 +73,8 @@ public class ContactGeometry
       pixels_step = (int) Math
               .ceil(((double) graphHeight) / (double) contact_height);
     }
+    contacts_step = pixels_step*contacts_per_pixel;
+    lastStep = (int) Math.min((double)graphHeight, ((double)graphHeight)/((double)pixels_step));
   }
 
   public class contactInterval
@@ -69,6 +97,22 @@ public class ContactGeometry
 
     public final int pEnd;
 
+    @Override
+    public boolean equals(Object obj)
+    {
+      if (obj == null || !(obj instanceof contactInterval))
+      {
+        return false;
+      }
+      contactInterval them = (contactInterval) obj;
+      return cStart == them.cStart && cEnd == them.cEnd && pEnd == them.pEnd
+              && pStart == them.pStart;
+    }
+    @Override
+    public String toString()
+    {
+      return "Contacts ["+cStart+","+cEnd+"] : Pixels ["+pStart+","+pEnd+"]";
+    }
   }
 
   /**
@@ -100,7 +144,7 @@ public class ContactGeometry
       {
         // TODO: turn into function on hiddenColumns and create test !!
         Iterator<int[]> viscont = hiddenColumns.getVisContigsIterator(
-                mappedRange[p], mappedRange[p + 1], false);
+                -1+mappedRange[p], -1+mappedRange[p + 1], false);
         containsHidden = !viscont.hasNext();
         if (!containsHidden)
         {
@@ -112,8 +156,8 @@ public class ContactGeometry
       }
       else
       {
-        rowsel = columnSelection.intersects(mappedRange[p],
-                mappedRange[p + 1]);
+        rowsel = columnSelection.intersects(-1+mappedRange[p],
+                -1+mappedRange[p + 1]);
       }
     }
     return rowsel;
@@ -121,21 +165,56 @@ public class ContactGeometry
   }
 
   /**
+   * Return mapped cell intersecting pStart \
+   * 
+   * FIXME: REDUNDANT METHOD - COULD DELETE FIXME: OR RE-IMPLEMENT AS EFFICIENT
+   * RANGE QUERY
    * 
    * @param pStart
+   *          [0..)
    * @param pEnd
-   * @return range for
+   * @return nearest full cell containing pStart - does not set
+   *         contactInterval.pEnd or cEnd to equivalent position on pEnd !
    */
   public contactInterval mapFor(int pStart, int pEnd)
   {
-    int cStart = (int) Math.floor(pStart * contacts_per_pixel);
-    contactInterval ci = new contactInterval(cStart,
-            (int) Math.min(contact_height,
-                    Math.ceil(
-                            cStart + (pEnd - pStart) * contacts_per_pixel)),
-            pStart, pEnd);
-
-    return ci;
+    if (pStart < 0)
+    {
+      pStart = 0;
+    }
+    if (pEnd < pStart)
+    {
+      pEnd = pStart;
+    }
+    if (pEnd >= graphHeight)
+    {
+      pEnd = graphHeight - 1;
+    }
+    if (pStart >= graphHeight)
+    {
+      pStart = graphHeight - pixels_step;
+    }
+    int step = Math.floorDiv(pStart, pixels_step);
+    return findStep(step);
+  }
+
+  /**
+   * 
+   * @param step
+   *          [0..) n steps covering height and contactHeight
+   * @return contactInterval for step, or null if out of bounds
+   */
+  contactInterval findStep(int step)
+  {
+    if (step < 0 || step > lastStep)
+    {
+      return null;
+    }
+    return new contactInterval((int) Math.floor(contacts_step * step),
+            -1 + (int) Math.min(contact_height,
+                    Math.floor(contacts_step * (step + 1))),
+            pixels_step * step,
+            Math.min(graphHeight, (step + 1) * pixels_step) - 1);
   }
 
   /**
@@ -146,15 +225,31 @@ public class ContactGeometry
    */
   public contactInterval mapFor(int pCentre)
   {
-    int pStart = Math.max(pCentre - pixels_step, 0);
-    int pEnd = Math.min(pStart + pixels_step, graphHeight);
-    int cStart = (int) Math.floor(pStart * contacts_per_pixel);
-    contactInterval ci = new contactInterval(cStart,
-            (int) Math.min(contact_height,
-                    Math.ceil(cStart + (pixels_step) * contacts_per_pixel)),
-            pStart, pEnd);
-
-    return ci;
+    if (pCentre >= graphHeight + pixels_step)
+    {
+      return null;
+    }
+    int step = Math.floorDiv(pCentre, pixels_step);
+    return findStep(step);
+  }
+
+  public List<contactInterval> allSteps()
+  {
+    contactInterval[] array = new contactInterval[lastStep + 1];
+    int csum = 0, psum = 0;
+    for (int i = 0; i <= lastStep; i++)
+    {
+      array[i] = findStep(i);
+      csum += 1 + array[i].cEnd - array[i].cStart;
+      psum += 1 + array[i].pEnd - array[i].pStart;
+    }
+    if (csum != contact_height || psum != graphHeight)
+    {
+      System.err.println("csum = " + csum + " not " + contact_height + "\n"
+              + "psum = " + psum + " not " + graphHeight);
+      return null;
+    }
+    return Arrays.asList(array);
   }
 
   public Iterator<contactInterval> iterateOverContactIntervals(
index 0471145..af8838c 100644 (file)
@@ -49,6 +49,15 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
      */
     Color selMinColor, selMaxColor;
 
+    /**
+     * 
+     * @param no_data - colour when no data available
+     * @param hidden - colour if this row is hidden
+     * @param maxColor - colour for maximum value of contact
+     * @param minColor - colour for minimum value of contact
+     * @param selMinColor - min colour if the contact has been selected
+     * @param selMaxColor - max colour if contact is selected
+     */
     public Shading(Color no_data, Color hidden, Color maxColor,
             Color minColor, Color selMinColor, Color selMaxColor)
     {
@@ -79,8 +88,8 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
       {
         return new Shading(Color.pink, Color.red,
 
-                new Color(246, 252, 243), new Color(0, 60, 26),
-                new Color(26, 0, 60), new Color(243, 246, 252));
+                new Color(247, 252, 245), new Color(0, 68, 28),
+                new Color(28, 0, 68), new Color(245,247,252));
       }
     };
   }
@@ -169,14 +178,14 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
       for (int ht = 0, botY = topY
               - _aa.height; ht < _aa.graphHeight; ht += cgeom.pixels_step)
       {
-        ContactGeometry.contactInterval ci = cgeom.mapFor(ht,
-                ht + cgeom.pixels_step);
+        ContactGeometry.contactInterval ci = cgeom.mapFor(ht);
         // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel);
         // cend = (int) Math.min(contact_height,
         // Math.ceil(cstart + contacts_per_pixel * pixels_step));
 
         Color col;
-        boolean rowsel = false, containsHidden = false;
+        boolean rowsel = false;
+        boolean containsHidden = false;
         if (columnSelection != null)
         {
           rowsel = cgeom.intersects(ci, columnSelection, hiddenColumns,
@@ -217,7 +226,7 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
         g.setColor(col);
         if (cgeom.pixels_step > 1)
         {
-          g.fillRect(x * charWidth, botY+ht, charWidth, 1 + cgeom.pixels_step);
+          g.fillRect(x * charWidth, botY+ht, charWidth, cgeom.pixels_step);
         }
         else
         {
@@ -248,7 +257,7 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI
   {
     ContactRange cr = cl.getRangeFor(i, j);
     // average for moment - probably more interested in maxIntProj though
-    return jalview.util.ColorUtils.getGraduatedColour((float) cr.getMean(),
+    return jalview.util.ColorUtils.getGraduatedColour((float) cr.getMin(),
             0, shade.selMinColor, max, shade.selMaxColor);
   }
 
index 9bd247a..2f1ddc0 100644 (file)
@@ -1050,7 +1050,7 @@ public class StructureSelectionManager
           int indexpos = sm.getSeqPos(atom.getPdbResNum());
           if (lastipos != indexpos || lastseq != sm.sequence)
           {
-            results.addResult(sm.sequence, indexpos, indexpos);
+            results.appendResult(sm.sequence, indexpos, indexpos);
             lastipos = indexpos;
             lastseq = sm.sequence;
             // construct highlighted sequence list
index ce7ab6d..df2c76c 100755 (executable)
@@ -940,7 +940,7 @@ public class Format
   }
 
   /**
-   * Bespoke method to format percentage float value to the specified number of
+   * Bespoke method to format a percentage (or any other) float value to the specified number of
    * decimal places. Avoids use of general-purpose format parsers as a
    * processing hotspot.
    * 
index 8dae8f3..ecec67d 100644 (file)
@@ -96,7 +96,7 @@ public class CommandsTest
   }
   */
 
-  @Test(groups = "Functional", dataProvider = "cmdLines")
+  @Test(groups = {"Functional","testTask3"}, dataProvider = "cmdLines")
   public void commandsOpenTest(String cmdLine, boolean cmdArgs,
           int numFrames, String[] sequences)
   {
@@ -147,7 +147,7 @@ public class CommandsTest
 
   @Test(
     groups =
-    { "Functional", "testTask1" },
+    { "Functional", "testTask3" },
     dataProvider = "structureImageOutputFiles")
   public void structureImageOutputTest(String cmdLine, String[] filenames)
           throws IOException
index 49a721f..f737c92 100644 (file)
@@ -62,8 +62,8 @@ public class HiDPISettingTest1
     Cache.loadProperties("test/jalview/bin/hidpiTestProps.jvprops");
     Jalview.main(
             new String[]
-            { "-nosplash", "-nonews", "-noquestionnaire",
-                "-nowebservicediscovery" });
+            { "--nosplash", "--nonews", "--noquestionnaire",
+                "--nowebservicediscovery" });
 
     af = new FileLoader().LoadFileWaitTillLoaded("examples/uniref50.fa",
             DataSourceType.FILE);
index bdfa5a3..fa7aca5 100644 (file)
@@ -116,7 +116,11 @@ public class PAEContactMatrixTest
     AlignmentI alForSeq = new Alignment(new SequenceI[] { alseq });
     newaa = AlignmentUtils.addReferenceAnnotationTo(alForSeq, alseq, newaa,
             null);
-    ContactListI alcl = alForSeq.getContactListFor(newaa, 1);
+    // check for null on out of bounds
+    ContactListI alcl = alForSeq.getContactListFor(newaa, newaa.annotations.length);
+    assertNull(alcl,"Should've gotten null!");
+    // now check for mapping
+    alcl = alForSeq.getContactListFor(newaa, 1);
     assertNotNull(alcl);
     mappedCl = alcl.getMappedPositionsFor(0, 4);
     assertNotNull(mappedCl);
index b1bb43c..8059af9 100644 (file)
@@ -206,6 +206,32 @@ public class SearchResultsTest
     assertFalse(m.contains(null, 3, 3));
   }
 
+  @Test(groups = { "Functional" })
+  public void testMatchAdjacent()
+  {
+    SequenceI seq1 = new Sequence("", "abcdefghijklm");
+    SequenceI seq2 = new Sequence("", "abcdefghijklm");
+    SearchResultMatchI m = new SearchResults().new Match(seq1, 2, 5);
+
+    assertTrue(m.adjacent(seq1, 2, 5));
+    assertTrue(m.adjacent(seq1, 3, 5));
+    assertTrue(m.adjacent(seq1, 2, 4));
+    assertTrue(m.adjacent(seq1, 3, 3));
+
+    assertTrue(m.adjacent(seq1, 2, 6));
+    assertTrue(m.adjacent(seq1, 1, 5));
+    assertTrue(m.adjacent(seq1, 1, 8));
+    assertFalse(m.adjacent(seq1, 0, 0));
+    assertFalse(m.adjacent(seq1, 7, 8));
+    assertTrue(m.adjacent(seq1, 6, 8));
+    assertTrue(m.adjacent(seq1, 5, 8));
+    assertTrue(m.adjacent(seq1, 0, 1));
+    
+    
+    assertFalse(m.adjacent(seq2, 3, 3));
+    assertFalse(m.adjacent(null, 3, 3));
+  }
+
   /**
    * test markColumns for creating column selections
    */
@@ -308,6 +334,36 @@ public class SearchResultsTest
   }
 
   /**
+   * Test to verify appending creates a minimal set of results
+   */
+  @Test(groups = { "Functional" })
+  public void testAppendResult()
+  {
+    SequenceI seq1 = new Sequence("", "abcdefghijklm"),seq2=new Sequence("","defdefdefdef");
+    SearchResultsI sr = new SearchResults();
+    sr.appendResult(seq1, 3, 5);
+    assertEquals(1, sr.getCount());
+    sr.appendResult(seq1, 3, 6);
+    assertEquals(1, sr.getCount());
+    sr.appendResult(seq1, 8, 8);
+    assertEquals(2, sr.getCount());
+    sr.appendResult(seq1, 7, 7);
+    assertEquals(1, sr.getCount());
+    sr.appendResult(seq2, 7, 7);
+    assertEquals(2, sr.getCount());
+    sr.appendResult(seq2, 2, 7);
+    assertTrue(sr.appendResult(seq2, 7, 49));
+    assertTrue(sr.appendResult(seq2, 0, 30));
+    assertEquals(2, sr.getCount());
+    int c=0;
+    for (SearchResultMatchI sre: sr.getResults())
+    {
+      c++;
+    }
+    assertEquals(c,2);
+    
+  }
+  /**
    * Test for method that checks if search results matches a sequence region
    */
   @Test(groups = { "Functional" })
index 9dec19b..a74e51d 100644 (file)
@@ -65,7 +65,7 @@ public class JmolViewerTest
   {
     Jalview.main(
             new String[]
-            { "-noquestionnaire", "-nonews", "-props",
+            { "--noquestionnaire", "--nonews", "--props",
                 "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
   }
 
index c8e30a7..e191980 100644 (file)
@@ -81,7 +81,7 @@ public class JalviewChimeraView
   {
     Jalview.main(
             new String[]
-            { "-noquestionnaire", "-nonews", "-props",
+            { "--noquestionnaire", "--nonews", "--props",
                 "test/jalview/ext/rbvi/chimera/testProps.jvprops" });
     Cache.setProperty(Preferences.STRUCTURE_DISPLAY,
             ViewerType.CHIMERA.name());
index ab2a1d0..2affde0 100644 (file)
@@ -69,7 +69,7 @@ public class AlignFrameTest
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
   @AfterMethod(alwaysRun = true)
index 4537dcc..7399c66 100644 (file)
@@ -77,7 +77,7 @@ public class AlignViewportTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+            { "--nonews", "--props", "test/jalview/testProps.jvprops" });
 
     /*
      * remove any sequence mappings left lying around by other tests
index c1896bc..892cdef 100644 (file)
@@ -24,6 +24,7 @@ import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotEquals;
 import static org.testng.Assert.assertNotNull;
 
+import java.awt.Container;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.FontMetrics;
@@ -52,7 +53,7 @@ public class AlignmentPanelTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+            { "--nonews", "--props", "test/jalview/testProps.jvprops" });
 
     Cache.applicationProperties.setProperty("SHOW_IDENTITY",
             Boolean.TRUE.toString());
@@ -208,8 +209,9 @@ public class AlignmentPanelTest
   /**
    * Test the variant of calculateIdWidth that computes the longest of any
    * sequence name or annotation label width
+   * FIXME: JAL-4291: test needs updating for JAL-244 and JAL-4091
    */
-  @Test(groups = "Functional")
+  @Test(groups = "Functional",enabled=false)
   public void testCalculateIdWidth_withMaxWidth()
   {
     AlignViewportI av = af.alignPanel.getAlignViewport();
@@ -218,13 +220,20 @@ public class AlignmentPanelTest
     av.setShowAnnotation(false);
     av.setIdWidth(18);
 
+    FontMetrics fmfor = new Container()
+            .getFontMetrics(new Font(af.viewport.font.getName(),
+                    Font.ITALIC, af.viewport.font.getSize()));
+
     /*
      * note 4 pixels 'padding' are added to the longest seq name/annotation label
      */
     Dimension d = af.alignPanel.calculateIdWidth(2000);
+    // Assumption ID_WIDTH_PADDING == 4
+    int expwidth = 3 + fmfor.stringWidth("Conservation");
+
     assertEquals(d.width, 166); // 4 + pixel width of "Q93Z60_ARATH/1-118"
     assertEquals(d.height, 12); // fixed value (not used?)
-    assertEquals(av.getIdWidth(), 18); // not changed by this method
+    assertEquals(av.getIdWidth(), expwidth); // not changed by this method
 
     /*
      * make the longest sequence name longer
@@ -243,11 +252,10 @@ public class AlignmentPanelTest
      */
     AlignmentAnnotation aa = av.getAlignment().getAlignmentAnnotation()[0];
     aa.label = "THIS IS A VERY LONG LABEL INDEED";
-    FontMetrics fmfor = af.alignPanel
-            .getFontMetrics(af.alignPanel.getAlabels().getFont());
-    // Assumption ID_WIDTH_PADDING == 4
-    int expwidth = 4 + fmfor.stringWidth(aa.label);
     d = af.alignPanel.calculateIdWidth(2000);
+    // Assumption ID_WIDTH_PADDING == 3
+    expwidth = 3 + fmfor.stringWidth(aa.label);
+
     assertEquals(d.width, expwidth); // 228 == ID_WIDTH_PADDING + pixel width of
                                      // "THIS IS A VERY LONG LABEL INDEED"
     assertEquals(d.height, 12);
index 897f28e..5bf3507 100644 (file)
@@ -71,7 +71,7 @@ public class DesktopTests
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
   @AfterMethod(alwaysRun = true)
index 1b988ec..5ef693a 100644 (file)
@@ -57,7 +57,7 @@ public class FeatureSettingsTest
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
   @AfterMethod(alwaysRun = true)
index a9eca49..475af2f 100644 (file)
@@ -120,7 +120,7 @@ public class FreeUpMemoryTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+            { "--nonews", "--props", "test/jalview/testProps.jvprops" });
     String True = Boolean.TRUE.toString();
     Cache.applicationProperties.setProperty("SHOW_ANNOTATIONS", True);
     Cache.applicationProperties.setProperty("SHOW_QUALITY", True);
index b19f160..a6a9642 100644 (file)
@@ -55,7 +55,7 @@ public class QuitHandlerTest
 
     Jalview.main(
             new String[]
-            { "-nowebservicediscovery", "-nosplash", "-nonews" });
+            { "--nowebservicediscovery", "--nosplash", "--nonews" });
   }
 
   @AfterClass(alwaysRun = true)
index 446c6ad..1523741 100644 (file)
@@ -252,7 +252,7 @@ public class ScalePanelTest
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
 }
index 701431b..4e8758b 100644 (file)
@@ -882,7 +882,7 @@ public class SeqPanelTest
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
   /**
index 9b49232..bf78c85 100644 (file)
@@ -51,7 +51,7 @@ public class SequenceRendererTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+            { "--nonews", "--props", "test/jalview/testProps.jvprops" });
   }
 
   @BeforeMethod(alwaysRun = true)
index 1b62ce3..e369a02 100644 (file)
@@ -199,7 +199,7 @@ public class AnnotatedPDBFileInputTest
   {
     jalview.bin.Jalview
             .main(new String[]
-            { "-props", "test/jalview/io/testProps.jvprops" });
+            { "--props", "test/jalview/io/testProps.jvprops" });
   }
 
   /**
index 59c5986..29af915 100644 (file)
@@ -56,7 +56,7 @@ public class JalviewExportPropertiesTests
   {
     jalview.bin.Jalview
             .main(new String[]
-            { "-props", "test/jalview/io/testProps.jvprops" });
+            { "--props", "test/jalview/io/testProps.jvprops" });
   }
 
   /**
index 288444e..5f9af25 100644 (file)
@@ -45,7 +45,7 @@ public class JvCacheableInputBoxTest
     appCache = AppCache.getInstance();
   }
 
-  @Test(groups = { "Functional" })
+  @Test(groups = { "Functional", "testTask2" })
   public void getUserInputTest()
   {
     String userInput = cacheBox.getUserInput();
diff --git a/test/jalview/renderer/ContactGeometryTest.java b/test/jalview/renderer/ContactGeometryTest.java
new file mode 100644 (file)
index 0000000..6e38e89
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ * 
+ * This file is part of Jalview.
+ * 
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License 
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *  
+ * Jalview is distributed in the hope that it will be useful, but 
+ * WITHOUT ANY WARRANTY; without even the implied warranty 
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
+ * PURPOSE.  See the GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
+package jalview.renderer;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+import org.testng.annotations.Test;
+
+import jalview.datamodel.AlignmentAnnotation;
+import jalview.datamodel.ColumnSelection;
+import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactRange;
+import jalview.datamodel.Mapping;
+import jalview.datamodel.SeqDistanceContactMatrix;
+import jalview.datamodel.Sequence;
+import jalview.datamodel.SequenceI;
+import jalview.util.MapList;
+import jalview.util.StringUtils;
+import jalview.ws.datamodel.MappableContactMatrixI;
+
+public class ContactGeometryTest
+{
+  @Test(groups="Functional")
+  public void testCoverageofRange()
+  {
+    // a really dumb test to make sure we really cover the requested pixel and
+    // contactList range for any dimension of each
+    for (int range = 12; range < 2000; range += 35)
+    {
+      StringBuilder sb = new StringBuilder();
+      while (sb.length() < range)
+      {
+        sb.append("c");
+      }
+      SequenceI sq = new Sequence("a", sb.toString());
+      MappableContactMatrixI cm = new SeqDistanceContactMatrix(range);
+      AlignmentAnnotation cm_aan = sq.addContactList(cm);
+      ContactListI cl = sq.getContactListFor(cm_aan, 10);
+      assertNotNull(cl);
+      for (int ht = range / 2; ht < range * 3; ht++)
+      {
+        ContactGeometry clgeom = new ContactGeometry(cl, ht);
+        assertNotNull(clgeom.allSteps());
+      }
+    }
+  }
+  @Test(groups = "Functional")
+  public void testContactGeometry()
+  {
+    SequenceI sq = new Sequence("a", "SSSQ");
+    MappableContactMatrixI cm = new SeqDistanceContactMatrix(4);
+    AlignmentAnnotation cm_aan = sq.addContactList(cm);
+    checkConsistencyFor(sq,cm_aan);
+    // Also check all is good when there's a sequence mapping involved
+    MappableContactMatrixI newcm=cm.liftOver(sq,
+            new Mapping(sq, new MapList(new int[]
+            { 1, 4 }, new int[] { 1, 4 }, 1, 1)));
+    AlignmentAnnotation mapped_cm = sq.addContactList(newcm);
+    checkConsistencyFor(sq,mapped_cm);    
+  }
+  // Do some asserts for a sequence and a contact matrix
+  private void checkConsistencyFor(SequenceI sq, AlignmentAnnotation cm_aan)
+  {
+    int col=1;
+    ContactListI cl = sq.getContactListFor(cm_aan, col);
+    assertNotNull(cl);
+    assertEquals(cl.getContactHeight(),4);
+    
+    // Map contacts 0 to 3 to a tiny range and check    
+    ContactGeometry  testee = new ContactGeometry(cl,2);
+    assertEquals(testee.contacts_per_pixel,2d);
+    ContactGeometry.contactInterval lastInterval = testee.mapFor(1);
+    assertEquals(lastInterval.cStart,2);
+    assertEquals(lastInterval.cEnd,3);
+    assertEquals(lastInterval.pStart,1);
+    assertEquals(lastInterval.pEnd,1);
+    ContactGeometry.contactInterval another = testee.mapFor(1,2); 
+    assertEquals(lastInterval,another);
+    // Also check for a big pixel range
+    testee = new ContactGeometry(cl, 395);
+    lastInterval = testee.mapFor(390, 395); // 395 is one over limit.
+    assertNotNull(lastInterval);
+    assertEquals(lastInterval.cEnd,3);
+    assertEquals(lastInterval.pEnd,394);
+    // Map contacts 0 to 3 to Pixels 0-9, 10-19, 20-29, 30-39
+    testee = new ContactGeometry(cl, 40);
+
+    // verify mapping from pixel to contacts
+    
+    // renderer thinks base 0 for pixel coordinates
+    // contact coordinates are base 1
+    for (int p = 0; p < 40; p++)
+    {
+      int expectC=(p / 10);
+      int expectP=(expectC)*10;
+      ContactGeometry.contactInterval ci_at = testee.mapFor(p),
+              ci_from = testee.mapFor(p, p);
+      assertNotNull(ci_at);
+      // mapFor and map should locate the same pixel window
+      assertEquals(ci_at.cStart, expectC,"Different cStart at position "+p);
+      assertEquals(ci_at.cEnd, expectC,"Different cEnd at position "+p);
+      assertEquals(ci_at.pStart,expectP, "Different pStart at position "+p);
+      assertEquals(ci_at.pEnd,expectP+9, "Different pEnd at position "+p);
+      
+      assertEquals(ci_from,ci_at, "Different contactIntervals at position "+p);
+      // also test getRangeFor
+      ContactRange cr = cl.getRangeFor(ci_at.cStart, ci_at.cEnd);
+      assertEquals(cr.getFrom_column(),cr.getTo_column());
+      assertEquals((double) cr.getMean(),(double)Math.abs(col-cr.getFrom_column()), "Didn't resolve expected value at position "+p);
+    }
+    
+    ContactGeometry.contactInterval ci_at0 = testee.mapFor(0);
+    ContactGeometry.contactInterval ci_at9 = testee.mapFor(9);
+    assertNotNull(ci_at9);
+    
+    assertEquals(ci_at0,ci_at9);
+
+    // Adjacent cell
+    ContactGeometry.contactInterval ci_at10 = testee.mapFor(10);
+    assertNotNull(ci_at10);
+    ContactGeometry.contactInterval ci_at11 = testee.mapFor(11);
+    assertNotNull(ci_at11);
+
+    assertEquals(ci_at11,ci_at10,"Off-by-one in ContactGeometry mapping.");
+
+    assertNotEquals(ci_at0,ci_at10,"Expected adjacent cells to be not equal.");
+    
+    // verify adjacent window is mapped
+    assertEquals(ci_at11.cStart,ci_at9.cStart+1);
+    
+    assertEquals(ci_at9.cEnd+1,ci_at11.cStart);
+    assertEquals(ci_at9.cEnd+1,ci_at11.cEnd);
+    
+    // verify interval/intersection
+    // column selection is base 0
+    ColumnSelection cs = new ColumnSelection();
+    cs.addElement(2);
+
+    boolean mask = false;
+    do
+    {
+      assertFalse(testee.intersects(ci_at0, cs, null, mask));
+      assertFalse(testee.intersects(ci_at11, cs, null, mask));
+      assertTrue(testee.intersects(testee.mapFor(21), cs, null, mask));
+      assertFalse(testee.intersects(testee.mapFor(31), cs, null, mask));
+      cs.addElement(3);
+      assertTrue(testee.intersects(testee.mapFor(31), cs, null, mask));
+      cs.removeElement(2);
+      assertFalse(testee.intersects(testee.mapFor(21), cs, null, mask));
+      mask = !mask;
+    } while (!mask);    
+    
+  }
+}
index 944f147..53f98c3 100644 (file)
@@ -625,7 +625,7 @@ public class FeatureRendererTest
   {
     Jalview.main(
             new String[]
-            { "-nonews", "-props", "test/jalview/testProps.jvprops" });
+            { "--nonews", "--props", "test/jalview/testProps.jvprops" });
 
     // codons for MCWHSE
     String cdsSeq = ">cds\nATGtgtTGGcacTCAgaa";
index 0184f12..052bb8b 100644 (file)
@@ -186,7 +186,7 @@ public class ColourSchemesTest
      * use read-only test properties file
      */
     Cache.loadProperties("test/jalview/io/testProps.jvprops");
-    Jalview.main(new String[] { "-nonews" });
+    Jalview.main(new String[] { "--nonews" });
   }
 
   @AfterClass(alwaysRun = true)