int basePositionToXAxis( int base, int seqStart, int seqEnd, int
	width, int xOff  ) 
{
	double scale = scaleForPixels(width);
	double x1 = round((double)((int)base-seqStart)*scale) + xOff; 
	return(x1);
}
Ejemplo n.º 2
0
void mapBoxForCenterVariant(struct vcfRecord *rec, struct hvGfx *hvg, struct track *tg,
			    int xOff, int yOff, int width)
/* Special mouseover for center variant */
{
struct dyString *dy = dyStringNew(0);
unsigned int chromStartMap = vcfRecordTrimIndelLeftBase(rec);
unsigned int chromEndMap = vcfRecordTrimAllelesRight(rec);
gtSummaryString(rec, dy);
dyStringAppend(dy, "   Haplotypes sorted on ");
char *centerChrom = cartOptionalStringClosestToHome(cart, tg->tdb, FALSE, "centerVariantChrom");
if (centerChrom == NULL || !sameString(chromName, centerChrom))
    dyStringAppend(dy, "middle variant by default. ");
else
    dyStringAppend(dy, "this variant. ");
dyStringAppend(dy, "To anchor sorting to a different variant, click on that variant and "
	       "then click on the 'Use this variant' button below the variant name.");
const double scale = scaleForPixels(width);
int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff;
int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff;
int w = x2-x1;
if (w <= 1)
    {
    x1--;
    w = 3;
    }
mapBoxHgcOrHgGene(hvg, chromStartMap, chromEndMap, x1, yOff, w, tg->height, tg->track,
		  rec->name, dy->string, NULL, TRUE, NULL);
}
Ejemplo n.º 3
0
static void altGraphXDrawPackTrack(struct track *tg, int seqStart, int seqEnd,         
			 struct hvGfx *hvg, int xOff, int yOff, int width, 
			 MgFont *font, Color color, enum trackVisibility vis)
/* Draws the blocks for an alt-spliced gene and the connections */
{
int heightPer = tg->heightPer;
int lineHeight = tg->lineHeight;
double scale = scaleForPixels(width);
if(vis == tvFull) 
    {
    hvGfxSetClip(hvg, insideX, yOff, insideWidth, tg->height);
    altGraphXDrawPack(tg->items, tg->ss, hvg, xOff, yOff, width, heightPer, lineHeight,
		      winStart, winEnd, scale, font, color, shadesOfGray,
		      tg->track, altGraphXMap);
    hvGfxUnclip(hvg);
    }
}
Ejemplo n.º 4
0
static void contigDraw(struct track *tg, int seqStart, int seqEnd,
        struct hvGfx *hvg, int xOff, int yOff, int width, 
        MgFont *font, Color color, enum trackVisibility vis)
