b1ae6aafe2f0997af978f7984c270fbe3c1edb4a kate Wed Jan 17 17:40:46 2018 -0800 Add feature to draw dotted lines for reverse direction interactions. refs #17512 diff --git src/hg/lib/hvGfx.c src/hg/lib/hvGfx.c index a52ab67..142e42e 100644 --- src/hg/lib/hvGfx.c +++ src/hg/lib/hvGfx.c @@ -253,142 +253,241 @@ struct memPng *png = (struct memPng *)hvg->vg->data; struct memGfx *img = (struct memGfx *)png; if ((x < img->clipMinX) || (x > img->clipMaxX) || (y < img->clipMinY) || (y > img->clipMaxY)) return; Color *pt = _mgPixAdr(img,x,y); float invFrac = 1 - frac; int r = COLOR_32_RED(*pt) * invFrac + COLOR_32_RED(col) * frac; int g = COLOR_32_GREEN(*pt) * invFrac + COLOR_32_GREEN(col) * frac; int b = COLOR_32_BLUE(*pt) * invFrac + COLOR_32_BLUE(col) * frac; hvGfxDot(hvg, x, y, MAKECOLOR_32(r,g,b)); hvGfxDot(hvg, x, y, MAKECOLOR_32(r,g,b)); } -void hvGfxCurveSegAA(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, Color color) +void hvGfxDottedLine(struct hvGfx *hvg, int x1, int y1, int x2, int y2, Color color) +/* Brezenham line algorithm, alternating dots */ +{ +int duty_cycle; +int incy; +int delta_x, delta_y; +int dots; +delta_y = y2-y1; +delta_x = x2-x1; +boolean putDot = TRUE; +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 (putDot) + { + hvGfxDot(hvg,x1,y1,color); + putDot = FALSE; + } + else + putDot = TRUE; + // try putDot = !putDot; + 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 (putDot) + { + hvGfxDot(hvg,x1,y1,color); + putDot = FALSE; + } + else + putDot = TRUE; + duty_cycle += delta_x; + y1+=incy; + if (duty_cycle > 0) + { + duty_cycle -= delta_y; /* update duty cycle */ + x1 += 1; + } + } + } +} + +void hvGfxCurveSegAA(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, + Color color, boolean isDotted) /* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier) + * Optionally alternate dots. * Adapted trivially from code posted on github and at http://members.chello.at/~easyfilter/bresenham.html */ /* Thanks to author * @author Zingl Alois * @date 22.08.2016 */ { int sx = x2-x1, sy = y2-y1; long xx = x0-x1, yy = y0-y1, xy; /* relative values for checks */ double dx, dy, err, ed, cur = xx*sy-yy*sx; /* curvature */ assert(xx*sx <= 0 && yy*sy <= 0); /* sign of gradient must not change */ if (sx*(long)sx+sy*(long)sy > xx*xx+yy*yy) { /* begin with longer part */ x2 = x0; x0 = sx+x1; y2 = y0; y0 = sy+y1; cur = -cur; /* swap P0 P2 */ } if (cur != 0) { /* no straight line */ xx += sx; xx *= sx = x0 < x2 ? 1 : -1; /* x step direction */ yy += sy; yy *= sy = y0 < y2 ? 1 : -1; /* y step direction */ xy = 2*xx*yy; xx *= xx; yy *= yy; /* differences 2nd degree */ if (cur*sx*sy < 0) { /* negated curvature? */ xx = -xx; yy = -yy; xy = -xy; cur = -cur; } dx = 4.0*sy*(x1-x0)*cur+xx-xy; /* differences 1st degree */ dy = 4.0*sx*(y0-y1)*cur+yy-xy; xx += xx; yy += yy; err = dx+dy+xy; /* error 1st step */ + boolean putDot = TRUE; do { cur = fmin(dx+xy,-xy-dy); ed = fmax(dx+xy,-xy-dy); /* approximate error distance */ ed += 2*ed*cur*cur/(4*ed*ed+cur*cur); + if (isDotted && !putDot) { + putDot = TRUE; + } else { mixDot(hvg, x0,y0, 1-fabs(err-dx-dy-xy)/ed, color); /* plot curve */ + putDot = FALSE; + } if (x0 == x2 || y0 == y2) break; /* last pixel -> curve finished */ x1 = x0; cur = dx-err; y1 = 2*err+dy < 0; if (2*err+dx > 0) { /* x step */ if (err-dy < ed) mixDot(hvg, x0,y0+sy, 1-fabs(err-dy)/ed, color); x0 += sx; dx -= xy; err += dy += yy; } if (y1) { /* y step */ if (cur < ed) mixDot(hvg, x1+sx,y0, 1-fabs(cur)/ed, color); y0 += sy; dy -= xy; err += dx += xx; } } while (dy < dx); /* gradient negates -> close curves */ } hvGfxLine(hvg, x0,y0, x2,y2, color); /* plot remaining needle to end */ } -void hvGfxCurve(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, Color color) +void hvGfxCurve(struct hvGfx *hvg, int x0, int y0, int x1, int y1, int x2, int y2, + Color color, boolean isDotted) /* Draw a segment of an anti-aliased curve within 3 points (quadratic Bezier) + * Optionally alternate dots. * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html */ { int x = x0-x1, y = y0-y1; double t = x0-2*x1+x2, r; if ((long)x*(x2-x1) > 0) { /* horizontal cut at P4? */ if ((long)y*(y2-y1) > 0) /* vertical cut at P6 too? */ if (fabs((y0-2*y1+y2)/t*x) > abs(y)) { /* which first? */ x0 = x2; x2 = x+x1; y0 = y2; y2 = y+y1; /* swap points */ } /* now horizontal cut at P4 comes first */ t = (x0-x1)/t; r = (1-t)*((1-t)*y0+2.0*t*y1)+t*t*y2; /* By(t=P4) */ t = (x0*x2-x1*x1)*t/(x0-x1); /* gradient dP4/dx=0 */ x = floor(t+0.5); y = floor(r+0.5); r = (y1-y0)*(t-x0)/(x1-x0)+y0; /* intersect P3 | P0 P1 */ - hvGfxCurveSegAA(hvg,x0,y0, x,floor(r+0.5), x,y, color); + hvGfxCurveSegAA(hvg,x0,y0, x,floor(r+0.5), x,y, color, isDotted); r = (y1-y2)*(t-x2)/(x1-x2)+y2; /* intersect P4 | P1 P2 */ x0 = x1 = x; y0 = y; y1 = floor(r+0.5); /* P0 = P4, P1 = P8 */ } if ((long)(y0-y1)*(y2-y1) > 0) { /* vertical cut at P6? */ t = y0-2*y1+y2; t = (y0-y1)/t; r = (1-t)*((1-t)*x0+2.0*t*x1)+t*t*x2; /* Bx(t=P6) */ t = (y0*y2-y1*y1)*t/(y0-y1); /* gradient dP6/dy=0 */ x = floor(r+0.5); y = floor(t+0.5); r = (x1-x0)*(t-y0)/(y1-y0)+x0; /* intersect P6 | P0 P1 */ - hvGfxCurveSegAA(hvg,x0,y0, floor(r+0.5),y, x,y, color); + hvGfxCurveSegAA(hvg,x0,y0, floor(r+0.5),y, x,y, color, isDotted); r = (x1-x2)*(t-y2)/(y1-y2)+x2; /* intersect P7 | P1 P2 */ x0 = x; x1 = floor(r+0.5); y0 = y1 = y; /* P0 = P6, P1 = P7 */ } - hvGfxCurveSegAA(hvg,x0,y0, x1,y1, x2,y2, color); /* remaining part */ + hvGfxCurveSegAA(hvg,x0,y0, x1,y1, x2,y2, color, isDotted); /* remaining part */ } -void hvGfxEllipseDraw(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color, int mode) +void hvGfxEllipseDraw(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color, + int mode, boolean isDotted) /* Draw an ellipse (or limit to top or bottom) specified by rectangle, using Bresenham algorithm. + * Optionally, alternate dots. * Point 0 is left, point 1 is top of rectangle * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html */ { int a = abs(x1-x0), b = abs(y1-y0), b1 = b&1; /* values of diameter */ long dx = 4*(1-a)*b*b, dy = 4*(b1+1)*a*a; /* error increment */ long err = dx+dy+b1*a*a, e2; /* error of 1.step */ if (x0 > x1) { x0 = x1; x1 += a; } /* if called with swapped points */ if (y0 > y1) y0 = y1; /* .. exchange them */ y0 += (b+1)/2; y1 = y0-b1; /* starting pixel */ a *= 8*a; b1 = 8*b*b; + boolean putDot = TRUE; do { - if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) { + if (isDotted && !putDot) + putDot = TRUE; + else + { + if (mode == ELLIPSE_BOTTOM || mode == ELLIPSE_FULL) + { hvGfxDot(hvg, x1, y0, color); /* I. Quadrant */ hvGfxDot(hvg, x0, y0, color); /* II. Quadrant */ } - if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) { + if (mode == ELLIPSE_TOP || mode == ELLIPSE_FULL) + { hvGfxDot(hvg, x0, y1, color); /* III. Quadrant */ hvGfxDot(hvg, x1, y1, color); /* IV. Quadrant */ } + putDot = FALSE; + } e2 = 2*err; if (e2 <= dy) { y0++; y1--; err += dy += a; } /* y step */ if (e2 >= dx || 2*err > dy) { x0++; x1--; err += dx += b1; } /* x step */ } while (x0 <= x1); + putDot = TRUE; while (y0-y1 < b) { /* too early stop of flat ellipses a=1 */ + if (isDotted && !putDot) + putDot = TRUE; + else + { hvGfxDot(hvg, x0-1, y0, color); /* -> finish tip of ellipse */ hvGfxDot(hvg, x1+1, y0++, color); hvGfxDot(hvg, x0-1, y1, color); hvGfxDot(hvg, x1+1, y1--, color); } } +} void hvGfxEllipse(struct hvGfx *hvg, int x0, int y0, int x1, int y1, Color color) /* Draw a full ellipse specified by rectangle, using Bresenham algorithm. * Point 0 is left, point 1 is top of rectangle * Adapted trivially from code posted at http://members.chello.at/~easyfilter/bresenham.html */ { -hvGfxEllipseDraw(hvg, x0, y0, x1, y1, color, ELLIPSE_FULL); +hvGfxEllipseDraw(hvg, x0, y0, x1, y1, color, ELLIPSE_FULL, FALSE); }