From ace1948817becc602079fb95bee45db7266d2142 Mon Sep 17 00:00:00 2001 From: Joel Bodenmann Date: Sun, 8 Dec 2013 21:13:54 +0100 Subject: [PATCH] added gdispGDrawThickLine() --- include/gdisp/gdisp.h | 18 ++++++ releases.txt | 1 + src/gdisp/gdisp.c | 125 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+) diff --git a/include/gdisp/gdisp.h b/include/gdisp/gdisp.h index 99b1a504..ef5115a9 100644 --- a/include/gdisp/gdisp.h +++ b/include/gdisp/gdisp.h @@ -624,6 +624,24 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co */ void gdispGFillConvexPoly(GDisplay *g, coord_t tx, coord_t ty, const point *pntarray, unsigned cnt, color_t color); #define gdispFillConvexPoly(x,y,p,i,c) gdispGFillConvexPoly(GDISP,x,y,p,i,c) + + /** + * @brief Draw a line with a specified thickness + * @details The line thickness is specified in pixels. The line ends can + * be selected to be either flat or round. + * @note Uses gdispGFillConvexPoly() internally to perform the drawing. + * + * @param[in] g The display to use + * @param[in] x0,y0 The start position + * @param[in] x1,y1 The end position + * @param[in] color The color to use + * @param[in] width The width of the line + * @param[in] round Use round ends for the line + * + * @api + */ + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round); + #define gdispDrawThickLine(x0,y0,x1,y1,c,w,r) gdispGDrawThickLine(GDISP,x0,y0,x1,y1,c,w,r) #endif /* Text Functions */ diff --git a/releases.txt b/releases.txt index e8ac0e03..85d7c506 100644 --- a/releases.txt +++ b/releases.txt @@ -6,6 +6,7 @@ current release: 2.0 FIX: Significant improvements to the way the MCU touch driver works. FEATURE: Add support for edge to edge touch calibration. FEATURE: Added progressbar widget +FEATURE: Added gdispGDrawThickLine() by user jpa- *** changes after 1.9 *** diff --git a/src/gdisp/gdisp.c b/src/gdisp/gdisp.c index 1eb5a41b..b625f8a7 100644 --- a/src/gdisp/gdisp.c +++ b/src/gdisp/gdisp.c @@ -2565,6 +2565,131 @@ void gdispGDrawBox(GDisplay *g, coord_t x, coord_t y, coord_t cx, coord_t cy, co } } } + + static int32_t rounding_div(const int32_t n, const int32_t d) + { + if ((n < 0) != (d < 0)) + return (n - d/2) / d; + else + return (n + d/2) / d; + } + + void gdispGDrawThickLine(GDisplay *g, coord_t x0, coord_t y0, coord_t x1, coord_t y1, color_t color, coord_t width, bool_t round) { + coord_t dx, dy, nx, ny; + + /* Compute the direction vector for the line */ + dx = x1 - x0; + dy = y1 - y0; + + /* Compute vector for the normal of the line */ + nx = dy; + ny = -dx; + + /* Normalize the normal vector to length width. + * This uses Newton-Rhapson to avoid the need for floating point sqrt. + * We try to solve f(div) = div^2 - len/width^2. + */ + { + int32_t div, len, tmp; + uint8_t i; + + len = nx*nx + ny*ny; + div = 100; /* Initial guess for divider; not critical */ + + for (i = 0; i < 5; i++) { + tmp = width * width * div; + div -= (tmp * div - len) / (2 * tmp); + } + + nx = rounding_div(nx, div); + ny = rounding_div(ny, div); + } + + /* Offset the x0,y0 by half the width of the line. This way we + * can keep the width of the line accurate even if it is not evenly + * divisible by 2. + */ + { + x0 -= rounding_div(nx, 2); + y0 -= rounding_div(ny, 2); + } + + /* Fill in the point array */ + if (!round) { + /* We use 4 points for the basic line shape: + * + * pt1 pt2 + * (+n) ------------------------------------ (d+n) + * | | + * (0,0) ----------------------------------- (d) + * pt0 pt3 + */ + point pntarray[4]; + + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx; + pntarray[1].y = ny; + pntarray[2].x = dx + nx; + pntarray[2].y = dy + ny; + pntarray[3].x = dx; + pntarray[3].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 4, color); + } else { + /* We use 4 points for basic shape, plus 4 extra points for ends: + * + * pt3 ------------------ pt4 + * / \ + * pt2 pt5 + * | | + * pt1 pt6 + * \ / + * pt0 -------------------pt7 + */ + point pntarray[8]; + coord_t nx2, ny2; + + /* Magic numbers: + * 75/256 = sin(45) / (1 + sqrt(2)) diagonal octagon segments + * 106/256 = 1 / (1 + sqrt(2)) octagon side + * 53/256 = 0.5 / (1 + sqrt(2)) half of octagon side + * 150/256 = 1 - 1 / (1 + sqrt(2)) octagon height minus one side + */ + + /* Rotate the normal vector 45 deg counter-clockwise and reduce + * to 1 / (1 + sqrt(2)) length, for forming octagonal ends. */ + nx2 = rounding_div((nx * 75 + ny * 75), 256); + ny2 = rounding_div((-nx * 75 + ny * 75), 256); + + /* Offset and extend the line so that the center of the octagon + * is at the specified points. */ + x0 += ny * 53 / 256; + y0 -= nx * 53 / 256; + dx -= ny * 106 / 256; + dy += nx * 106 / 256; + + /* Now fill in the points by summing the calculated vectors. */ + pntarray[0].x = 0; + pntarray[0].y = 0; + pntarray[1].x = nx2; + pntarray[1].y = ny2; + pntarray[2].x = nx2 + nx * 106/256; + pntarray[2].y = ny2 + ny * 106/256; + pntarray[3].x = nx; + pntarray[3].y = ny; + pntarray[4].x = dx + nx; + pntarray[4].y = dy + ny; + pntarray[5].x = dx + nx - nx2; + pntarray[5].y = dy + ny - ny2; + pntarray[6].x = dx + nx * 150/256 - nx2; + pntarray[6].y = dy + ny * 150/256 - ny2; + pntarray[7].x = dx; + pntarray[7].y = dy; + + gdispGFillConvexPoly(g, x0, y0, pntarray, 8, color); + } + } #endif #if GDISP_NEED_TEXT