4586cbe789d7ff340473682da13625025263131d
larrym
  Mon Nov 15 10:52:07 2010 -0800
support codon numbering in genePred tracks
diff --git src/hg/hgTracks/cds.c src/hg/hgTracks/cds.c
index 11d4fc2..05b6088 100644
--- src/hg/hgTracks/cds.c
+++ src/hg/hgTracks/cds.c
@@ -46,58 +46,66 @@
 /* Array of colors used in drawing codons/bases/differences: */
 Color cdsColor[CDS_NUM_COLORS];
 boolean cdsColorsMade = FALSE;
 
 Color getCdsColor(int index)
 {
 assert(index < CDS_NUM_COLORS);
 return cdsColor[index];
 }
 
 static void drawScaledBoxSampleWithText(struct hvGfx *hvg, 
                                         int chromStart, int chromEnd,
                                         double scale, int xOff, int y,
                                         int height, Color color, int score,
                                         MgFont *font, char *text, bool zoomed,
-                                        int winStart, int maxPixels, boolean isCoding)
+                                        int winStart, int maxPixels, boolean isCoding, boolean justifyString)
 /* Draw a box scaled from chromosome to window coordinates with
    a codon or set of 3 or less bases drawn in the box. */
 {
 
 /*first draw the box itself*/
 drawScaledBoxSample(hvg, chromStart, chromEnd, scale, xOff, y, height, 
 		    color, score);
 
 /*draw text in box if space, and align properly for codons or DNA*/
 if (zoomed)
     {
     int i;
     int x1, x2, w;
     x1 = round((double)(chromStart-winStart)*scale) + xOff;
     x2 = round((double)(chromEnd-winStart)*scale) + xOff;
     if (x2 >= maxPixels)
         x2 = maxPixels - 1;
     w = x2-x1;
     if (w < 1)
         w = 1;
 
     if (chromEnd - chromStart == 3 && isCoding)
-        spreadBasesString(hvg,x1,y,w,height,whiteIndex(),
-		     font,text,strlen(text), TRUE);
+        {
+        if(justifyString)
+            spreadBasesString(hvg, x1, y, w, height, whiteIndex(),  font, text, strlen(text),  TRUE);
+        else
+            hvGfxTextCentered(hvg, x1, y, w, height, whiteIndex(), font, text);
+        }
     else if (chromEnd - chromStart < 3 && isCoding)
-        spreadBasesString(hvg,x1,y,w,height,cdsColor[CDS_PARTIAL_CODON],font,
-		     text,strlen(text), TRUE);
+        {
+        if(justifyString)
+            spreadBasesString(hvg, x1, y, w, height, cdsColor[CDS_PARTIAL_CODON], font, text, strlen(text), TRUE);
+        else
+            hvGfxTextCentered(hvg, x1, y, w, height, cdsColor[CDS_PARTIAL_CODON], font, text);
+        }
     else
         {
         int thisX,thisX2;
         char c[2];
 	c[1] = '\0';
 	int iMin = max(0, (winStart-chromStart));
 	int iMax = min((chromEnd-chromStart), (winEnd-chromStart));
         for (i=iMin; i<iMax; i++)
             {
 	    if (text[i] == ' ')
 		continue;
             c[0] = text[i];
             thisX = round((double)(chromStart+i-winStart)*scale) + xOff;
             thisX2 = round((double)(chromStart+1+i-winStart)*scale) + xOff;
             hvGfxTextCentered(hvg, thisX, y, thisX2-thisX, height,
@@ -644,31 +652,31 @@
     /* Previous code didn't use exon frames for baseColorDrawGenomicCodons.
      * This meant simply counting off aligned bases to define frames.  It
      * didn't work very well for TransMap alignments and not clear that its
      * the right thing to do for any alignment.  By using exonFrames for
      * genomic codons, this is letting the query sequence define the frame.
      */
     struct genbankCds cds;
     getPslCds(psl, tg, &cds);
     int insertMergeSize = -1;
     unsigned opts = genePredCdsStatFld|genePredExonFramesFld;
     struct genePred *gp = genePredFromPsl2(psl, opts, &cds, insertMergeSize);
     lf->start = gp->txStart;
     lf->end = gp->txEnd;
     lf->tallStart = gp->cdsStart;
     lf->tallEnd = gp->cdsEnd;
-    sfList = baseColorCodonsFromGenePred(lf, gp, colorStopStart);
+    sfList = baseColorCodonsFromGenePred(lf, gp, colorStopStart, FALSE);
     genePredFree(&gp);
     }
 return(sfList);
 }
 
 
 
 struct simpleFeature *baseColorCodonsFromPsl(struct linkedFeatures *lf, 
         struct psl *psl, int sizeMul, boolean isXeno, int maxShade,
         enum baseColorDrawOpt drawOpt, struct track *tg)
 /* Given an lf and the psl from which the lf was constructed, 
  * return a list of simpleFeature elements, one per codon (or partial 
  * codon if the codon falls on a gap boundary.  sizeMul, isXeno and maxShade
  * are for defaulting to one-simpleFeature-per-exon if cds is not found. */
 {
@@ -999,31 +1007,31 @@
         {
         sf->start = winEnd - sf->start + winStart - 3;
         sf->end = sf->start + 3;
         }
     // Base offsets mod 6 for alternating colors: 0,1,2 --> first codon, 3,4,5 --> second codon.
     bool codonFirstColor = (sf->start % 6 < 3);
     sf->grayIx = codonToGrayIx(codon, codonFirstColor, NULL, FALSE, TRUE);
     zeroBytes(codon, 4);
     slAddHead(&sfList, sf);
     }
 slReverse(&sfList);
 return sfList;
 }
 
 struct simpleFeature *baseColorCodonsFromGenePred(struct linkedFeatures *lf, 
-	struct genePred *gp, boolean colorStopStart)
+        struct genePred *gp, boolean colorStopStart, boolean codonNumbering)
 /* Given an lf and the genePred from which the lf was constructed, 
  * return a list of simpleFeature elements, one per codon (or partial 
  * codon if the codon falls on a gap boundary. */
 {
 unsigned *starts = gp->exonStarts;
 unsigned *ends = gp->exonEnds;
 int blockCount = gp->exonCount;
 unsigned cdsStart = gp->cdsStart;
 unsigned cdsEnd = gp->cdsEnd;
 int *exonFrames = gp->exonFrames;
 boolean useExonFrames = (gp->optFields >= genePredExonFramesFld);
     int frame = 0;
     int currentStart = 0, currentEnd = 0;
     char partialCodonSeq[4];
     int currentSize;
@@ -1037,30 +1045,33 @@
     partialCodonSeq[0] = '\0';
 
     if (lf->orientation > 0) //positive strand
     {
         i0 = 0; iN = blockCount; iInc = 1;
         posStrand = TRUE;
     }
     else
     {
         i0 = blockCount-1; iN=-1; iInc = -1;
         posStrand = FALSE;
     }
 
     bool altColor = FALSE;
     unsigned cds5Prime = posStrand ? cdsStart : cdsEnd;
+    int width = winEnd - winStart;
+    // width cutoff really should be based on (a) how many codons this gene has, (2) the current font and (3) image width.
+    int codonIndex = !codonNumbering || width > 60 ? 0 : 1;
     for (i=i0; (iInc*i)<(iInc*iN); i=i+iInc)
 	{
         int exonStart = starts[i];
         int exonEnd = ends[i];
         if (useExonFrames)
 	    {
             if(exonFrames[i] > 0)
                 frame = 3 - exonFrames[i];
             else
                 frame = 0;
 	    }
 
         if(frame == 0)
             strcpy(partialCodonSeq,"");
 
@@ -1163,30 +1174,31 @@
 			safef(tempCodonSeq, sizeof(tempCodonSeq), "%s%s", partialCodonSeq, 
 			      theRestOfCodon);
 		    else
 			safef(tempCodonSeq, sizeof(tempCodonSeq), "%s%s", theRestOfCodon, 
 			      partialCodonSeq );
 		    tempCodonSeq[4] = '\0';  // no more than 3 bases
 
 		    AllocVar(sf);
 		    sf->start = currentStart;
 		    sf->end = currentEnd;
 		    sf->grayIx = ((posStrand && currentEnd <= cdsEnd) || 
 				  (!posStrand && currentStart >= cdsStart)) ?
 			codonToGrayIx(tempCodonSeq, altColor, &foundStart, 
 				      !posStrand, colorStopStart) :
 			GRAYIX_CDS_ERROR;
+                    sf->codonIndex = codonIndex;
 		    slAddHead(&sfList, sf);
 		    }
                 break;
 		} // end if we've gone off the end of the current exon
 
             currentSize = currentEnd - currentStart;
             /*inside a coding block (with 3 bases)*/
             if (currentSize == 3)
 		{
 		AllocVar(sf);
 		sf->start = currentStart;
 		sf->end = currentEnd;
 		if ((posStrand && currentEnd <= cdsEnd) ||
 		    (!posStrand && currentStart >= cdsStart))
 		    {
@@ -1209,30 +1221,32 @@
 		sf->end = currentEnd;
                 if (strlen(partialCodonSeq) == 3) 
                     sf->grayIx = codonToGrayIx(partialCodonSeq, altColor,
                             &foundStart, !posStrand, colorStopStart);
                 else
                     sf->grayIx = GRAYIX_CDS_ERROR;
                 strcpy(partialCodonSeq,"" );
 
                 /*update frame based on bases appended*/
                 frame -= currentSize;
 		}
             else
                 errAbort("%s: Too much dna (%d - %d = %d)<br>\n", lf->name, 
 			 currentEnd, currentStart, currentSize);
 
+            if(codonIndex)
+                sf->codonIndex = codonIndex++;
             slAddHead(&sfList, sf);
             if(posStrand)
                 currentStart = currentEnd;
             else
                 currentEnd = currentStart;
 	    } // end loop on codons within exon
 
 	/* coding + UTR exon */
 	if (posStrand && (exonEnd < ends[i]) &&
 	    exonEnd < winEnd && ends[i] > winStart)
 	    {
             AllocVar(sf);
             sf->start = exonEnd;
             sf->end = ends[i];
             slAddHead(&sfList, sf);
@@ -1344,88 +1358,88 @@
     mrnaBases[0] = '\0';
     if (psl && isCoding)
 	getMrnaBases(psl, mrnaSeq, mrnaS, s, e, (lf->orientation == -1),
 		    mrnaBases, &queryInsertion);
     if (queryInsertion && isCoding)
 	color = cdsColor[CDS_QUERY_INSERTION];
 
     dyStringAppendN(dyMrnaSeq, (char*)&mrnaSeq->dna[mrnaS], e-s);
 
     if (drawOpt == baseColorDrawItemBases)
 	{
 	if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE))
 	    complement(dyMrnaSeq->string, dyMrnaSeq->stringSize);
 	drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, 
 				    color, lf->score, font, dyMrnaSeq->string,
-				    zoomedToBaseLevel, winStart, maxPixels, isCoding);
+				    zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE);
 	}
     else if (drawOpt == baseColorDrawItemCodons)
 	{
 	if (e <= lf->tallEnd)
 	    {
 	    boolean startColor = FALSE;
 	    /* re-set color of this block based on mrna codons rather than
 	     * genomic, but keep the odd/even cycle of dark/light shades. */
 	    int mrnaGrayIx = codonToGrayIx(mrnaBases, (grayIx > 26), NULL,
 					   FALSE, TRUE);
 	    if (color == cdsColor[CDS_START])
                 startColor = TRUE;
 	    color = colorAndCodonFromGrayIx(hvg, mrnaCodon, mrnaGrayIx,
 					    ixColor);
 	    if (startColor && sameString(mrnaCodon,"M"))
                 color = cdsColor[CDS_START];
 	    drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, 
 					color, lf->score, font, mrnaCodon,
 					zoomedToCodonLevel, winStart,
-					maxPixels, isCoding);
+					maxPixels, isCoding, TRUE);
 	    }
 	else
 	    drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	}
     else if (drawOpt == baseColorDrawDiffBases)
 	{
 	char *diffStr = NULL;
 	char *genoDna = getCachedDna(s, e);
 	diffStr = needMem(sizeof(char) * (e - s + 1));
 	maskDiffString(diffStr, dyMrnaSeq->string, genoDna, ' ');
 	if (cartUsualBooleanDb(cart, database, COMPLEMENT_BASES_VAR, FALSE))
 	    complement(diffStr, strlen(diffStr));
 	drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, 
 				    color, lf->score, font, diffStr, 
-				    zoomedToBaseLevel, winStart, maxPixels, isCoding);
+				    zoomedToBaseLevel, winStart, maxPixels, isCoding, TRUE);
 	freeMem(diffStr);
 	}
     else if (drawOpt == baseColorDrawDiffCodons)
 	{
 	if (e <= lf->tallEnd)
 	    {
 	    /* Color codons red wherever mrna differs from genomic;
 	     * keep the odd/even cycle of dark/light shades. */
 	    colorAndCodonFromGrayIx(hvg, genomicCodon, grayIx, ixColor);
 	    int mrnaGrayIx = mrnaCodonToGrayIx(mrnaBases, genomicCodon[0],
 					       (grayIx > 26));
 	    color = colorAndCodonFromGrayIx(hvg, mrnaCodon, mrnaGrayIx,
 					    ixColor);
 	    // Look up mrnaCodon again because if mrnaGrayIx is GRAYIX_SYN_PROT,
 	    // codon value is lost:
 	    safef(mrnaCodon, sizeof(mrnaCodon), "%c", baseColorLookupCodon(mrnaBases));
 	    if (mrnaCodon[0] != genomicCodon[0])
 		{
 		drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, 
 					    heightPer, color, lf->score, font,
 					    mrnaCodon, zoomedToCodonLevel,
-					    winStart, maxPixels, isCoding);
+					    winStart, maxPixels, isCoding, TRUE);
 		}
 	    else
 		drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	    }
 	else
 	    drawScaledBox(hvg, s, e, scale, xOff, y, heightPer, color);
 	}
     else if (drawOpt != baseColorDrawCds)
         errAbort("Unknown drawOpt: %d<br>\n", drawOpt);
 
     dyStringFree(&dyMrnaSeq);
     }
 else
     {
     /*show we have an error by coloring entire exon block yellow*/
@@ -1433,44 +1447,46 @@
     // FIXME: this shouldn't ever happen, should be an errAbort
     warn("Bug: drawDiffTextBox: convertCoordUsingPsl failed<br>\n");
     }
 }
 
 void baseColorDrawItem(struct track *tg,  struct linkedFeatures *lf, 
 		       int grayIx, struct hvGfx *hvg, int xOff, 
                        int y, double scale, MgFont *font, int s, int e, 
                        int heightPer, boolean zoomedToCodonLevel, 
                        struct dnaSeq *mrnaSeq, struct simpleFeature *sf, struct psl *psl, 
 		       enum baseColorDrawOpt drawOpt,
                        int maxPixels, int winStart, 
                        Color originalColor)
 /* Draw codon/base-colored item. */
 {
-char codon[2] = " ";
+char codon[64] = " ";
 Color color = colorAndCodonFromGrayIx(hvg, codon, grayIx, originalColor);
+if(sf->codonIndex)
+    safef(codon, sizeof(codon), "%c %d", codon[0], sf->codonIndex);
 /* When we are zoomed out far enough so that multiple bases/codons share the 
  * same pixel, we have to draw differences in a separate pass (baseColorOverdrawDiff)
  * so don't waste time drawing the differences here: */
 boolean zoomedOutToPostProcessing =
     ((drawOpt == baseColorDrawDiffBases && !zoomedToBaseLevel) ||
      (drawOpt == baseColorDrawDiffCodons && !zoomedToCdsColorLevel));
 
 if (drawOpt == baseColorDrawGenomicCodons && (e-s <= 3))
     {
     drawScaledBoxSampleWithText(hvg, s, e, scale, xOff, y, heightPer, 
                                 color, lf->score, font, codon, 
-                                zoomedToCodonLevel, winStart, maxPixels, TRUE);
+                                zoomedToCodonLevel, winStart, maxPixels, TRUE, !sf->codonIndex);
     }
 else if (mrnaSeq != NULL && (psl != NULL || sf != NULL) && !zoomedOutToPostProcessing &&
 	 drawOpt != baseColorDrawGenomicCodons && drawOpt != baseColorDrawOff)
     {
     drawDiffTextBox(hvg, xOff, y, scale, heightPer, font, 
 		    color, chromName, s, e, sf, psl, mrnaSeq, lf,
 		    grayIx, drawOpt, maxPixels,
 		    tg->colorShades, originalColor);
     }
 else
     {
     /* revert to normal coloring */
     drawScaledBoxSample(hvg, s, e, scale, xOff, y, heightPer, 
 			color, lf->score );
     }
