432c144417eb7a077b01ad04999942ec6b8bb3c1
hiram
  Wed Jan 7 14:47:39 2015 -0800
bring up to date with Robert's latest code to help fix bugs refs #9741
diff --git src/hg/hgTracks/rmskJoinedTrack.c src/hg/hgTracks/rmskJoinedTrack.c
index 8642be6..de7af0a 100644
--- src/hg/hgTracks/rmskJoinedTrack.c
+++ src/hg/hgTracks/rmskJoinedTrack.c
@@ -1,23 +1,26 @@
 /* joinedRmskTrack - A comprehensive RepeatMasker visualization track
  *                   handler. This is an extension of the original
  *                   rmskTrack.c written by UCSC.
  *
- *  Written by Robert Hubley 10/2012
+ *  Written by Robert Hubley 10/2012-11/2014
  *
  *  Modifications:
  *
+ *  11/2014
+ *     Request for traditional pack/squish modes.
+ *
  *  7/2014
  *     With the help of Jim Kent we modified the
  *     glyph design to more closely resemble the
  *     existing browser styles.
  */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "hash.h"
 #include "linefile.h"
 #include "jksql.h"
 #include "hdb.h"
 #include "hgTracks.h"
@@ -52,118 +55,101 @@
 struct hash *classHash = NULL;
 static struct repeatItem *otherRepeatItem = NULL;
 
 /* Hash of subtracks
  *   The joinedRmskTrack is designed to be used as a composite track.
  *   When rmskJoinedLoadItems is called this hash is populated with the
  *   results of one or more table queries
  */
 struct hash *subTracksHash = NULL;
 
 struct subTrack
 {
   // The number of display levels used in levels[]
 int levelCount;
   // The rmskJoined records from table query
-struct rmskJoined *levels[30];
+struct rmskJoined **levels;
 };
 
 
 // Repeat Classes: Display names
 static char *rptClassNames[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple", "Low Complexity", "Satellite",
 "RNA", "Other", "Unknown",
 };
 
 // Repeat Classes: Data names
 static char *rptClasses[] = {
 "SINE", "LINE", "LTR", "DNA", "Simple_repeat", "Low_complexity",
 "Satellite", "RNA", "Other", "Unknown",
 };
 
 
-/* Repeat class to color mappings. I took these from a
- * online color dictionary website:
- *
- *     http://people.csail.mit.edu/jaffer/Color/Dictionaries
- *
- *  I used the html4 catalog and tried to the 10 most distinct
- *  colors.
+/* Repeat class to color mappings.  
+ *   - Currently borrowed from snakePalette.
  *
  *  NOTE: If these are changed, do not forget to update the
  *        help table in joinedRmskTrack.html
  */
-// trying colors from snakePalette
 static Color rmskJoinedClassColors[] = {
 0xff1f77b4,			// SINE - red
 0xffff7f0e,			// LINE - lime
 0xff2ca02c,			// LTR - maroon
 0xffd62728,			// DNA - fuchsia
 0xff9467bd,			// Simple - yellow
 0xff8c564b,			// LowComplex - olive
 0xffe377c2,			// Satellite - blue
 0xff7f7f7f,			// RNA - green
 0xffbcbd22,			// Other - teal
 0xff17becf,			// Unknown - aqua
 };