/* Draw contig items. */
{
struct ctgPos *ctg;
int y = yOff;
int heightPer = tg->heightPer;
int lineHeight = tg->lineHeight;
int x1,x2,w;
boolean isFull = (vis == tvFull);
int ix = 0;
char *s;
double scale = scaleForPixels(width);
for (ctg = tg->items; ctg != NULL; ctg = ctg->next)
    {
    x1 = round((double)((int)ctg->chromStart-winStart)*scale) + xOff;
    x2 = round((double)((int)ctg->chromEnd-winStart)*scale) + xOff;
    /* Clip here so that text will tend to be more visible... */
    if (x1 < xOff)
	x1 = xOff;
    if (x2 > xOff + width)
	x2 = xOff + width;
    w = x2-x1;
    if (w < 1)
	w = 1;
    hvGfxBox(hvg, x1, y, w, heightPer, color);
    s = abbreviateContig(ctg->contig, tl.font, w);
    if (s != NULL)
	hvGfxTextCentered(hvg, x1, y, w, heightPer, MG_WHITE, tl.font, s);
    if (isFull)
	y += lineHeight;
    else 
	{
	mapBoxHc(hvg, ctg->chromStart, ctg->chromEnd, x1,y,w,heightPer, tg->track, 
	    tg->mapItemName(tg, ctg), 
	    tg->itemName(tg, ctg));
	}
    ++ix;
    }
}
Ejemplo n.º 5
0
struct preDrawContainer *bedGraphLoadPreDraw(struct track *tg, int seqStart, int seqEnd, int width)
/* Do bits that load the predraw buffer tg->preDrawContainer. */
{
/* Just need to do this once... */
if (tg->preDrawContainer)
    return tg->preDrawContainer;

struct bedGraphItem *wi;
double pixelsPerBase = scaleForPixels(width);
double basesPerPixel = 1.0;
int i;				/* an integer loop counter	*/
if (pixelsPerBase > 0.0)
    basesPerPixel = 1.0 / pixelsPerBase;

/* Allocate predraw and save it and related info in the track. */
struct preDrawContainer *pre = tg->preDrawContainer = initPreDrawContainer(width);
struct preDrawElement *preDraw = pre->preDraw;	/* to accumulate everything in prep for draw */
int preDrawZero = pre->preDrawZero;		/* location in preDraw where screen starts */
int preDrawSize = pre->preDrawSize;		/* size of preDraw array */

/*	walk through all the data fill in the preDraw array	*/
for (wi = tg->items; wi != NULL; wi = wi->next)
    {
    double dataValue = wi->dataValue;	/* the data value to graph */

    /*	Ready to draw, what do we know:
    *	the feature being processed:
    *	chrom coords:  [wi->start : wi->end)
    *	its data value: dataValue = wi->dataValue
    *
    *	The drawing window, in pixels:
    *	xOff = left margin, yOff = top margin, h = height of drawing window
    *	drawing window in chrom coords: seqStart, seqEnd
    *	'basesPerPixel' is known, 'pixelsPerBase' is known
    */
    /*	let's check end point screen coordinates.  If they are
     *	the same, then this entire data block lands on one pixel,
     *	It is OK if these end up + or -, we do want to
     *	keep track of pixels before and after the screen for
     *	later smoothing operations
     */
    int x1 = (wi->start - seqStart) * pixelsPerBase;
    int x2 = (wi->end - seqStart) * pixelsPerBase;

    if (x2 > x1)
	{
	for (i = x1; i <= x2; ++i)
	    {
	    int xCoord = preDrawZero + i;
	    if ((xCoord >= 0) && (xCoord < preDrawSize))
		{
		++preDraw[xCoord].count;
		if (dataValue > preDraw[xCoord].max)
		    preDraw[xCoord].max = dataValue;
		if (dataValue < preDraw[xCoord].min)
		    preDraw[xCoord].min = dataValue;
		preDraw[xCoord].sumData += dataValue;
		preDraw[xCoord].sumSquares += dataValue * dataValue;
		}
	    }
	}
    else
	{	/*	only one pixel for this block of data */
	int xCoord = preDrawZero + x1;
	/*	if the point falls within our array, record it.
	 *	the (wi->validCount > 0) is a safety check.  It
	 *	should always be true unless the data was
	 *	prepared incorrectly.
	 */
	if ((xCoord >= 0) && (xCoord < preDrawSize))
	    {
	    ++preDraw[xCoord].count;
	    if (dataValue > preDraw[xCoord].max)
		preDraw[xCoord].max = dataValue;
	    if (dataValue < preDraw[xCoord].min)
		preDraw[xCoord].min = dataValue;
	    preDraw[xCoord].sumData += dataValue;
	    preDraw[xCoord].sumSquares += dataValue * dataValue;
	    }
	}
    }	/*	for (wi = tg->items; wi != NULL; wi = wi->next)	*/
return pre;
}
Ejemplo n.º 6
0
static void cgDrawEither(struct track *tg, int seqStart, int seqEnd,
        struct hvGfx *hvg, int xOff, int yOff, int width,
        MgFont *font, Color color, enum trackVisibility vis,
        char *binFileName)