@@ -1806,31 +1822,31 @@
 struct simpleFeature *sf;
 
 if (!cdsColorsMade)
     {
     makeCdsShades(hvg, cdsColor);
     cdsColorsMade = TRUE;
     }
 
 for (sf = sfList; sf != NULL; sf = sf->next)
     {
     char codon[4];
     Color color = colorAndCodonFromGrayIx(hvg, codon, sf->grayIx, MG_GRAY);
     if (zoomedToText)
         drawScaledBoxSampleWithText(hvg, sf->start, sf->end, scale, insideX, y,
 				    height, color, 1.0, font, codon, TRUE,
-				    winStart, maxPixels, TRUE);
+				    winStart, maxPixels, TRUE, TRUE);
     else
         /* zoomed in just enough to see colored boxes */
         drawScaledBox(hvg, sf->start, sf->end, scale, xOff, y, height, color);
     }
 }
 
 
 void baseColorDrawCleanup(struct linkedFeatures *lf, struct dnaSeq **pMrnaSeq,
 			  struct psl **pPsl)
 /* Free structures allocated just for base/cds coloring. */
 {
 // We could free lf->original here (either genePredFree or pslFree, depending
 // on the type -- but save time by skipping that.  Maybe we should save time
 // by skipping this free too:
 if (pMrnaSeq != NULL)