b8180d9f6d41dc708a2f249ba892cbca311e7a06 jcasper Mon Feb 27 11:38:55 2023 -0800 Adding transparency support for colors refs #30569 diff --git src/hg/hgGenome/mainPage.c src/hg/hgGenome/mainPage.c index 4f8b436..c714545 100644 --- src/hg/hgGenome/mainPage.c +++ src/hg/hgGenome/mainPage.c @@ -1,694 +1,694 @@ /* mainPage - draws the main hgGenome page, including some controls * on the top and the graphic. */ /* Copyright (C) 2013 The Regents of the University of California * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #include "common.h" #include "psGfx.h" #include "pscmGfx.h" #include "linefile.h" #include "hash.h" #include "cheapcgi.h" #include "htmshell.h" #include "cart.h" #include "hui.h" #include "dbDb.h" #include "ra.h" #include "hdb.h" #include "web.h" #include "portable.h" #include "hgColors.h" #include "trackLayout.h" #include "chromInfo.h" #include "hvGfx.h" #include "genoLay.h" #include "cytoBand.h" #include "hCytoBand.h" #include "errCatch.h" #include "chromGraph.h" #include "customTrack.h" #include "hPrint.h" #include "jsHelper.h" #include "hgGenome.h" #include "trashDir.h" static char *allColors[] = { "black", "blue", "purple", "red", "orange", "yellow", "green", "gray", }; static char *defaultColors[maxLinesOfGraphs][maxGraphsPerLine] = { {"blue", "red", "black", "yellow", }, {"gray", "purple", "green", "orange",}, {"blue", "red", "black", "yellow", }, {"gray", "purple", "green", "orange",}, {"blue", "red", "black", "yellow", }, {"gray", "purple", "green", "orange",}, }; char *graphColorAt(int row, int col) /* Return graph color at given row/column, NULL if nonw. */ { char *varName = graphColorVarName(row, col); char *color = cartUsualString(cart, varName, defaultColors[row][col]); if (color == NULL) color = defaultColors[0][0]; return color; } Color colorFromAscii(struct hvGfx *hvg, char *asciiColor) /* Get color index for a named color. */ { if (sameWord("red", asciiColor)) return MG_RED; else if (sameWord("blue", asciiColor)) return MG_BLUE; else if (sameWord("yellow", asciiColor)) return hvGfxFindColorIx(hvg, 220, 220, 0); else if (sameWord("purple", asciiColor)) return hvGfxFindColorIx(hvg, 150, 0, 200); else if (sameWord("orange", asciiColor)) return hvGfxFindColorIx(hvg, 230, 120, 0); else if (sameWord("green", asciiColor)) return hvGfxFindColorIx(hvg, 0, 180, 0); else if (sameWord("gray", asciiColor)) return MG_GRAY; else return MG_BLACK; } /* Page drawing stuff. */ void drawChromGraph(struct hvGfx *hvg, struct sqlConnection *conn, struct genoLay *gl, char *chromGraph, int yOff, int height, Color color, boolean leftLabel, boolean rightLabel, boolean firstInRow) /* Draw chromosome graph on all chromosomes in layout at given * y offset and height. */ { boolean yellowMissing = getYellowMissing(); struct genoGraph *gg = hashFindVal(ggHash, chromGraph); if (gg != NULL) { /* Get binary data source and scaling info etc. */ struct chromGraphBin *cgb = gg->cgb; struct chromGraphSettings *cgs = gg->settings; int maxGapToFill = cgs->maxGapToFill; - static struct rgbColor missingDataColor = { 180, 180, 120}; + static struct rgbColor missingDataColor = { 180, 180, 120, 255}; Color missingColor = hvGfxFindRgb(hvg, &missingDataColor); double pixelsPerBase = 1.0/gl->basesPerPixel; double gMin = cgs->minVal, gMax = cgs->maxVal, gScale; gScale = height/(gMax-gMin); /* Draw significance threshold as a light blue line */ if (leftLabel) { - static struct rgbColor guidelineColor = { 220, 220, 255}; + static struct rgbColor guidelineColor = { 220, 220, 255, 255}; Color lightBlue = hvGfxFindRgb(hvg, &guidelineColor); struct slRef *ref; struct genoLayChrom *chrom; int rightX = gl->picWidth - gl->rightLabelWidth - gl->margin; int leftX = gl->leftLabelWidth + gl->margin; int width = rightX - leftX; double threshold = getThreshold(); if (threshold >= gMin && threshold <= gMax) { int y = height - ((threshold - gMin)*gScale) + yOff; for (ref = gl->leftList; ref != NULL; ref = ref->next) { chrom = ref->val; hvGfxBox(hvg, leftX, y + chrom->y, width, 1, lightBlue); } ref = gl->bottomList; if (ref != NULL) { chrom = ref->val; hvGfxBox(hvg, leftX, y + chrom->y, width, 1, lightBlue); } } } /* Draw graphs on each chromosome */ chromGraphBinRewind(cgb); while (chromGraphBinNextChrom(cgb)) { struct genoLayChrom *chrom = hashFindVal(gl->chromHash, cgb->chrom); if (chrom) { int chromX = chrom->x, chromY = chrom->y; int minY = chromY + yOff; int maxY = chromY + yOff + height - 1; if (chromGraphBinNextVal(cgb)) { /* Set clipping so can't scribble outside of box. */ hvGfxSetClip(hvg, chromX, chromY, chrom->width+1, chrom->height); /* Handle first point as special case here, so don't * have to test for first point in inner loop. */ double x,lastX; int y,start,lastStart,lastY; start = lastStart = cgb->chromStart; x = lastX = pixelsPerBase*start + chromX; y = lastY = (height - ((cgb->val - gMin)*gScale)) + chromY+yOff; if (y < minY) y = minY; else if (y > maxY) y = maxY; struct pscmGfx *pscm = NULL; if (hvg->pixelBased) { hvGfxDot(hvg, x, y, color); } else { pscm = (struct pscmGfx *)hvg->vg->data; pscmSetColor(pscm, color); psFillEllipse(pscm->ps, x, y, 0.01, 0.01); psSetLineWidth(pscm->ps, 0.01); } /* Draw rest of points, connecting with line to previous point * if not too far off. */ while (chromGraphBinNextVal(cgb)) { start = cgb->chromStart; x = pixelsPerBase*start + chromX; if (hvg->pixelBased) x = (int) x; y = (height - ((cgb->val - gMin)*gScale)) + chromY+yOff; if (y < minY) y = minY; else if (y > maxY) y = maxY; if (x != lastX || y != lastY) { if (start - lastStart <= maxGapToFill) { if (hvg->pixelBased) hvGfxLine(hvg, lastX, lastY, x, y, color); else { pscmSetColor(pscm, color); psDrawLine(pscm->ps, lastX, lastY, x, y); } } else { if (yellowMissing && leftLabel) { int width = x - lastX - 1; if (width > 0) hvGfxBox(hvg, lastX+1, minY, width, height, missingColor); } if (hvg->pixelBased) { hvGfxDot(hvg, x, y, color); } else { pscmSetColor(pscm, color); psFillEllipse(pscm->ps, x, y, 0.01, 0.01); } } } lastX = x; lastY = y; lastStart = start; } if (!hvg->pixelBased) { psSetLineWidth(pscm->ps, 1); } hvGfxUnclip(hvg); } } else { /* Just read and discard data. */ while (chromGraphBinNextVal(cgb)) ; } } /* Draw labels. */ if (withLabels && (leftLabel || rightLabel)) { int lineY = yOff; int i,j; int spaceWidth = tl.nWidth; int tickWidth = spaceWidth*2/3; int fontPixelHeight = mgFontPixelHeight(gl->font); for (i=0; i<gl->lineCount; ++i) { hvGfxSetClip(hvg, 0, lineY, gl->picWidth, gl->lineHeight); for (j=0; j< cgs->linesAtCount; ++j) { double lineAt = cgs->linesAt[j]; int y = (height - ((lineAt - gMin)*gScale)) + lineY; int textTop = y - fontPixelHeight/2+1; int textBottom = textTop + fontPixelHeight; char label[24]; safef(label, sizeof(label), "%g", lineAt); if (leftLabel) { hvGfxBox(hvg, gl->margin + gl->leftLabelWidth - tickWidth-1, y, tickWidth, 1, color); if (textTop >= lineY && textBottom < lineY + height) { hvGfxTextRight(hvg, gl->margin, textTop, gl->leftLabelWidth-spaceWidth, fontPixelHeight, color, gl->font, label); } } if (rightLabel) { hvGfxBox(hvg, gl->picWidth - gl->margin - gl->rightLabelWidth+1, y, tickWidth, 1, color); if (textTop >= lineY && textBottom < lineY + height) { hvGfxText(hvg, gl->picWidth - gl->margin - gl->rightLabelWidth + spaceWidth, textTop, color, gl->font, label); } } } lineY += gl->lineHeight; hvGfxUnclip(hvg); } } } } void genomeGif(struct sqlConnection *conn, struct genoLay *gl, int graphRows, int graphCols, int oneRowHeight, char *psOutput) /* Create genome GIF file and HTML that includes it. */ { struct hvGfx *hvg; struct tempName gifTn; Color shadesOfGray[10]; int maxShade = ArraySize(shadesOfGray)-1; int spacing = 1; int yOffset = 2*spacing; int innerHeight = oneRowHeight - 3*spacing; int i,j; if (psOutput) { hvg = hvGfxOpenPostScript(gl->picWidth, gl->picHeight, psOutput); } else { /* Create gif file and make reference to it in html. */ trashDirFile(&gifTn, "hgg", "ideo", ".png"); hvg = hvGfxOpenPng(gl->picWidth, gl->picHeight, gifTn.forCgi, FALSE); hPrintf("<INPUT TYPE=IMAGE SRC=\"%s\" BORDER=1 WIDTH=%d HEIGHT=%d NAME=\"%s\">", gifTn.forHtml, gl->picWidth, gl->picHeight, hggClick); } /* Get our grayscale. */ hMakeGrayShades(hvg, shadesOfGray, maxShade); /* Draw the labels and then the chromosomes. */ genoLayDrawChromLabels(gl, hvg, MG_BLACK); genoLayDrawBandedChroms(gl, hvg, database, conn, shadesOfGray, maxShade, MG_BLACK); /* Draw chromosome graphs. */ for (i=0; i<graphRows; ++i) { boolean firstInRow = TRUE; for (j=graphCols-1; j>=0; --j) { char *graph = graphSourceAt(i,j); if (graph != NULL && graph[0] != 0) { Color color = colorFromAscii(hvg, graphColorAt(i,j)); drawChromGraph(hvg, conn, gl, graph, gl->betweenChromOffsetY + yOffset, innerHeight, color, j==0, j==1, firstInRow); firstInRow = FALSE; } } yOffset += oneRowHeight; } hvGfxBox(hvg, 0, 0, gl->picWidth, 1, MG_GRAY); hvGfxBox(hvg, 0, gl->picHeight-1, gl->picWidth, 1, MG_GRAY); hvGfxBox(hvg, 0, 0, 1, gl->picHeight, MG_GRAY); hvGfxBox(hvg, gl->picWidth-1, 0, 1, gl->picHeight, MG_GRAY); hvGfxClose(&hvg); } void graphDropdown(struct sqlConnection *conn, char *varName, char *curVal, char *event, char *js) /* Make a drop-down with available chrom graphs */ { int totalCount = 1; char **menu, **values; int i = 0; struct slRef *ref; for (ref = ggList; ref != NULL; ref = ref->next) { struct genoGraph *gg = ref->val; if (gg->isComposite == FALSE) totalCount++; } AllocArray(menu, totalCount); AllocArray(values, totalCount); menu[0] = "-- nothing --"; values[0] = ""; for (ref = ggList; ref != NULL; ref = ref->next) { struct genoGraph *gg = ref->val; if (gg->isComposite == FALSE) { ++i; menu[i] = gg->shortLabel; values[i] = gg->name; } } cgiMakeDropListFull(varName, menu, values, totalCount, curVal, event, js); freez(&menu); freez(&values); } void colorDropdown(int row, int col, char *event, char *js) /* Put up color drop down menu. */ { char *varName = graphColorVarName(row, col); char *curVal = graphColorAt(row, col); cgiMakeDropListFull(varName, allColors, allColors, ArraySize(allColors), curVal, event, js); } static void addThresholdGraphCarries(struct dyString *dy, int graphRows, int graphCols, boolean cgaOnly) /* Add javascript that carries over threshold and graph vars * to new form. */ { if (cgaOnly) return; jsTextCarryOver(dy, getThresholdName()); int i,j; for (i=0; i<graphRows; ++i) for (j=0; j<graphCols; ++j) { jsDropDownCarryOver(dy, graphVarName(i,j)); jsDropDownCarryOver(dy, graphColorVarName(i,j)); } } static struct dyString *onChangeStart(int graphRows, int graphCols, boolean cgaOnly) /* Return common prefix to onChange javascript string */ { struct dyString *dy = jsOnChangeStart(); addThresholdGraphCarries(dy, graphRows, graphCols, cgaOnly); return dy; } static char *onChangeClade(int graphRows, int graphCols, boolean cgaOnly) /* Return javascript executed when they change clade. */ { struct dyString *dy = onChangeStart(graphRows, graphCols, cgaOnly); jsDropDownCarryOver(dy, "clade"); dyStringAppend(dy, " document.hiddenForm.org.value=0;"); dyStringAppend(dy, " document.hiddenForm.db.value=0;"); return jsOnChangeEnd(&dy); } static char *onChangeOrg(int graphRows, int graphCols, boolean cgaOnly) /* Return javascript executed when they change organism. */ { struct dyString *dy = onChangeStart(graphRows, graphCols, cgaOnly); jsDropDownCarryOver(dy, "clade"); jsDropDownCarryOver(dy, "org"); dyStringAppend(dy, " document.hiddenForm.db.value=0;"); return jsOnChangeEnd(&dy); } static void saveOnChangeOtherFunction(int graphRows, int graphCols, boolean cgaOnly) /* Write out Javascript function to save vars in hidden * form and submit. */ { struct dyString *dy = dyStringNew(4096); dyStringAppend(dy, "var submitted=false;\n" "function changeOther()\n" "{\n" "if (!submitted)\n" " {\n" " submitted=true;\n" " "); addThresholdGraphCarries(dy, graphRows, graphCols, cgaOnly); jsDropDownCarryOver(dy, "clade"); jsDropDownCarryOver(dy, "org"); jsDropDownCarryOver(dy, "db"); dyStringAppend(dy, "document.hiddenForm.submit();\n"); dyStringAppend(dy, " }\n" "}\n" ); jsInline(dy->string); } static char *onChangeOther() /* Return javascript executed when they change database. */ { return "changeOther();"; } boolean renderGraphic(struct sqlConnection *conn, char *psOutput) /* draw just the graphic */ { struct genoLay *gl; int graphRows = linesOfGraphs(); int graphCols = graphsPerLine(); boolean result = FALSE; if (ggList != NULL) { /* Get genome layout. This can fail so it is wrapped in an error * catcher. */ struct errCatch *errCatch = errCatchNew(); if (errCatchStart(errCatch)) { gl = ggLayout(conn, graphRows, graphCols); /* Draw picture. Enclose in table to add a couple of pixels between * it and controls on IE. */ genomeGif(conn, gl, graphRows, graphCols, graphHeight()+betweenRowPad, psOutput); result = TRUE; } errCatchEnd(errCatch); if (errCatch->gotError) warn("%s", errCatch->message->string); errCatchFree(&errCatch); } else { hPrintf("<BR><B>No graph data is available for this assembly. " "Upload your own data or import from a table or custom track.</B>"); } return result; } void handlePostscript(struct sqlConnection *conn) /* Deal with Postscript output. */ { struct tempName psTn; char *pdfFile = NULL; trashDirFile(&psTn, "hgg", "hgg", ".eps"); cartWebStart(cart, database, "%s Genome Graphs", genome); printf("<H1>PDF Output</H1>\n"); boolean result = renderGraphic(conn, psTn.forCgi); if (result) { printf("EPS (PostScript) output has been discontinued in pursuit of additional features\n"); printf("that are not PostScript-compatible. If you require PostScript output for your\n"); printf("workflow, please <a href='https://genome.ucsc.edu/contacts.html'>reach out to us</a>\n"); printf("and let us know what your needs are - we may be able to help.\n"); pdfFile = convertEpsToPdf(psTn.forCgi); if(pdfFile != NULL) { printf("<BR><BR>PDF can be viewed with Adobe Acrobat Reader.<BR>\n"); printf("<A TARGET=_blank HREF=\"%s\">Click here</A> " "to download the current browser graphic in PDF.", pdfFile); } else printf("<BR><BR>PDF format not available"); freez(&pdfFile); } printf("<BR><BR><A HREF=\"../cgi-bin/hgGenome\">Return</A>"); cartWebEnd(); } void mainPage(struct sqlConnection *conn) /* Do main page of application: hotlinks bar, controls, graphic. */ { int graphRows = linesOfGraphs(); int graphCols = graphsPerLine(); int i, j; int realCount = 0; char *scriptName = "../cgi-bin/hgGenome"; cartWebStart(cart, database, "%s Genome Graphs", genome); /* Start form and save session var. */ hPrintf("<FORM ACTION=\"%s\" NAME=\"mainForm\" METHOD=GET>\n", scriptName); cartSaveSession(cart); /* it might be a non-chrom based assembly */ // defined externally in import.c at the moment extern struct slName *getChroms(); /* Get a chrom list. */ struct slName *chromList = getChroms(); int count = slCount(chromList); boolean cgaOnly = FALSE; if ((count > 500) || (count == 0)) { cgaOnly = TRUE; } jsInit(); saveOnChangeOtherFunction(graphRows, graphCols, cgaOnly); char *jsOther = onChangeOther(); /* Print clade, genome and assembly line. */ hPrintf("<TABLE>"); boolean gotClade = hGotClade(); if (gotClade) { hPrintf("<TR><TD><B>clade:</B>\n"); printCladeListHtml(hGenome(database), "change", onChangeClade(graphRows, graphCols, cgaOnly)); htmlNbSpaces(3); hPrintf("<B>genome:</B>\n"); printGenomeListForCladeHtml(database, "change", onChangeOrg(graphRows, graphCols, cgaOnly)); } else { hPrintf("<TR><TD><B>genome:</B>\n"); printGenomeListHtml(database, "change", onChangeOrg(graphRows, graphCols, cgaOnly)); } htmlNbSpaces(3); hPrintf("<B>assembly:</B>\n"); printAssemblyListHtml(database, "change", jsOther); hPrintf("</TD></TR>\n"); hPrintf("</TABLE>"); if (cgaOnly) { if (count > 500) /* Too-many-chrom assembly */ { warn("Sorry, too many chromosomes. " "This one has %d. Please select another organism or assembly.", count); } else if (count == 0) /* non-chrom assembly */ { warn("Sorry, can only do genome layout on assemblies mapped to chromosomes. " "This one has no usable chromosomes. Please select another organism or assembly."); } } else { /* Draw graph controls. */ hPrintf("<TABLE>"); for (i=0; i<graphRows; ++i) { hPrintf("<TR>"); hPrintf("<TD><B>graph</B></TD>"); for (j=0; j<graphCols; ++j) { char *varName = graphVarName(i,j); char *curVal = cartUsualString(cart, varName, ""); if (curVal[0] != 0) ++realCount; hPrintf("<TD>"); graphDropdown(conn, varName, curVal, "change", jsOther); hPrintf(" <B>in</B> "); colorDropdown(i, j, "change", jsOther); if (j != graphCols-1) hPrintf(","); hPrintf("</TD>"); } hPrintf("</TR>"); } hPrintf("</TABLE>"); cgiMakeButton(hggUpload, "upload"); hPrintf(" "); cgiMakeButton(hggImport, "import"); hPrintf(" "); cgiMakeButton(hggConfigure, "configure"); hPrintf(" "); cgiMakeOptionalButton(hggCorrelate, "correlate", realCount < 2); hPrintf(" <B>significance threshold:</B>"); hPrintf("<INPUT TYPE=\"TEXT\" NAME=\"%s\" id='%s' SIZE=\"%d\" VALUE=\"%g\">" , getThresholdName(), getThresholdName(), 3, getThreshold()); jsOnEventById("change" , getThresholdName(), "changeOther();"); jsOnEventById("keypress", getThresholdName(), "return submitOnEnter(event,document.mainForm);"); hPrintf(" "); cgiMakeOptionalButton(hggBrowse, "browse regions", realCount == 0); hPrintf(" "); cgiMakeOptionalButton(hggSort, "sort genes", realCount == 0 || !hgNearOk(database)); hPrintf("<BR>"); hPrintf("<TABLE CELLPADDING=2><TR><TD>\n"); boolean result = renderGraphic(conn, NULL); hPrintf("</TD></TR></TABLE>\n"); if (result) /* Write a little click-on-help */ hPrintf("<i>Click on a chromosome to open Genome Browser at that position.</i>"); } /* end else non-chrom assembly */ hPrintf("</FORM>\n"); /* Hidden form - fo the benefit of javascript. */ { /* Copy over both the regular, non-changing variables, and * also all the source/color pairs that depend on the * configuration. */ static char *regularVars[] = { "clade", "org", "db", }; int regularCount = ArraySize(regularVars); int varCount = regularCount + 1 + 2 * graphRows * graphCols; int varIx = regularCount; char **allVars; AllocArray(allVars, varCount); CopyArray(regularVars, allVars, regularCount); allVars[varIx++] = cloneString(getThresholdName()); for (i=0; i<graphRows; ++i) { for (j=0; j<graphCols; ++j) { allVars[varIx++] = cloneString(graphVarName(i,j)); allVars[varIx++] = cloneString(graphColorVarName(i,j)); } } jsCreateHiddenForm(cart, scriptName, allVars, varCount); } webNewSection("Using Genome Graphs"); printMainHelp(); cartWebEnd(); }