/* Draw chromosome graph - either built in or not. */
{
struct chromGraphSettings *cgs = tg->customPt;
int x,y,lastX=0,lastY=0, llastX = 0, llastY = 0;
int height = tg->height;
int maxGapToFill = cgs->maxGapToFill;
int lastPos = -maxGapToFill-1;
double minVal = cgs->minVal;
double yScale = (height-1)/(cgs->maxVal - minVal);
double xScale = scaleForPixels(width);
Color myColor = cgColorLikeHgGenome(tg, hvg);

/* Draw background lines in full mode. */
if (vis == tvFull && cgs->linesAtCount != 0)
    {
    int i;
    Color lightBlue = hvGfxFindRgb(hvg, &guidelineColor);
    for (i=0; i<cgs->linesAtCount; ++i)
        {
	y = height - 1 - (cgs->linesAt[i] - minVal)*yScale + yOff;
	hvGfxBox(hvg, xOff, y, width, 1, lightBlue);
	}
    }

if (binFileName)
    {
    struct chromGraphBin *cgb = chromGraphBinOpen(binFileName);
    if (chromGraphBinSeekToChrom(cgb, chromName))
	{
	int seqStartMinus = seqStart - cgs->maxGapToFill;
	while (chromGraphBinNextVal(cgb))
	    {
	    int pos = cgb->chromStart;
	    if (pos >= seqStartMinus)
		{
		double val = cgb->val;
		x = (pos - seqStart)*xScale + xOff;
		y = height - 1 - (val - minVal)*yScale + yOff;
		if (x >= xOff)
		    {
		    if (pos - lastPos <= maxGapToFill)
			{
			if (llastX != lastX || llastY != lastY || lastX != x || lastY != y)
			    hvGfxLine(hvg, lastX, lastY, x, y, myColor);
			}
		    else
			hvGfxDot(hvg, x, y, myColor);
		    }
		llastX = lastX;
		llastY = lastY;
		lastX = x;
		lastY = y;
		lastPos = pos;
		if (pos >= seqEnd)
		    break;
		}
	    }
	}
    }
else
    {
    struct sqlConnection *conn = hAllocConn(database);
    char query[512];
    struct sqlResult *sr;
    char **row;
    /* Construct query.  Set up a little more than window so that
     * we can draw connecting lines. */
    sqlSafef(query, sizeof(query),
	"select chromStart,val from %s "
	"where chrom='%s' and chromStart>=%d and chromStart<%d",
	tg->table, chromName,
	seqStart - cgs->maxGapToFill, seqEnd + cgs->maxGapToFill);
    sr = sqlGetResult(conn, query);

    /* Loop through drawing lines from one point to another unless
     * the points are too far apart. */
    while ((row = sqlNextRow(sr)) != NULL)
	{
	int pos = sqlUnsigned(row[0]);
	double val = atof(row[1]);
	x = (pos - seqStart)*xScale + xOff;
	y = height - 1 - (val - minVal)*yScale + yOff;
	if (x >= xOff)
	    {
	    if (pos - lastPos <= maxGapToFill)
		{
		if (llastX != lastX || llastY != lastY || lastX != x || lastY != y)
		    hvGfxLine(hvg, lastX, lastY, x, y, myColor);
		}
	    else
		hvGfxDot(hvg, x, y, myColor);
	    }
	llastX = lastX;
	llastY = lastY;
	lastX = x;
	lastY = y;
	lastPos = pos;
	if (pos >= seqEnd)
	    break;
	}
    sqlFreeResult(&sr);
    hFreeConn(&conn);
    }

/* Do map box */
xOff = hvGfxAdjXW(hvg, xOff, width);

char *encodedTrack = cgiEncode(tg->track);
if (theImgBox && curImgTrack)
    {
    char link[512];     // FIXME: winStart/winEnd are not right when using a portal
    safef(link,sizeof(link),"%s&c=%s&o=%d&t=%d&g=%s", hgcNameAndSettings(),
        chromName, winStart, winEnd, encodedTrack);
#ifdef IMAGEv2_SHORT_MAPITEMS
    if (xOff < insideX && xOff+width > insideX)
        warn("cgDrawEither(%s) map item spanning slices. LX:%d TY:%d RX:%d BY:%d  link:[%s]",
             encodedTrack,xOff, yOff, xOff+width, yOff+height, link);
#endif//def IMAGEv2_SHORT_MAPITEMS
    imgTrackAddMapItem(curImgTrack,link,NULL,xOff,yOff,xOff+width,yOff+height,tg->track);
    }
else
    {
    hPrintf("<AREA SHAPE=RECT COORDS=\"%d,%d,%d,%d\" ", xOff, yOff, xOff+width,
            yOff+height);
    hPrintf("HREF=\"%s&o=%d&t=%d&g=%s&c=%s&l=%d&r=%d&db=%s&pix=%d\">\n",
            hgcNameAndSettings(), winStart, winEnd, encodedTrack, chromName, winStart, winEnd,
            database, tl.picWidth);
    }
}
static void wiggleLinkedFeaturesDraw(struct track *tg, 
	int seqStart, int seqEnd,
struct hvGfx *hvg, int xOff, int yOff, int width, 
	MgFont *font, Color color, enum trackVisibility vis)
	/* Currently this routine is adapted from Terry's 
	* linkedFeatureSeriesDraw() routine.
	* It is called for 'sample' tracks as specified in the trackDb.ra.
	* and it looks at the cart to decide whether to interpolate, fill blocks,
	* and use anti-aliasing.*/
{
	int i;
	struct linkedFeatures *lf;
	struct simpleFeature *sf;
	int y = yOff;
	int heightPer = tg->heightPer;
	int lineHeight = tg->lineHeight;
	int x1,x2;
	boolean isFull = (vis == tvFull);
	Color bColor = tg->ixAltColor;
	double scale = scaleForPixels(width);
	int prevX = -1;
	int gapPrevX = -1;
	double prevY = -1;
	double y1 = -1, y2;
	int ybase;
	int sampleX, sampleY; /* A sample in sample coordinates. 
						  * Sample X coordinate is chromosome coordinate.
						  * Sample Y coordinate is usually 0-1000 */
	int binCount = 1.0/tg->scaleRange;   /* Maximum sample Y coordinate. */
	int bin;	      /* Sample Y coordinates are first converted to
					  * bin coordinates, and then to pixels.  I'm not
					  * totally sure why.  */



	int currentX, currentXEnd, currentWidth;

	int leftSide, rightSide;

	int noZoom = 1;
	enum wiggleOptEnum wiggleType;
	char *interpolate = NULL;
	char *aa = NULL; 
	boolean antiAlias = FALSE;
	int fill; 
	int lineGapSize;
	double min0, max0;

	char o1[128]; /* Option 1 - linear interp */
	char o2[128]; /* Option 2 - anti alias */
	char o3[128]; /* Option 3 - fill */
	char o4[128]; /* Option 4 - minimum vertical range cutoff of plot */	
	char o5[128]; /* Option 5 - maximum vertical range cutoff of plot */
	char o6[128]; /* Option 6 - max gap where interpolation is still done */
	char cartStr[64];
	char *fillStr;

	double hFactor;
	double minRange, maxRange;
	double minRangeCutoff, maxRangeCutoff;


	Color gridColor = hvGfxFindRgb(hvg, &guidelineColor); /* for horizontal lines*/

	lf=tg->items;    
	if(lf==NULL) return;

	//take care of cart options
	safef( o1, 128,"%s.linear.interp", tg->track);
	safef( o2, 128, "%s.anti.alias", tg->track);
	safef( o3, 128,"%s.fill", tg->track);
	safef( o4, 128,"%s.min.cutoff", tg->track);
	safef( o5, 128,"%s.max.cutoff", tg->track);
	safef( o6, 128,"%s.interp.gap", tg->track);

	interpolate = cartUsualString(cart, o1, "Linear Interpolation");
	wiggleType = wiggleStringToEnum(interpolate);
	aa = cartUsualString(cart, o2, "on");
	antiAlias = sameString(aa, "on");

	//don't fill gcPercent track by default (but fill others)
	if(sameString( tg->table, "pGC") && sameString(database,"zooHuman3"))
	{
		fillStr = cartUsualString(cart, o3, "0");
	}
	else
	{
		fillStr = cartUsualString(cart, o3, "1");
	}
	fill = atoi(fillStr);
	cartSetString(cart, o3, fillStr );

	//the 0.1 is so the label doesn't get truncated with integer valued user input min
	//display range.
	minRangeCutoff = max( atof(cartUsualString(cart,o4,"0.0"))-0.1, tg->minRange );
	maxRangeCutoff = min( atof(cartUsualString(cart,o5,"1000.0"))+0.1, tg->maxRange);

	lineGapSize = atoi(cartUsualString(cart, o6, "200"));

	//update cart settings to reflect truncated range cutoff values
	cartSetString( cart, "win", "F" );
	safef( cartStr, 64, "%g", minRangeCutoff );
	cartSetString( cart, o4, cartStr );
	safef( cartStr, 64, "%g", maxRangeCutoff );
	cartSetString( cart, o5, cartStr );

	heightPer = tg->heightPer+1;
	hFactor = (double)heightPer*tg->scaleRange;

	//errAbort( "min=%g, max=%g\n", minRangeCutoff, maxRangeCutoff );


	if( sameString( tg->table, "zoo" ) || sameString( tg->table, "zooNew" ) )
		binCount = binCount - 100;    //save some space at top, between each zoo species

	minRange = whichSampleBin( minRangeCutoff, tg->minRange, tg->maxRange, binCount );
	maxRange = whichSampleBin( maxRangeCutoff, tg->minRange, tg->maxRange, binCount );

	//errAbort( "(%g,%g) cutoff=(%g,%g)\n", tg->minRange, tg->maxRange, minRangeCutoff, maxRangeCutoff );


	if( sameString( tg->table, "zoo" ) || sameString( tg->table, "zooNew" ) )
	{
		/*Always interpolate zoo track (since gaps are explicitly defined*/
		lineGapSize = -1;
	}
	else if( tg->minRange == 0 && tg->maxRange == 8 )    //range for all L-score tracks
	{
		if( isFull )
		{
			min0 = whichSampleNum( minRange, tg->minRange, tg->maxRange, binCount );
			max0 = whichSampleNum( maxRange, tg->minRange, tg->maxRange,  binCount );
			for( i=1; i<=6; i++ )
				drawWiggleHorizontalLine(hvg, (double)i, min0, max0,
				binCount, y, hFactor, heightPer, gridColor );
		}
	}

	for(lf = tg->items; lf != NULL; lf = lf->next) 
	{
		gapPrevX = -1;
		prevX = -1;
		ybase = (int)((double)y+hFactor+(double)heightPer);


		for (sf = lf->components; sf != NULL; sf = sf->next)
		{
			sampleX = sf->start;
			sampleY = sf->end - sampleX;	// Stange encoding but so it is. 
			// It is to deal with the fact that
			// for a BED: sf->end = sf->start + length
			// but in our case length = height (or y-value)
			// so to recover height we take
			// height = sf->end - sf->start.
			// Otherwise another sf variable would 
			// be needed.


			/*mapping or sequencing gap*/
			if (sampleY == 0)
			{
				bin = -whichSampleBin( (int)((maxRange - minRange)/5.0+minRange), 
					minRange, maxRange, binCount );
				y1 = (int)((double)y+((double)bin)* hFactor+(double)heightPer);
				if( gapPrevX >= 0 )
					drawScaledBox(hvg, sampleX, gapPrevX, scale, 
					xOff, (int)y1, (int)(.10*heightPer), shadesOfGray[2]);
				gapPrevX = sampleX;
				prevX = -1; /*connect next point with gray bar too*/
				continue;
			}
			if (sampleY > maxRange)
				sampleY = maxRange;
			if (sampleY < minRange)
				sampleY = minRange;
			bin = -whichSampleBin( sampleY, minRange, maxRange, binCount );


			x1 = round((double)(sampleX-winStart)*scale) + xOff;
			y1 = (int)((double)y+((double)bin)* hFactor+(double)heightPer);



			if (prevX > 0)
			{
				y2 = prevY;
				x2 = round((double)(prevX-winStart)*scale) + xOff;
				if( wiggleType == wiggleLinearInterpolation ) 
					/*connect samples*/
				{
					if( lineGapSize < 0 || prevX - sampleX <= lineGapSize )   
						/*don't interpolate over large gaps*/
					{
						if (fill)
							hvGfxFillUnder(hvg, x1,y1, x2,y2, ybase, bColor);
						else
							hvGfxLine(hvg, x1,y1, x2,y2, color);
					}
				}
			}

			//if( x1 < 0 || x1 > tl.picWidth )
			//printf("x1 = %d, sampleX=%d, winStart = %d\n<br>", x1, sampleX, winStart );
			if( x1 >= 0 && x1 <= tl.picWidth )
			{
				/* Draw the points themselves*/
				drawScaledBox(hvg, sampleX, sampleX+1, scale, xOff, (int)y1-1, 3, color);
				if( fill )
					drawScaledBox(hvg, sampleX, sampleX+1, scale, xOff, (int)y1+2, 
					ybase-y1-2, bColor);
			}

			prevX = gapPrevX = sampleX;
			prevY = y1;
		}

		leftSide = max( tg->itemStart(tg,lf), winStart );
		rightSide = min(  tg->itemEnd(tg,lf), winEnd );

		currentX =  round((double)((int)leftSide-winStart)*scale) + xOff;
		currentXEnd =  round((double)((int)rightSide-winStart)*scale) + xOff;
		currentWidth = currentXEnd - currentX;

		if( noZoom && isFull )
		{
			fprintf(stderr, "mapBoxHc(id: %s;) in wiggleLinkedFeatures\n", 
				tg->track);
			mapBoxHc(hvg, lf->start, lf->end, currentX ,y, currentWidth,
				heightPer, tg->track, tg->mapItemName(tg, lf), tg->itemName(tg, lf));

			if( lf->next != NULL )
				y += sampleUpdateY( lf->name, lf->next->name, lineHeight );
			else
				y += lineHeight;
		}

	}
}
static void goldDrawDense(struct track *tg, int seqStart, int seqEnd,
        struct hvGfx *hvg, int xOff, int yOff, int width, 
        MgFont *font, Color color, enum trackVisibility vis)
