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);
 }