src/hg/instinct/hgBamBam/drawingCode.c 1.4

1.4 2010/05/31 00:02:34 jsanborn
big update
Index: src/hg/instinct/hgBamBam/drawingCode.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/hgBamBam/drawingCode.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -b -B -U 4 -r1.3 -r1.4
--- src/hg/instinct/hgBamBam/drawingCode.c	26 May 2010 08:02:55 -0000	1.3
+++ src/hg/instinct/hgBamBam/drawingCode.c	31 May 2010 00:02:34 -0000	1.4
@@ -25,9 +25,10 @@
 #include "spaceSaver.h"
 
 #define MIN_LABEL_SIZE 100 // minimum size of labels 
 #define TEXT_BUFFER 5 // pixels to put between text and heatmap
-#define FOOT_HEIGHT 3
+#define FOOT_HEIGHT 5
+#define INTER_BREAK_HEIGHT 5
 
 struct trackLayout tl;           /* Dimensions of things, fonts, etc. */
 
 /***** BEGIN HELPER *****/
@@ -115,30 +116,299 @@
 return str;
 }
 
 void drawBackgroundLines(struct vGfx *vg, int startX, int startY, int width, int height)
-{
-int x, spacing = 20;
+{ // draw background lines like genome browser.
+int x, spacing = 12;
+
+/* First line is red */
+Color lightRed = vgFindColorIx(vg, 220, 0, 0);
+x = startX;
+vgBox(vg, x, startY, 1, height, lightRed);
+x += spacing;
 
+/* Remaining are blue */
 Color lightBlue = vgFindColorIx(vg, 220, 220, 255);
 vgSetClip(vg, startX, startY, width, height);
-for (x = startX; x < (startX + width); x += spacing)
+for ( ; x < (startX + width); x += spacing)
     vgBox(vg, x, startY, 1, height, lightBlue);
 vgUnclip(vg);
 }
 
-static int calcX(struct chromLay *cl, int base, int max)
+void prepareImage(struct vGfx *vg, struct settings *settings)
+{
+drawBackgroundLines(vg, settings->dataOffsetX - 1, 0, settings->dataWidth + 1, settings->height);
+
+if (settings->title)
+    vgTextCentered(vg, settings->dataOffsetX, 0, settings->dataWidth, TITLE_HEIGHT,
+		   MG_BLACK, settings->font, settings->title);
+}
+
+static int calcX(struct chromLay *cl, int base, int min, int max)
 {
 if (!cl)
     return -1;
 int x = round(cl->pxStart+cl->pxWidth*((float)(base - cl->baseStart)/cl->baseWidth));
-return (x > max) ? max : x;
+
+if (x < min)
+    x = min;
+if (x > max)
+    x = max;
+return x;
 }
 
 
 /***** END HELPER *****/
 
-void drawChromBreaks(struct vGfx *vg, struct spaceSaver *ss, 
+struct chromLay *getChromLayBreaks(struct sqlConnection *conn, 
+				   struct breaks *breaks, struct settings *settings)
+{
+if (!breaks || !settings || slCount(settings->layoutList) > 1)
+    return NULL;
+
+char *currentChrom = settings->layoutList->chrom;
+
+/* Find all chroms that have inter chrom breakpoints */
+struct hash *chromHash = hashNew(0);
+struct breaks *br;
+for (br = breaks; br; br = br->next)
+    {
+
+    struct hashEl *el = hashLookup(chromHash, br->chrom);
+    if (!el)
+	hashAddInt(chromHash, br->chrom, 1);
+
+    el = hashLookup(chromHash, br->chrom2);
+    if (!el)
+	hashAddInt(chromHash, br->chrom2, 1);
+    }
+
+struct genoLayChrom *gl, *glList = genoLayDbChroms(conn, FALSE);
+
+float totalBases = 0.0;
+for (gl = glList; gl; gl = gl->next)
+    {
+    if (sameString(gl->fullName, currentChrom))
+	continue;
+
+    struct hashEl *el = hashLookup(chromHash, gl->fullName);
+    if (el)
+	totalBases += gl->size;
+    }
+
+float cbases = 0.0;
+struct chromLay *cl, *clList = NULL;
+for (gl = glList; gl; gl = gl->next)
+    {
+    if (sameString(gl->fullName, currentChrom))
+	continue;
+
+    struct hashEl *el = hashLookup(chromHash, gl->fullName);
+    if (el)
+	{
+	AllocVar(cl);
+	cl->chrom     = cloneString(gl->fullName);
+	cl->baseStart = 0.0;
+	cl->baseEnd   = (float) gl->size;
+	cl->baseWidth = cl->baseEnd - cl->baseStart;
+	cl->pxWidth   = (float) settings->dataWidth * cl->baseWidth / totalBases;
+	cl->pxStart   = (float) settings->dataWidth * cbases / totalBases + settings->dataOffsetX;
+	cbases += (float) gl->size;
+	slAddHead(&clList, cl);
+	}
+    }
+slReverse(&clList);
+return clList;
+}
+
+int breaksCmp(const void *va, const void *vb)
+/* Compare to sort columns based on priority. */
+{
+const struct breaks *a = *((struct breaks **)va);
+const struct breaks *b = *((struct breaks **)vb);
+
+float dif = 0;
+if (sameString(a->chrom, b->chrom))
+    dif = a->chromStart - b->chromStart;
+else if (sameString(a->chrom, b->chrom2))
+    dif = a->chromStart - b->chromStart2;
+else if (sameString(a->chrom2, b->chrom2))
+    dif = a->chromStart2 - b->chromStart2;
+
+if (dif < 0)
+    return 1;
+else if (dif > 0)
+    return -1;
+else
+    return 0;
+}
+
+
+void drawInterChromBreaks(struct hvGfx *hvg, struct breaks *breaks, 
+			  struct chromLay *clList, struct settings *settings)
+{
+if (!settings || slCount(settings->layoutList) > 1)
+    return;
+
+char *currentChrom = settings->layoutList->chrom;
+
+struct vGfx *vg = hvg->vg;
+
+struct breaks *br;
+struct chromLay *cl;
+struct hash *clHash = hashNew(0);
+for (cl = clList; cl; cl = cl->next)
+    {
+    if (sameString(cl->chrom, currentChrom))
+	continue;
+    hashAdd(clHash, cl->chrom, cl);
+    }
+
+int xl1, xl2, wl, xr1, xr2, wr, ldir, rdir;
+char *lstrand, *rstrand;
+struct chromLay *clL, *clR;
+struct hashEl *el;
+
+drawColorIdeogram(hvg, settings, clList, settings->dataOffsetY + DEFAULT_LABEL_HEIGHT);
+
+int yfoot = FOOT_HEIGHT; 
+int yl = settings->height - settings->dataOffsetY - yfoot;
+int yr = yfoot + CYTO_HEIGHT + DEFAULT_LABEL_HEIGHT;
+
+vgSetClip(vg, settings->dataOffsetX, 0, settings->dataWidth, settings->height);
+
+int ibreak = 0;
+
+slSort(&breaks, breaksCmp);
+
+int yOff;
+for (br = breaks; br; br = br->next)
+    {
+    yOff = settings->dataOffsetY;
+    clL = NULL;
+    clR = NULL;
+
+    int yBreak = INTER_BREAK_HEIGHT * ibreak + CYTO_HEIGHT + DEFAULT_LABEL_HEIGHT + yfoot + INTER_BREAK_HEIGHT/2;
+    ibreak++;
+
+    int csL, ceL;
+    int csR, ceR;
+    if (sameString(currentChrom, br->chrom))
+	{
+	el = hashLookup(settings->layoutHash, br->chrom);
+	if (el)
+	    clL = el->val;
+	csL = br->chromStart;
+	ceL = br->chromEnd;
+	lstrand = br->strand;
+
+	el = hashLookup(clHash, br->chrom2);
+	if (el)
+	    clR = el->val;
+	csR = br->chromStart2;
+	ceR = br->chromEnd2;
+	rstrand = br->strand2;
+	}
+    else
+	{
+	el = hashLookup(settings->layoutHash, br->chrom2);
+	if (el)
+	    clL = el->val;
+	csL = br->chromStart2;
+	ceL = br->chromEnd2;
+	lstrand = br->strand2;
+	
+	el = hashLookup(clHash, br->chrom);
+	if (el)
+	    clR = el->val;
+	csR = br->chromStart;
+	ceR = br->chromEnd;
+	rstrand = br->strand;
+	}
+    
+    /* +/- FOOT_HEIGHT to push far away breakpoint outside of clipped regions, so
+     * they are not displayed */
+ 
+    xl1 = calcX(clL, csL, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    xl2 = calcX(clL, ceL, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    wl = (xl2 - xl1 > 0) ? (xl2 - xl1) : 1;
+
+    xr1 = calcX(clR, csR, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    xr2 = calcX(clR, ceR, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    wr = (xr2 - xr1 > 0) ? (xr2 - xr1) : 1;
+
+    ldir = 1;
+    if (sameString(lstrand, "+"))
+	ldir = -1;
+
+    rdir = 1;  // reversed
+    if (sameString(rstrand, "+"))
+	rdir = -1;
+
+    Color col = getChromColor(vg, clR->chrom);
+    //vgLine(vg, xl2, yOff + yl, xr1, yOff + yr, col);
+    vgLine(vg, xl2, yOff + yBreak, xr1, yOff + yBreak, col);
+
+    if (xl1 >= 0 && xl2 >= 0)
+	{
+	vgBox(vg, xl1, yOff + yl, wl, 1, MG_BLACK);
+	vgLine(vg, xl1, yOff + yl, xl1 + ldir * FOOT_HEIGHT, yOff + yl + yfoot, MG_BLACK);  
+	vgLine(vg, xl1, yOff + yl, xl1, yOff + yBreak, col);
+	}
+    if (xr1 >= 0 && xr2 >= 0) 
+	{
+	vgBox(vg, xr1, yOff + yr, wr, 1, MG_BLACK);
+	vgLine(vg, xr1, yOff + yr, xr1 + rdir * FOOT_HEIGHT, yOff + yr - yfoot, MG_BLACK);  
+	vgLine(vg, xr1, yOff + yr, xr1, yOff + yBreak, col);
+	}
+    }
+vgUnclip(vg);
+
+}
+
+char *interBreaksGif(struct sqlConnection *conn, struct breaks *breaks, 
+		     struct settings *settings)
+/* Create genome GIF file and HT that includes it. */
+{
+if (!breaks || !settings)
+    return NULL;
+
+if (settings->height <= 0 || settings->width <= 0)
+    return NULL;
+
+struct chromLay *clList = getChromLayBreaks(conn, breaks, settings);
+
+if (!clList)
+    return NULL;
+
+int numBreaks = slCount(breaks);    
+
+settings->dataHeight = (numBreaks + 1) * INTER_BREAK_HEIGHT + CYTO_HEIGHT + DEFAULT_LABEL_HEIGHT + FOOT_HEIGHT * 2;
+settings->height     = settings->dataHeight + TITLE_HEIGHT;
+
+struct hvGfx *vg;
+struct tempName md5Tn;
+char *strToHash = cartSettingsString(bbPrefix, "interBreaks");
+trashDirMD5File(&md5Tn, "hgg", ".gif", strToHash);
+
+off_t size = fileSize(md5Tn.forCgi);
+if (!fileExists(md5Tn.forCgi) || (size == 0) || DEBUG)
+    {
+    vg = hvGfxOpenGif(settings->width, settings->height, md5Tn.forCgi, FALSE);
+
+    prepareImage(vg->vg, settings);
+
+    drawInterChromBreaks(vg, breaks, clList, settings);
+
+    hvGfxClose(&vg);
+    }
+
+char *filename = replaceChars(md5Tn.forHtml, "..", "");
+return filename;
+}
+
+
+void drawIntraChromBreaks(struct vGfx *vg, struct spaceSaver *ss, 
 		     struct settings *settings)
 {
 struct breaks *br;
 
@@ -151,13 +421,14 @@
 int hline = 1;
 int yline = FOOT_HEIGHT; 
 
 struct spaceNode *sn;
+vgSetClip(vg, settings->dataOffsetX, 0, settings->dataWidth, settings->height);
 
 int yOff;
 for (sn = ss->nodeList; sn; sn = sn->next)
     {
-    yOff = sn->row * FOOT_HEIGHT * 2;
+    yOff = sn->row * FOOT_HEIGHT * 2 + settings->dataOffsetY;
     br   = (struct breaks *)sn->val;
 
     clL = NULL;
     clR = NULL;
@@ -168,15 +439,18 @@
     el = hashLookup(settings->layoutHash, br->chrom2);
     if (el)
 	clR = el->val;
     
-    xl1 = calcX(clL, br->chromStart, settings->width);
-    xl2 = calcX(clL, br->chromEnd, settings->width);
+    /* +/- FOOT_HEIGHT to push far away breakpoint outside of clipped regions, so
+     * they are not displayed */
+ 
+    xl1 = calcX(clL, br->chromStart, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    xl2 = calcX(clL, br->chromEnd, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
     wl = (xl2 - xl1 > 0) ? (xl2 - xl1) : 1;
     lstrand = br->strand;
 
-    xr1 = calcX(clR, br->chromStart2, settings->width);
-    xr2 = calcX(clR, br->chromEnd2, settings->width);
+    xr1 = calcX(clR, br->chromStart2, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
+    xr2 = calcX(clR, br->chromEnd2, settings->dataOffsetX-FOOT_HEIGHT-1, settings->width+FOOT_HEIGHT);
     wr = (xr2 - xr1 > 0) ? (xr2 - xr1) : 1;
     rstrand = br->strand2;
 
     if (xl1 > xr1)
@@ -202,12 +476,12 @@
     rdir = 1;  // reversed
     if (sameString(rstrand, "+"))
 	rdir = -1;
 
-    if (xl2 >= 0 && xr1 >= 0 && xl2 < xr1)
+    if (xl2 < xr1)
 	vgBox(vg, xl2, yOff + yline, (xr1-xl2), hline, MG_RED);
 
-    if (xl1 >= 0 && xr2 >= 0 && xr2 < xl1)
+    if (xr2 < xl1)
 	vgBox(vg, xr2, yOff + yline, (xl1-xr2), hline, MG_RED);
 
     if (xl1 >= 0 && xl2 >= 0)
 	{
@@ -219,12 +493,13 @@
 	vgBox(vg, xr1, yOff + yline, wr, 1, MG_BLACK);
 	vgLine(vg, xr1, yOff + yline, xr1 + rdir * FOOT_HEIGHT, yOff + yfoot + (yline - yfoot) * 2, MG_BLACK);  
 	}
     }
+vgUnclip(vg);
 
 }
 
-struct spaceSaver *breaksToSS(struct breaks *breaks, struct settings *settings)
+struct spaceSaver *intraBreaksToSS(struct breaks *breaks, struct settings *settings)
 {
 struct spaceSaver *ss = spaceSaverNew(0, settings->width, 100);
 
 struct breaks *br;
@@ -246,10 +521,10 @@
 
     if (!clL || !clR || !sameString(clL->chrom, clR->chrom))
 	continue;
     
-    x1 = calcX(clL, br->chromStart, settings->width);   
-    x2 = calcX(clR, br->chromEnd2, settings->width);
+    x1 = calcX(clL, br->chromStart, settings->dataOffsetX, settings->width);   
+    x2 = calcX(clR, br->chromEnd2, settings->dataOffsetX, settings->width);
 
     spaceSaverAdd(ss, x1, x2, br);
     }
 
@@ -257,9 +532,9 @@
 return ss;
 }
 
 
-char *breaksGif(struct sqlConnection *conn, struct breaks *breaks, 
+char *intraBreaksGif(struct sqlConnection *conn, struct breaks *breaks, 
 		struct settings *settings)
 /* Create genome GIF file and HT that includes it. */
 {
 if (!breaks || !settings)
@@ -267,9 +542,9 @@
 
 if (settings->height <= 0 || settings->width <= 0)
     return NULL;
 
-struct spaceSaver *ss = breaksToSS(breaks, settings);
+struct spaceSaver *ss = intraBreaksToSS(breaks, settings);
 
 int maxRow = -1;
 struct spaceNode *sn;
 for (sn = ss->nodeList; sn; sn = sn->next)
@@ -279,21 +554,24 @@
     }
 if (maxRow < 0)
     return NULL;
 
-settings->height = (maxRow + 1) * FOOT_HEIGHT * 2;
+settings->dataHeight = (maxRow + 1) * FOOT_HEIGHT * 2;
+settings->height = settings->dataHeight + TITLE_HEIGHT;
 
 struct hvGfx *vg;
 struct tempName md5Tn;
-char *strToHash = cartSettingsString(bbPrefix, "breaks");
+char *strToHash = cartSettingsString(bbPrefix, "intraBreaks");
 trashDirMD5File(&md5Tn, "hgg", ".gif", strToHash);
 
 off_t size = fileSize(md5Tn.forCgi);
 if (!fileExists(md5Tn.forCgi) || (size == 0) || DEBUG)
     {
     vg = hvGfxOpenGif(settings->width, settings->height, md5Tn.forCgi, FALSE);
 
-    drawChromBreaks(vg->vg, ss, settings);
+    prepareImage(vg->vg, settings);
+
+    drawIntraChromBreaks(vg->vg, ss, settings);
 
     hvGfxClose(&vg);
     }
 
@@ -332,20 +608,10 @@
 
     if (bed->chromStart > cl->baseEnd || bed->chromEnd < cl->baseStart)
 	continue;
 
-    x1 = round( cl->pxStart + cl->pxWidth * ((float) (bed->chromStart - cl->baseStart) / cl->baseWidth) );
-    if (x1 < 0)
-	x1 = 0;
-    if (x1 > settings->width)
-	x1 = settings->width;
-
-    x2 = round( cl->pxStart + cl->pxWidth * ((float) (bed->chromEnd - cl->baseStart) / cl->baseWidth) );
-    if (x2 < 0)
-	x2 = 0;
-    if (x2 > settings->width)
-	x2 = settings->width;
-
+    x1 = calcX(cl, bed->chromStart, settings->dataOffsetX, settings->width);
+    x2 = calcX(cl, bed->chromEnd, settings->dataOffsetX, settings->width);
     w  = (x2 - x1 > 0) ? x2 - x1 : 1;
     val = sqlFloat(bed->name);
     
     safef(pixelStr,sizeof (pixelStr), "%d", x1);
@@ -380,27 +646,29 @@
 
 medVal = medVal / (settings->maxVal - settings->minVal);
 if (medVal > 1.0)
     medVal = 1.0;
-int medY = round((float) settings->height * (1.0 - medVal) );
+int medY = round((float) settings->dataHeight * (1.0 - medVal) ) + settings->dataOffsetY;
+
+vgSetClip(vg, settings->dataOffsetX, 0, settings->dataWidth, settings->height);
 
 for (hm = hmList; hm ; hm = hm->next)
     {
     val = hm->val / hm->count;
     val = val / (settings->maxVal - settings->minVal);
     if (val > 1.0)
 	val = 1.0;
     
-    y = round((float) settings->height * (1.0 - val) );
+    y = round((float) settings->dataHeight * (1.0 - val) ) + settings->dataOffsetY;
 
     if (y > medY)
 	vgBox(vg, hm->x, medY, hm->w, y - medY, fillCol);
     else
 	vgBox(vg, hm->x, y, hm->w, medY - y, fillCol);
 
     vgBox(vg, hm->x, y-1, hm->w, 1, col);
     }
-
+vgUnclip(vg);
 }
 
 char *copyNumberGif(struct sqlConnection *conn, struct bed *bed, 
 		    struct settings *settings, char *suffix, float medVal, Color col)
@@ -421,8 +689,10 @@
 if (!fileExists(md5Tn.forCgi) || (size == 0) || DEBUG)
     {
     vg = hvGfxOpenGif(settings->width, settings->height, md5Tn.forCgi, FALSE);
 
+    prepareImage(vg->vg, settings);
+
     drawCopyNumber(vg->vg, bed, settings, medVal, col);
 
     hvGfxClose(&vg);
     }
@@ -432,34 +702,48 @@
 }
 
 
 struct settings *initSettings(struct sqlConnection *conn, 
-			      char *pos, int width, int height, float minVal, float maxVal)
+			      char *pos, char *name, int width, int height, 
+			      float minVal, float maxVal)
 {
 struct settings *settings = AllocA(struct settings);
 
 trackLayoutInit(&tl, cart);
+
+settings->title = NULL;
+if (name)
+    settings->title = cloneString(name);
+
 settings->fontHeight = tl.fontHeight;
 settings->font = tl.font;
 
 settings->width  = width;
 settings->height = height;
 
+settings->dataOffsetX = GUTTER_WIDTH;
+settings->dataWidth   = settings->width - GUTTER_WIDTH;
+settings->dataOffsetY = TITLE_HEIGHT;
+settings->dataHeight  = settings->height - TITLE_HEIGHT;
+
 settings->minVal = minVal;
 settings->maxVal = maxVal;
 
 char *chrom = NULL;
 int start = 0;
 int stop = 0;
 
+settings->layoutHash = hashNew(0);
+settings->layoutList = NULL;
+settings->totalBases = 0.0;
+
+
 if (pos)
     {
     if (!hgParseChromRange(NULL, pos, &chrom, &start, &stop))
 	return NULL;
     }
 
-settings->totalBases = 0.0;
-
 struct genoLayChrom *gl, *glList = genoLayDbChroms(conn, FALSE);
 
 slSort(&glList, genoLayChromCmpName);
 
@@ -478,10 +762,8 @@
     settings->totalBases = 1.0;
 
 float cbases = 0.0;
 struct chromLay *cl;
-settings->layoutHash = hashNew(0);
-settings->layoutList = NULL;
 for (gl = glList; gl; gl = gl->next)
     {
     if (!chrom)
 	{
@@ -489,10 +771,10 @@
 	cl->chrom     = cloneString(gl->fullName);
 	cl->baseStart = 0.0;
 	cl->baseEnd   = (float) gl->size;
 	cl->baseWidth = cl->baseEnd - cl->baseStart;
-	cl->pxWidth   = (float) width * cl->baseWidth / settings->totalBases;
-	cl->pxStart   = (float) width * cbases / settings->totalBases;
+	cl->pxWidth   = (float) settings->dataWidth * cl->baseWidth / settings->totalBases;
+	cl->pxStart   = (float) settings->dataWidth * cbases / settings->totalBases + settings->dataOffsetX;
 
 	hashAdd(settings->layoutHash, cl->chrom, cl);
 	slAddHead(&settings->layoutList, cl);
 	cbases += (float) gl->size;
@@ -503,13 +785,10 @@
 	cl->chrom = cloneString(gl->fullName);
 	cl->baseStart = (float) start;
 	cl->baseEnd   = (float) stop;
 	cl->baseWidth = cl->baseEnd - cl->baseStart;
-	cl->pxWidth   = (float) width;
-	cl->pxStart   = 0.0;
-
-	//cl->pxStart += 1.0;
-	//cl->pxWidth -= 2.0;
+	cl->pxWidth   = (float) settings->dataWidth;
+	cl->pxStart   = settings->dataOffsetX;
 
 	hashAdd(settings->layoutHash, cl->chrom, cl);
 	slAddHead(&settings->layoutList, cl);
 	break;