/* Draw golden path items. */
{
int baseWidth = seqEnd - seqStart;
struct agpFrag *frag;
struct agpGap *gap;
int y = yOff;
int heightPer = tg->heightPer;
int lineHeight = tg->lineHeight;
int x1,x2,w;
int midLineOff = heightPer/2;
boolean isFull = (vis == tvFull);
Color brown = color;
Color gold = tg->ixAltColor;
Color pink = 0;
Color pink1 = hvGfxFindColorIx(hvg, 240, 140, 140);
Color pink2 = hvGfxFindColorIx(hvg, 240, 100, 100);
int ix = 0;
double scale = scaleForPixels(width);

/* Draw gaps if any. */
if (!isFull)
    {
    int midY = y + midLineOff;
    for (gap = tg->customPt; gap != NULL; gap = gap->next)
	{
	if (!sameWord(gap->bridge, "no"))
	    {
	    drawScaledBox(hvg, gap->chromStart, gap->chromEnd, scale, xOff, midY, 1, brown);
	    }
	}
    }

for (frag = tg->items; frag != NULL; frag = frag->next)
    {
    x1 = round((double)((int)frag->chromStart-winStart)*scale) + xOff;
    x2 = round((double)((int)frag->chromEnd-winStart)*scale) + xOff;
    w = x2-x1;
    color =  ((ix&1) ? gold : brown);
    pink = ((ix&1) ? pink1 : pink2);
    if (w < 1)
	w = 1;
    if (sameString(frag->type, "A")) color = pink;
    hvGfxBox(hvg, x1, y, w, heightPer, color);
    if (isFull)
	y += lineHeight;
    else if (baseWidth < 10000000)
	{
	char status[256];
	sprintf(status, "%s:%d-%d %s %s:%d-%d", 
	    frag->frag, frag->fragStart, frag->fragEnd,
	    frag->strand,
	    frag->chrom, frag->chromStart, frag->chromEnd);

	mapBoxHc(hvg, frag->chromStart, frag->chromEnd, x1,y,w,heightPer, tg->track, 
	    frag->frag, status);
	}
    ++ix;
    }
}
Ejemplo n.º 9
0
static void drawOneRec(struct vcfRecord *rec, unsigned short *gtHapOrder, unsigned short gtHapCount,
		       struct track *tg, struct hvGfx *hvg, int xOff, int yOff, int width,
		       boolean isClustered, boolean isCenter, enum hapColorMode colorMode)
