533112afe2a2005e80cdb1f82904ea65032d4302 braney Sat Oct 2 11:37:34 2021 -0700 split hg/lib into two separate libaries, one only used by the cgis diff --git src/hg/cgilib/hvGfx.c src/hg/cgilib/hvGfx.c new file mode 100644 index 0000000..994beb6 --- /dev/null +++ src/hg/cgilib/hvGfx.c @@ -0,0 +1,301 @@ +/* hvGfx - browser graphics interface. This is a thin layer on top of vGfx + * providing genome browser-specific features. It was added to handle + * reverse-complement display. */ + +/* Copyright (C) 2014 The Regents of the University of California + * See README in this or parent directory for licensing information. */ + +#include "common.h" +#include "hvGfx.h" +#include "obscure.h" + + +static struct hvGfx *hvGfxAlloc(struct vGfx *vg) +/* allocate a hvgGfx object */ +{ +struct hvGfx *hvg; +AllocVar(hvg); +hvg->vg = vg; +hvg->pixelBased = vg->pixelBased; +hvg->width = vg->width; +hvg->height = vg->height; +hvg->clipMaxX = vg->width; +hvg->clipMaxY = vg->height; +return hvg; +} + +struct hvGfx *hvGfxOpenPng(int width, int height, char *fileName, boolean useTransparency) +/* Open up something that we'll write out as a PNG someday. */ +{ +return hvGfxAlloc(vgOpenPng(width, height, fileName, useTransparency)); +} + +struct hvGfx *hvGfxOpenPostScript(int width, int height, char *fileName) +/* Open up something that will someday be a PostScript file. */ +{ +return hvGfxAlloc(vgOpenPostScript(width, height, fileName)); +} + +void hvGfxClose(struct hvGfx **pHvg) +/* Close down virtual graphics object, and finish writing it to file. */ +{ +struct hvGfx *hvg = *pHvg; +if (hvg != NULL) + { + vgClose(&hvg->vg); + freez(pHvg); + } +} + +void hvGfxMakeColorGradient(struct hvGfx *hvg, + struct rgbColor *start, struct rgbColor *end, + int steps, Color *colorIxs) +/* Make a color gradient that goes smoothly from start to end colors in given + * number of steps. Put indices in color table in colorIxs */ +{ +double scale = 0, invScale; +double invStep; +int i; +int r,g,b; + +steps -= 1; /* Easier to do the calculation in an inclusive way. */ +invStep = 1.0/steps; +for (i=0; i<=steps; ++i) + { + invScale = 1.0 - scale; + r = invScale * start->r + scale * end->r; + g = invScale * start->g + scale * end->g; + b = invScale * start->b + scale * end->b; + colorIxs[i] = hvGfxFindColorIx(hvg, r, g, b); + scale += invStep; + } +} + +static long figureTickSpan(long totalLength, int maxNumTicks) +/* Figure out whether ticks on ruler should be 1, 5, 10, 50, 100, 500, + * 1000, etc. units apart. */ +{ +int roughTickLen = totalLength/maxNumTicks; +int i; +int tickLen = 1; + +for (i=0; i<9; ++i) + { + if (roughTickLen < tickLen) + return tickLen; + tickLen *= 5; + if (roughTickLen < tickLen) + return tickLen; + tickLen *= 2; + } +return 1000000000; +} + +void hvGfxDrawRulerBumpText(struct hvGfx *hvg, int xOff, int yOff, + int height, int width, + Color color, MgFont *font, + int startNum, int range, int bumpX, int bumpY) +/* Draw a ruler inside the indicated part of mg with numbers that start at + * startNum and span range. Bump text positions slightly. */ +{ +int tickSpan; +int tickPos; +double scale; +int firstTick; +int remainder; +int end = startNum + range; +int x; +char tbuf[18]; +int numWid; +int goodNumTicks; +int niceNumTicks = width/35; + +sprintLongWithCommas(tbuf, startNum+range); +numWid = mgFontStringWidth(font, tbuf)+4+bumpX; +goodNumTicks = width/numWid; +if (goodNumTicks < 1) goodNumTicks = 1; +if (goodNumTicks > niceNumTicks) goodNumTicks = niceNumTicks; + +tickSpan = figureTickSpan(range, goodNumTicks); + +scale = (double)width / range; + +firstTick = startNum + tickSpan; +remainder = firstTick % tickSpan; +firstTick -= remainder; +for (tickPos=firstTick; tickPos<end; tickPos += tickSpan) + { + sprintLongWithCommas(tbuf, tickPos); + numWid = mgFontStringWidth(font, tbuf)+4; + x = (int)((tickPos-startNum) * scale) + xOff; + hvGfxBox(hvg, x, yOff, 1, height, color); + if (x - numWid >= xOff) + { + hvGfxTextCentered(hvg, x-numWid + bumpX, yOff + bumpY, numWid, + height, color, font, tbuf); + } + } +} + +void hvGfxDrawRuler(struct hvGfx *hvg, int xOff, int yOff, int height, int width, + Color color, MgFont *font, + int startNum, int range) +/* Draw a ruler inside the indicated part of mg with numbers that start at + * startNum and span range. */ +{ +hvGfxDrawRulerBumpText(hvg, xOff, yOff, height, width, color, font, + startNum, range, 0, 0); +} + + +void hvGfxBarbedHorizontalLine(struct hvGfx *hvg, int x, int y, + int width, int barbHeight, int barbSpacing, int barbDir, Color color, + boolean needDrawMiddle) +/* Draw a horizontal line starting at xOff, yOff of given width. Will + * put barbs (successive arrowheads) to indicate direction of line. + * BarbDir of 1 points barbs to right, of -1 points them to left. */ +{ +barbDir = (hvg->rc) ? -barbDir : barbDir; +if (barbDir == 0) + return; // fully clipped, or no barbs +x = hvGfxAdjXW(hvg, x, width); +int x1, x2; +int yHi, yLo; +int offset, startOffset, endOffset, barbAdd; +int scrOff = (barbSpacing - 1) - (x % (barbSpacing)); + +yHi = y + barbHeight; +yLo = y - barbHeight; +if (barbDir < 0) + { + startOffset = scrOff - barbHeight; + startOffset = (startOffset >= 0) ?startOffset : 0; + endOffset = width - barbHeight; + barbAdd = barbHeight; + } +else + { + startOffset = scrOff + barbHeight; + endOffset = width; + barbAdd = -barbHeight; + } + +for (offset = startOffset; offset < endOffset; offset += barbSpacing) + { + x1 = x + offset; + x2 = x1 + barbAdd; + vgLine(hvg->vg, x1, y, x2, yHi, color); + vgLine(hvg->vg, x1, y, x2, yLo, color); + } +} + +void hvGfxNextItemButton(struct hvGfx *hvg, int x, int y, int w, int h, + Color color, Color hvgColor, boolean nextItem) +/* Draw a button that looks like a fast-forward or rewind button on */ +/* a remote control. If nextItem is TRUE, it points right, otherwise */ +/* left. color is the outline color, and hvgColor is the fill color. */ +{ +x = hvGfxAdjXW(hvg, x, w); +if (hvg->rc) + nextItem = !nextItem; + +struct gfxPoly *t1, *t2; +/* Make the triangles */ +t1 = gfxPolyNew(); +t2 = gfxPolyNew(); +if (nextItem) + /* point right. */ + { + gfxPolyAddPoint(t1, x, y); + gfxPolyAddPoint(t1, x+w/2, y+h/2); + gfxPolyAddPoint(t1, x, y+h); + gfxPolyAddPoint(t2, x+w/2, y); + gfxPolyAddPoint(t2, x+w, y+h/2); + gfxPolyAddPoint(t2, x+w/2, y+h); + } +else + /* point left. */ + { + gfxPolyAddPoint(t1, x, y+h/2); + gfxPolyAddPoint(t1, x+w/2, y); + gfxPolyAddPoint(t1, x+w/2, y+h); + gfxPolyAddPoint(t2, x+w/2, y+h/2); + gfxPolyAddPoint(t2, x+w, y); + gfxPolyAddPoint(t2, x+w, y+h); + } +/* The two filled triangles. */ +vgDrawPoly(hvg->vg, t1, hvgColor, TRUE); +vgDrawPoly(hvg->vg, t2, hvgColor, TRUE); +/* The two outline triangles. */ +vgDrawPoly(hvg->vg, t1, color, FALSE); +vgDrawPoly(hvg->vg, t2, color, FALSE); +gfxPolyFree(&t1); +gfxPolyFree(&t2); +} + +void hvGfxDottedLine(struct hvGfx *hvg, int x1, int y1, int x2, int y2, Color color, boolean isDash) +/* Brezenham line algorithm, alternating dots, by 1 pixel or two (isDash true) */ +{ +int duty_cycle; +int incy; +int delta_x, delta_y; +int dots; +int dotFreq = (isDash ? 3 : 2); +delta_y = y2-y1; +delta_x = x2-x1; +if (delta_y < 0) + { + delta_y = -delta_y; + incy = -1; + } +else + { + incy = 1; + } +if (delta_x < 0) + { + delta_x = -delta_x; + incy = -incy; + x1 = x2; + y1 = y2; + } +duty_cycle = (delta_x - delta_y)/2; +if (delta_x >= delta_y) + { + dots = delta_x+1; + while (--dots >= 0) + { + if (dots % dotFreq) + hvGfxDot(hvg,x1,y1,color); + duty_cycle -= delta_y; + x1 += 1; + if (duty_cycle < 0) + { + duty_cycle += delta_x; /* update duty cycle */ + y1+=incy; + } + } + } +else + { + dots = delta_y+1; + while (--dots >= 0) + { + if (dots % dotFreq) + hvGfxDot(hvg,x1,y1,color); + duty_cycle += delta_x; + y1+=incy; + if (duty_cycle > 0) + { + duty_cycle -= delta_y; /* update duty cycle */ + x1 += 1; + } + } + } +} + +void hvGfxSetFontMethod(struct hvGfx *hvg, unsigned int method, char *fontName, char *fontFile) +/* Use the Free Type library to draw fonts. */ +{ +vgSetFontMethod(hvg->vg, method, fontName, fontFile); +}