-/*
-static Color rmskJoinedClassColors[] = {
-0xff0000ff,			// SINE - red
-0xff00ff00,			// LINE - lime
-0xff000080,			// LTR - maroon
-0xffff00ff,			// DNA - fuchsia
-0xff00ffff,			// Simple - yellow
-0xff008080,			// LowComplex - olive
-0xffff0000,			// Satellite - blue
-0xff008000,			// RNA - green
-0xff808000,			// Other - teal
-0xffffff00,			// Unknown - aqua
-};
-*/
 
 // Basic range type
 struct Extents
 {
 int start;
 int end;
 };
 
 static struct Extents * getExtents(struct rmskJoined *rm)
 /* Calculate the glyph extents in genome coordinate
  * space ( ie. bp )
  *
  * It is not straightforward to determine the extent
  * of this track's glyph.  This is due to the mixture
  * of graphic and text elements within the glyph.
  *
- * TODO: Consider changing to graphics (ie. pixel) space.
  */
 {
 static struct Extents ex;
 char lenLabel[20];
 
 if (rm == NULL)
     return NULL;
 
-ex.start = rm->alignStart
-    -
+// Start position is anchored by the alignment start
+// coordinates.  Then we subtract from that space for 
+// the label.
+ex.start = rm->alignStart -
     (int) ((mgFontStringWidth(tl.font, rm->name) +
 	    LABEL_PADDING) / pixelsPerBase);
 
-
+// Now subtract out space for the unaligned sequence
+// bar starting off the graphics portion of the glyph.
 if ((rm->blockSizes[0] * pixelsPerBase) > MAX_UNALIGNED_PIXEL_LEN)
     {
+    // Unaligned sequence bar needs length label -- account for that.
     safef(lenLabel, sizeof(lenLabel), "%d", rm->blockSizes[0]);
     ex.start -= (int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth(tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     ex.start -= rm->blockSizes[0];
     }
 
 ex.end = rm->alignEnd;
 
 if ((rm->blockSizes[rm->blockCount - 1] * pixelsPerBase) >
     MAX_UNALIGNED_PIXEL_LEN)
     {
@@ -172,233 +158,276 @@
     ex.end +=
 	(int) (MAX_UNALIGNED_PIXEL_LEN / pixelsPerBase) +
 	(int) ((mgFontStringWidth(tl.font, lenLabel) +
 		LABEL_PADDING) / pixelsPerBase);
     }
 else
     {
     ex.end += rm->blockSizes[rm->blockCount - 1];
     }
 
 return &ex;
 }
 
 // A better way to organize the display
 static int cmpRepeatDiv(const void *va, const void *vb)
-/* Sort repeats by divergence.
+/* Sort repeats by divergence. TODO: Another way to do this
+   would be to sort by containment.  How many annotations
+   are contained within the repeat.  This would require
+   a "contained_count" be precalculated for each annotation.
  */
 {
 struct rmskJoined *a = *((struct rmskJoined **) va);
 struct rmskJoined *b = *((struct rmskJoined **) vb);
 
 return (b->score - a->score);
 }
 
-//static int cmpRepeatVisStart(const void *va, const void *vb)
-/* Sort repeats by display start position.  Note: We
- * account for the fact we may not start the visual
- * display at chromStart.  See MAX_UNALIGNED_PIXEL_LEN.
- */
-/*
-{
-struct rmskJoined *a = *((struct rmskJoined **) va);
-struct rmskJoined *b = *((struct rmskJoined **) vb);
-
-struct Extents *ext = NULL;
-ext = getExtents(a);
-int aStart = ext->start;
-ext = getExtents(b);
-int bStart = ext->start;
-
-return (aStart - bStart);
-}
-*/
+struct repeatItem *classRIList = NULL;
+struct repeatItem *fullRIList = NULL;
 
 static struct repeatItem * makeJRepeatItems()
 /* Initialize the track */
 {
-classHash = newHash(6);
-struct repeatItem *ri, *riList = NULL;
 int i;
+struct repeatItem *ri = NULL;
+
+// Build a permanent hash for mapping
+// class names to colors.  Used by glyph
+// drawing routine.  Also build the
+// classRIList primarily for use in tvDense
+// mode.
+if (!classHash)
+    {
+    classHash = newHash(6);
     int numClasses = ArraySize(rptClasses);
     for (i = 0; i < numClasses; ++i)
         {
         AllocVar(ri);
         ri->class = rptClasses[i];
         ri->className = rptClassNames[i];
         // New color attribute
         ri->color = rmskJoinedClassColors[i];
-    slAddHead(&riList, ri);
+        slAddHead(&classRIList, ri);
         // Hash now prebuilt to hold color attributes
         hashAdd(classHash, ri->class, ri);
         if (sameString(rptClassNames[i], "Other"))
             otherRepeatItem = ri;
         }
-slReverse(&riList);
-return riList;
+    slReverse(&classRIList);
+    }
+return classRIList;
 }
 
+
+
 static void rmskJoinedLoadItems(struct track *tg)
 /* We do the query(ies) here so we can report how deep the track(s)
  * will be when rmskJoinedTotalHeight() is called later on.
  */
 {
   /*
    * Initialize the subtracks hash - This will eventually contain
    * all the repeat data for each displayed subtrack.
    */
+
 if (!subTracksHash)
     subTracksHash = newHash(20);
 
-tg->items = makeJRepeatItems();
+//tg->items = makeJRepeatItems();
+ makeJRepeatItems();
 
 int baseWidth = winEnd - winStart;
 pixelsPerBase = (float) insideWidth / (float) baseWidth;
-if (tg->visibility == tvFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
+if ((tg->visibility == tvFull || tg->visibility == tvSquish || 
+     tg->visibility == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
+    struct repeatItem *ri = NULL;
     struct subTrack *st = NULL;
     AllocVar(st);
-    st->levels[0] = NULL;
     st->levelCount = 0;
     struct rmskJoined *rm = NULL;
     char **row;
     int rowOffset;
     struct sqlConnection *conn = hAllocConn(database);
     struct sqlResult *sr = hRangeQuery(conn, tg->table, chromName,
                                         winStart, winEnd, NULL,
                                         &rowOffset);
     struct rmskJoined *detailList = NULL;
     while ((row = sqlNextRow(sr)) != NULL)
         {
         rm = rmskJoinedLoad(row + rowOffset);
         slAddHead(&detailList, rm);
         }
-    //slSort(&detailList, cmpRepeatVisStart);
     slSort(&detailList, cmpRepeatDiv);
 
     sqlFreeResult(&sr);
     hFreeConn(&conn);
 
+    AllocArray( st->levels, slCount(&detailList));
+
     int crChromStart, crChromEnd;
     while (detailList)
         {
         st->levels[st->levelCount++] = detailList;
 
         struct rmskJoined *cr = detailList;
         detailList = detailList->next;
         cr->next = NULL;
         int rmChromStart, rmChromEnd;
         struct rmskJoined *prev = NULL;
         rm = detailList;
 
         struct Extents *ext = NULL;
         ext = getExtents(cr);
         crChromStart = ext->start;
         crChromEnd = ext->end;
 
+
+        if ( tg->visibility == tvFull )
+        {
+        AllocVar(ri);
+        ri->className = cr->name;
+        slAddHead(&fullRIList, ri);
+        }
+
+        // tvFull is one-per-line -- no need to group items in levels
+        if ( tg->visibility == tvSquish || tg->visibility == tvPack )
+             {
              while (rm)
                  {
                  ext = getExtents(rm);
                  rmChromStart = ext->start;
                  rmChromEnd = ext->end;
      
                  if (rmChromStart > crChromEnd)
                      {
                      cr->next = rm;
                      cr = rm;
                      crChromStart = rmChromStart;
                      crChromEnd = rmChromEnd;
                      if (prev)
                          prev->next = rm->next;
                      else
                          detailList = rm->next;
      
                      rm = rm->next;
                      cr->next = NULL;
                      }
                  else
                      {
                      // Save for a lower level
                      prev = rm;
                      rm = rm->next;
                      }
                  } // while ( rm )
+             } // else if ( tg->visibility == tvSquish ...
         } // while ( detailList )
     // Create Hash Entry
     hashReplace(subTracksHash, tg->table, st);
-    }				// if ( tg->visibility == tvFull
+    slReverse(&fullRIList);
+    tg->items = fullRIList;
+    } // if ((tg->visibility == tvFull || ...
+    else 
+       tg->items = classRIList;
 }
 
 static void rmskJoinedFreeItems(struct track *tg)
 /* Free up rmskJoinedMasker items. */
 {
 slFreeList(&tg->items);
 }
 
 static char * rmskJoinedName(struct track *tg, void *item)
 /* Return name of rmskJoined item track. */
 {
 static char empty = '\0';
 struct repeatItem *ri = item;
   /*
    * In detail view mode the items represent different packing
    * levels.  No need to display a label at each level.  Instead
    * Just return a label for the first level.
    */
-if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
+if ((tg->visibility == tvSquish || 
+     tg->visibility == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     if (strcmp(ri->className, "SINE") == 0)
 	return("Repeats");
     else
 	return &empty;
     }
 return ri->className;
 }
 
 
 int rmskJoinedItemHeight(struct track *tg, void *item)
 {
   // Are we in full view mode and at the scale needed to display
   // the detail view?
-if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
+if (tg->limitedVis == tvSquish && winBaseCount <= DETAIL_VIEW_MAX_SCALE )
+    {
+    if ( tg->heightPer < (MINHEIGHT/2) )
+      return (MINHEIGHT/2);
+    else
+      return tg->heightPer;
+    }
+else if ((tg->limitedVis == tvFull || tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     if ( tg->heightPer < MINHEIGHT )
       return MINHEIGHT;
     else
       return tg->heightPer;
     }
 else
     {
       return tgFixedItemHeight(tg, item);
     }
 }
 
 int rmskJoinedTotalHeight(struct track *tg, enum trackVisibility vis)
 {
   // Are we in full view mode and at the scale needed to display
   // the detail view?
-if (tg->limitedVis == tvFull && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
+if ((tg->limitedVis == tvFull || tg->limitedVis == tvSquish ||
+     tg->limitedVis == tvPack) && winBaseCount <= DETAIL_VIEW_MAX_SCALE)
     {
     // Lookup the depth of this subTrack and report it
     struct subTrack *st = hashFindVal(subTracksHash, tg->table);
     if (st)
+        {
+        tg->height = ((st->levelCount + 1) * rmskJoinedItemHeight(tg, NULL) );
         return ((st->levelCount + 1) * rmskJoinedItemHeight(tg, NULL) );
+        }
     else
+        {
+        tg->height = rmskJoinedItemHeight(tg, NULL);
         return (rmskJoinedItemHeight(tg, NULL));	// Just display one line
         }
+    }
 else
-return tgFixedTotalHeightNoOverflow(tg, vis);
+    {
+    if ( vis == tvDense )
+      {
+      tg->height = tgFixedTotalHeightNoOverflow(tg, tvDense );
+      return tgFixedTotalHeightNoOverflow(tg, tvDense );
+      }
+    else
+      {
+      tg->height = tgFixedTotalHeightNoOverflow(tg, tvFull );
+      return tgFixedTotalHeightNoOverflow(tg, tvFull );
+      }
+    }
 }
 
 static void drawDashedHorizLine(struct hvGfx *hvg, int x1, int x2,
 		     int y, int dashLen, int gapLen, Color lineColor)
 // ie.    - - - - - - - - - - - - - - - -
 {
 int cx1 = x1;
 int cx2;
 while (1)
     {
     cx2 = cx1 + dashLen;
     if (cx2 > x2)
         cx2 = x2;
     hvGfxLine(hvg, cx1, y, cx2, y, lineColor);
     cx1 += (dashLen + gapLen);
@@ -525,31 +554,32 @@
 }
 
 static void drawBoxWChevrons(struct hvGfx *hvg, int x1, int y1,
 		  int width, int height, Color fillColor, Color outlineColor,
 		  char strand)
 {
 drawNormalBox(hvg, x1, y1, width, height, fillColor, outlineColor);
 static Color white = 0xffffffff;
 int dir = (strand == '+' ? 1 : -1);
 int midY = y1 + ((height) >> 1);
 clippedBarbs(hvg, x1 + 1, midY, width, ((height >> 1) - 2), 5,
 	      dir, white, TRUE);
 }
 
 static void drawRMGlyph(struct hvGfx *hvg, int y, int heightPer,
-	     int width, int baseWidth, int xOff, struct rmskJoined *rm)
+	    int width, int baseWidth, int xOff, struct rmskJoined *rm,
+            enum trackVisibility vis)
 /*
  *  Draw a detailed RepeatMasker annotation glyph given
  *  a single rmskJoined structure.
  *
  *  A couple of caveats about the use of the rmskJoined
  *  structure to hold a RepeatMasker annotation.
  *
  *  chromStart/chromEnd:  These represent genomic coordinates
  *                        that mark the extents of graphics
  *                        annotation on the genome.  I.e
  *                        aligned blocks + unaligned consensus
  *                        blocks.  The code below may not
  *                        draw to these extents in some cases.
  *
  *  score:  This represents the average divergence of the
@@ -759,38 +789,43 @@
 	    else
 		{
 		lx1 = roundingScale(rm->chromStart - winStart,
 				     width, baseWidth) + xOff;
 		// Line Across
 		drawDashedHorizLine(hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		}
 	    // Line down
 	    hvGfxLine(hvg, lx2, y + alignedBlockOffset, lx2,
 		       y + unalignedBlockOffset, black);
 	    hvGfxLine(hvg, lx1, y + unalignedBlockOffset - 3, lx1,
 		       y + unalignedBlockOffset + 3, black);
 
 	    // Draw labels
+	    if ( vis != tvSquish && vis != tvFull )
+                 {
 	         MgFont *font = tl.font;
 	         int fontHeight = tl.fontHeight;
 	         int stringWidth =
 	     	     mgFontStringWidth(font, rm->name) + LABEL_PADDING;
 	         hvGfxTextCentered(hvg, lx1 - stringWidth,
 			       heightPer - fontHeight + y,
 			       stringWidth, fontHeight, MG_BLACK, font,
 			       rm->name);
+                 }
+
+
 
 
 	    }
 	else if (idx == (rm->blockCount - 1))
 	    {
 	    /*
 	     * Unaligned sequence at the end of an annotation
 	     * Draw as:
 	     *       -------------|   or        ------//------|
 	     *       |                          |
 	     *  >>>>>                    >>>>>>>
 	     */
 	    lx1 = roundingScale(rm->chromStart +
 				 rm->blockRelStarts[idx - 1] +
 				 rm->blockSizes[idx - 1] - winStart,
@@ -955,55 +990,53 @@
 		hvGfxLine(hvg, cx1, y + alignedBlockOffset, lx1,
 			   y + unalignedBlockOffset, black);
 		drawDashedHorizLine(hvg, lx1, lx2,
 				     y + unalignedBlockOffset, 5, 5, black);
 		// Line Down
 		cx2 = roundingScale(rm->chromStart +
 				     rm->blockRelStarts[idx +
 							1] -
 				     winStart, width, baseWidth) + xOff;
 		hvGfxLine(hvg, cx2, y + alignedBlockOffset, lx2,
 			   y + unalignedBlockOffset, black);
 		}
 	    }
 	}
     }
-
 }
 
 static void origRepeatDraw(struct track *tg, int seqStart, int seqEnd,
 		struct hvGfx *hvg, int xOff, int yOff, int width,
 		MgFont * font, Color color, enum trackVisibility vis)
 /* This is the original repeat drawing routine, modified
  * to handle the new rmskJoined table structure.
  */
 {
 int baseWidth = seqEnd - seqStart;
 struct repeatItem *ri;
 int y = yOff;
 int heightPer = tg->heightPer;
 int lineHeight = tg->lineHeight;
 int x1, x2, w;
-boolean isFull = (vis == tvFull);
 Color col;
 struct sqlConnection *conn = hAllocConn(database);
 struct sqlResult *sr = NULL;
 char **row;
 int rowOffset;
 
 
-if (isFull)
+if (vis == tvFull || vis == tvSquish || vis == tvPack)
     {
     /*
      * Do grayscale representation spread out among tracks.
      */
     struct hash *hash = newHash(6);
     struct rmskJoined *ro;
     int percId;
     int grayLevel;
 
     for (ri = tg->items; ri != NULL; ri = ri->next)
 	{
 	ri->yOffset = y;
 	y += lineHeight;
 	hashAdd(hash, ri->class, ri);
 	}
@@ -1135,82 +1168,84 @@
 	     struct hvGfx *hvg, int xOff, int yOff, int width,
 	     MgFont * font, Color color, enum trackVisibility vis)
 {
 int baseWidth = seqEnd - seqStart;
 
 // TODO: Document
 pixelsPerBase = (float) width / (float) baseWidth;
   /*
    * Its unclear to me why heightPer is not updated to the
    * value set in rmskJoinedItemHeight() at the time this callback
    * is invoked.  Getting the correct value myself.
    * was:
    * int heightPer = tg->heightPer;
    */
 int heightPer = rmskJoinedItemHeight(tg, NULL);
-boolean isFull = (vis == tvFull);
+//boolean isFull = (vis == tvFull);
 struct rmskJoined *rm;
+// DEBUG REMOVE
 
   // If we are in full view mode and the scale is sufficient,
   // display the new visualization.
-if (isFull && baseWidth <= DETAIL_VIEW_MAX_SCALE)
+if ((vis == tvFull || vis == tvSquish || vis == tvPack) && baseWidth <= DETAIL_VIEW_MAX_SCALE)
     {
     int level = yOff;
-
     struct subTrack *st = hashFindVal(subTracksHash, tg->table);
     if (!st)
 	return;
 
     int lidx = st->levelCount;
     int currLevel = 0;
     for (currLevel = 0; currLevel < lidx; currLevel++)
 	{
 	rm = st->levels[currLevel];
 	while (rm)
 	    {
-	    drawRMGlyph(hvg, level, heightPer, width, baseWidth, xOff, rm);
+	    drawRMGlyph(hvg, level, heightPer, width, baseWidth, xOff, rm, vis );
 
 	    char statusLine[128];
 	    int ss1 = roundingScale(rm->alignStart - winStart,
 				     width, baseWidth) + xOff;
 
 	    safef(statusLine, sizeof(statusLine), "%s", rm->name);
 
 	    int x1 = roundingScale(rm->alignStart - winStart, width,
 				    baseWidth) + xOff;
 	    x1 = max(x1, 0);
 	    int x2 = roundingScale(rm->alignEnd - winStart, width,
 				    baseWidth) + xOff;
 	    int w = x2 - x1;
 	    if (w <= 0)
 	        w = 1;
 
 	    mapBoxHc(hvg, rm->alignStart, rm->alignEnd, ss1, level,
    	             w, heightPer, tg->track, rm->id, statusLine);
 	    rm = rm->next;
 	    }
 	level += heightPer;
 	rmskJoinedFreeList(&(st->levels[currLevel]));
         }
+
     }
 else
     {
     // Draw the stereotypical view
     origRepeatDraw(tg, seqStart, seqEnd,
 		    hvg, xOff, yOff, width, font, color, vis);
     }
 }
 
 void rmskJoinedMethods(struct track *tg)
 {
 tg->loadItems = rmskJoinedLoadItems;
 tg->freeItems = rmskJoinedFreeItems;
 tg->drawItems = rmskJoinedDrawItems;
 tg->colorShades = shadesOfGray;
 tg->itemName = rmskJoinedName;
 tg->mapItemName = rmskJoinedName;
 tg->totalHeight = rmskJoinedTotalHeight;
 tg->itemHeight = rmskJoinedItemHeight;
 tg->itemStart = tgItemNoStart;
 tg->itemEnd = tgItemNoEnd;
 tg->mapsSelf = TRUE;
+tg->canPack = TRUE;
 }