/* Draw a stack of genotype bars for this record */
{
unsigned int chromStartMap = vcfRecordTrimIndelLeftBase(rec);
unsigned int chromEndMap = vcfRecordTrimAllelesRight(rec);
const double scale = scaleForPixels(width);
int x1 = round((double)(rec->chromStart-winStart)*scale) + xOff;
int x2 = round((double)(rec->chromEnd-winStart)*scale) + xOff;
int w = x2-x1;
if (w <= 1)
    {
    x1--;
    w = 3;
    }
// When coloring mode is altOnly, we draw one extra pixel row at the top & one at bottom
// to show the locations of variants, since the reference alleles are invisible:
int extraPixel = 0;
int hapHeight = tg->height - CLIP_PAD;
if (colorMode == altOnlyMode)
    {
    hvGfxLine(hvg, x1, yOff, x2, yOff, (isClustered ? purple : shadesOfGray[5]));
    extraPixel = 1;
    hapHeight -= extraPixel*2;
    }
double hapsPerPix = (double)gtHapCount / hapHeight;
int pixIx;
for (pixIx = 0;  pixIx < hapHeight;  pixIx++)
    {
    int gtHapOrderIxStart = (int)(hapsPerPix * pixIx);
    int gtHapOrderIxEnd = round(hapsPerPix * (pixIx + 1));
    if (gtHapOrderIxEnd == gtHapOrderIxStart)
	gtHapOrderIxEnd++;
    int unks = 0, refs = 0, alts = 0;
    int gtHapOrderIx;
    for (gtHapOrderIx = gtHapOrderIxStart;  gtHapOrderIx < gtHapOrderIxEnd;  gtHapOrderIx++)
	{
	int gtHapIx = gtHapOrder[gtHapOrderIx];
	int hapIx = gtHapIx & 1;
	int gtIx = gtHapIx >>1;
	struct vcfGenotype *gt = &(rec->genotypes[gtIx]);
	if (gt->isPhased || gt->isHaploid || (gt->hapIxA == gt->hapIxB))
	    {
	    int alIx = hapIx ? gt->hapIxB : gt->hapIxA;
	    if (alIx < 0)
		unks++;
	    else if (alIx > 0)
		alts++;
	    else
		refs++;
	    }
	else
	    unks++;
	}
    int y = yOff + extraPixel + pixIx;
    Color col;
    if (colorMode == baseMode)
	col = colorByBase(refs, alts, unks, rec->alleles[0], rec->alleles[1]);
    else if (colorMode == refAltMode)
	col = colorByRefAlt(refs, alts, unks);
    else
	col = colorByAltOnly(refs, alts, unks);
    if (col != MG_WHITE)
	hvGfxLine(hvg, x1, y, x2, y, col);
    }
int yBot = yOff + tg->height - CLIP_PAD - 1;
if (isCenter)
    {
    if (colorMode == altOnlyMode)
	{
	// Colorful outline to distinguish this variant:
	hvGfxLine(hvg, x1-1, yOff, x1-1, yBot, purple);
	hvGfxLine(hvg, x2+1, yOff, x2+1, yBot, purple);
	hvGfxLine(hvg, x1-1, yOff, x2+1, yOff, purple);
	hvGfxLine(hvg, x1-1, yBot, x2+1, yBot, purple);
	}
    else
	{
	// Thick black lines to distinguish this variant:
	hvGfxBox(hvg, x1-3, yOff, 3, tg->height, MG_BLACK);
	hvGfxBox(hvg, x2, yOff, 3, tg->height, MG_BLACK);
	hvGfxLine(hvg, x1-2, yOff, x2+2, yOff, MG_BLACK);
	hvGfxLine(hvg, x1-2, yBot, x2+2, yBot, MG_BLACK);
	}
    // Mouseover was handled already by mapBoxForCenterVariant
    }
else
    {
    struct dyString *dy = dyStringNew(0);
    gtSummaryString(rec, dy);
    mapBoxHgcOrHgGene(hvg, chromStartMap, chromEndMap, x1, yOff, w, tg->height, tg->track,
		      rec->name, dy->string, NULL, TRUE, NULL);
    }
if (colorMode == altOnlyMode)
    hvGfxLine(hvg, x1, yBot, x2, yBot, (isClustered ? purple : shadesOfGray[5]));
}