diff --git a/cppsrc/U8g2lib.h b/cppsrc/U8g2lib.h index 9303377d..c9c83101 100644 --- a/cppsrc/U8g2lib.h +++ b/cppsrc/U8g2lib.h @@ -239,6 +239,9 @@ class U8G2 void drawEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt = U8G2_DRAW_ALL) { u8g2_DrawEllipse(&u8g2, x0, y0, rx, ry, opt); } void drawFilledEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt = U8G2_DRAW_ALL) { u8g2_DrawFilledEllipse(&u8g2, x0, y0, rx, ry, opt); } + /* u8g2_arc.c */ + void drawArc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t start, uint8_t end) { u8g2_DrawArc(&u8g2, x0, y0, rad, start, end); } + /* u8g2_line.c */ void drawLine(u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2) { u8g2_DrawLine(&u8g2, x1, y1, x2, y2); } diff --git a/csrc/u8g2.h b/csrc/u8g2.h index a8097a4d..9b617692 100644 --- a/csrc/u8g2.h +++ b/csrc/u8g2.h @@ -1695,6 +1695,12 @@ void u8g2_DrawDisc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad void u8g2_DrawEllipse(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t option); void u8g2_DrawFilledEllipse(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t option); + +/*==========================================*/ +/* u8g2_arc.c */ +void u8g2_DrawArc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t start, uint8_t end); + + /*==========================================*/ /* u8g2_line.c */ void u8g2_DrawLine(u8g2_t *u8g2, u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2); diff --git a/csrc/u8g2_arc.c b/csrc/u8g2_arc.c new file mode 100644 index 00000000..336bb179 --- /dev/null +++ b/csrc/u8g2_arc.c @@ -0,0 +1,105 @@ +/* + + u8g2_arc.c + + Universal 8bit Graphics Library (https://github.com/olikraus/u8g2/) + + Copyright (c) 2023, olikraus@gmail.com + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "u8g2.h" + + +static void u8g2_draw_arc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t start, uint8_t end) +{ + // Manage angle inputs + uint8_t full = (start == end); + uint8_t inverted = (start > end); + uint8_t a_start = inverted ? end : start; + uint8_t a_end = inverted ? start : end; + + // Initialize variables + uint32_t ratio; + u8g2_int_t x = 0; + u8g2_int_t y = rad; + u8g2_int_t d = rad - 1; + + // Trace arc radius with the Andres circle algorithm (process each pixel of a 1/8th circle of radius rad) + while (y >= x) + { + // Get the percentage of 1/8th circle drawn with a fast approximation of arctan(x/y) + ratio = x * 255 / y; // x/y [0..255] + ratio = ratio * (770195 - (ratio - 255) * (ratio + 941)) / 6137491; // arctan(x/y) [0..32] + + // Fill the pixels of the 8 sections of the circle, but only on the arc defined by the angles (start and end) + if(full || ((ratio >= a_start && ratio < a_end) ^ inverted)) u8g2_DrawPixel(u8g2, x0 + y, y0 - x); + if(full || (((ratio + a_end) > 63 && (ratio + a_start) <= 63) ^ inverted)) u8g2_DrawPixel(u8g2, x0 + x, y0 - y); + if(full || (((ratio + 64) >= a_start && (ratio + 64) < a_end) ^ inverted)) u8g2_DrawPixel(u8g2, x0 - x, y0 - y); + if(full || (((ratio + a_end) > 127 && (ratio + a_start) <= 127) ^ inverted)) u8g2_DrawPixel(u8g2, x0 - y, y0 - x); + if(full || (((ratio + 128) >= a_start && (ratio + 128) < a_end) ^ inverted)) u8g2_DrawPixel(u8g2, x0 - y, y0 + x); + if(full || (((ratio + a_end) > 191 && (ratio + a_start) <= 191) ^ inverted)) u8g2_DrawPixel(u8g2, x0 - x, y0 + y); + if(full || (((ratio + 192) >= a_start && (ratio + 192) < a_end) ^ inverted)) u8g2_DrawPixel(u8g2, x0 + x, y0 + y); + if(full || (((ratio + a_end) > 255 && (ratio + a_start) <= 255) ^ inverted)) u8g2_DrawPixel(u8g2, x0 + y, y0 + x); + + // Run Andres circle algorithm to get to the next pixel + if (d >= 2 * x) + { + d = d - 2 * x - 1; + x = x + 1; + } + else if (d < 2 * (rad - y)) + { + d = d + 2 * y - 1; + y = y - 1; + } + else + { + d = d + 2 * (y - x - 1); + y = y - 1; + x = x + 1; + } + } +} + +void u8g2_DrawArc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t start, uint8_t end) +{ + /* check for bounding box */ +#ifdef U8G2_WITH_INTERSECTION + { + if ( u8g2_IsIntersection(u8g2, x0-rad, y0-rad, x0+rad+1, y0+rad+1) == 0 ) + return; + } +#endif /* U8G2_WITH_INTERSECTION */ + + + /* draw arc */ + u8g2_draw_arc(u8g2, x0, y0, rad, start, end); +} + + diff --git a/sys/arm-linux/port/U8g2lib.h b/sys/arm-linux/port/U8g2lib.h index 85d47b04..30f6ea97 100644 --- a/sys/arm-linux/port/U8g2lib.h +++ b/sys/arm-linux/port/U8g2lib.h @@ -367,6 +367,12 @@ public: u8g2_DrawFilledEllipse(&u8g2, x0, y0, rx, ry, opt); } + /* u8g2_arc.c */ + void drawArc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, + uint8_t start, uint8_t end) { + u8g2_DrawArc(&u8g2, x0, y0, rad, start, end); + } + /* u8g2_line.c */ void drawLine(u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2) { diff --git a/sys/rt-thread/port/U8g2lib.h b/sys/rt-thread/port/U8g2lib.h index 7b88fd91..a9ae85e6 100644 --- a/sys/rt-thread/port/U8g2lib.h +++ b/sys/rt-thread/port/U8g2lib.h @@ -197,6 +197,9 @@ class U8G2: public Print void drawEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt = U8G2_DRAW_ALL) { u8g2_DrawEllipse(&u8g2, x0, y0, rx, ry, opt); } void drawFilledEllipse(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rx, u8g2_uint_t ry, uint8_t opt = U8G2_DRAW_ALL) { u8g2_DrawFilledEllipse(&u8g2, x0, y0, rx, ry, opt); } + /* u8g2_arc.c */ + void drawArc(u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad, uint8_t start, uint8_t end) { u8g2_DrawArc(&u8g2, x0, y0, rad, start, end); } + /* u8g2_line.c */ void drawLine(u8g2_uint_t x1, u8g2_uint_t y1, u8g2_uint_t x2, u8g2_uint_t y2) { u8g2_DrawLine(&u8g2, x1, y1, x2, y2); } diff --git a/sys/sdl/draw_arc/main.c b/sys/sdl/draw_arc/main.c index 2c513ace..018715fc 100644 --- a/sys/sdl/draw_arc/main.c +++ b/sys/sdl/draw_arc/main.c @@ -10,165 +10,190 @@ #include "u8g2.h" #include -#include - -typedef float (*u8g2_atan2f_t)(float, float); - - -//static const float M_PI_2 = 1.57079632679489661923; -//static const float M_PI_4 = 0.78539816339744830962; - - -float myatan(float a, float b) -{ - return atan2f(a, b); -} - -/* -#define C360 360 -#define C270 270 -#define C180 180 -#define C90 90 -#define C45 45 -*/ - -#define C360 256 -#define C270 192 -#define C180 128 -#define C90 64 -#define C45 32 - - - -void u8g2_draw_arc(u8g2_t *u8g2, u8g2_uint_t x0, u8g2_uint_t y0, u8g2_uint_t rad_in, u8g2_uint_t rad_out, u8g2_uint_t angle_start, u8g2_uint_t angle_end, u8g2_atan2f_t atan2f_func) -{ - // Declare variables - u8g2_long_t x, y, d, r, as, ae, cnt, num_pts; - - // Manage angle inputs - if(angle_start == angle_end) return; - uint8_t inverted = (angle_start > angle_end); - as = inverted ? angle_end : angle_start; - ae = inverted ? angle_start : angle_end; - - // Trace each arc radius with the Andres circle algorithm - for(r = rad_in; r <= rad_out; r++) - { - x = 0; - y = r; - d = r - 1; - cnt = -1; - num_pts = atan2f_func ? 100 : (r * 8 / 10); // if no atan2f() function is provided, we make a low cost approximation of the number of pixels drawn for a 1/8th circle of radius r - - // Process each pixel of a 1/8th circle of radius r - while (y >= x) - { - // If atan2f() function is provided, get the percentage of 1/8th circle drawn, otherwise count the drawn pixels - cnt = atan2f_func ? ((M_PI_2 - atan2f_func(y, x)) * 100 / M_PI_4) : (cnt + 1); - - // Fill the pixels of the 8 sections of the circle, but only on the arc defined by the angles (start and end) - if((cnt > num_pts * as / C45 && cnt <= num_pts * ae / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 + y, y0 - x); - if((cnt > num_pts * (C90 - ae) / C45 && cnt <= num_pts * (C90 - as) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 + x, y0 - y); - if((cnt > num_pts * (as - C90) / C45 && cnt <= num_pts * (ae - C90) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 - x, y0 - y); - if((cnt > num_pts * (C180 - ae) / C45 && cnt <= num_pts * (C180 - as) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 - y, y0 - x); - if((cnt > num_pts * (as - C180) / C45 && cnt <= num_pts * (ae - C180) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 - y, y0 + x); - if((cnt > num_pts * (C270 - ae) / C45 && cnt <= num_pts * (C270 - as) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 - x, y0 + y); - if((cnt > num_pts * (as - C270) / C45 && cnt <= num_pts * (ae - C270) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 + x, y0 + y); - if((cnt > num_pts * (C360 - ae) / C45 && cnt <= num_pts * (C360 - as) / C45) ^ inverted) u8g2_DrawPixel(u8g2, x0 + y, y0 + x); - - // Run Andres circle algorithm to get to the next pixel - if (d >= 2 * x) - { - d = d - 2 * x - 1; - x = x + 1; - } else if (d < 2 * (r - y)) - { - d = d + 2 * y - 1; - y = y - 1; - } else - { - d = d + 2 * (y - x - 1); - y = y - 1; - x = x + 1; - } - } - } -} +#ifndef NO_SDL +#include "SDL.h" +#endif u8g2_t u8g2; +// #define HIDE_INSTRUCTIONS + +void spinner_animation(uint8_t animation_enabled, uint8_t* start, uint8_t* end) +{ + const uint8_t big_increment = 10; + const uint8_t small_increment = 5; + static uint8_t grow = 0, initialize = 1; + static uint8_t prev_arc_length; + + if(!animation_enabled) + { + grow = 0; + initialize = 1; + } + else + { + do + { + // Decrement angles + *start = (256 + *start - (grow ? big_increment : small_increment)) % 256; + *end = (256 + *end - (grow ? small_increment : big_increment)) % 256; + + // Get arc length + uint8_t arc_length = (256 + *end - *start) % 256; + if(initialize) + { + prev_arc_length = arc_length; + initialize = 0; + } + + // Check if arc overflows (grown above 255 or shrunk below 0) + if((grow && arc_length < prev_arc_length) || (!grow && arc_length > prev_arc_length)) + { + // invert start and end angles, and invert animation + uint8_t tmp = *start; + *start = *end; + *end = tmp; + grow = !grow; + arc_length = grow ? 0 : 255; + } + + prev_arc_length = arc_length; // update arc length for next iteration + + } while (*start == *end); // prevent drawing when arc length is 0 (arc function would draw a full circle) + } +} + + int main(void) { - int x, y; int k; - int i; + char buffer[256]; + + uint8_t animation_enabled = 1; + const uint8_t desired_fps = 30; + uint32_t last_ticks = 0; + + u8g2_uint_t x = 30; + u8g2_uint_t y = 32; - u8g2_uint_t rad_in = 10; - u8g2_uint_t rad_out = 14; + uint8_t rad_in = 18; + uint8_t rad_out = 20; - u8g2_uint_t angle_start = 0; - u8g2_uint_t angle_end = 60; + uint8_t start = 0; + uint8_t end = 220; u8g2_SetupBuffer_SDL_128x64_4(&u8g2, &u8g2_cb_r0); u8x8_ConnectBitmapToU8x8(u8g2_GetU8x8(&u8g2)); /* connect to bitmap */ u8x8_InitDisplay(u8g2_GetU8x8(&u8g2)); - u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0); - - u8g2_SetFont(&u8g2, u8g2_font_helvB18_tn); - - x = 50; - y = 30; + u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0); + u8g2_SetFont(&u8g2, u8g2_font_tiny5_tf); + u8g2_SetFontPosCenter(&u8g2); + u8g2_SetFontMode(&u8g2, 1); + for(;;) { + #ifndef NO_SDL + + // Wait for next frame with constant FPS + if (SDL_GetTicks() - last_ticks < 1000/desired_fps) continue; + last_ticks = SDL_GetTicks(); + + // Get current key press + k = u8g_sdl_get_key(); + switch(k) { + case ' ': animation_enabled = !animation_enabled; break; + case 'y': rad_in -= 1; break; + case 'u': rad_in += 1; break; + case 'h': rad_out -= 1; break; + case 'j': rad_out += 1; break; + case 'i': start -= 1; break; + case 'o': start += 1; break; + case 'k': end -= 1; break; + case 'l': end += 1; break; + case 'e': case 273: y -= 1; break; + case 'x': case 274: y += 1; break; + case 's': case 276: x -= 1; break; + case 'd': case 275: x += 1; break; + case 'q': return 0; + case 't': u8x8_SaveBitmapTGA(u8g2_GetU8x8(&u8g2), "screenshot.tga"); break; + } + if(k == ' ') printf(animation_enabled ? "play\n" : "pause\n"); + else if(k > 0) printf("x=%d, y=%d, rad_in=%d, rad_out=%d, start=%d, end=%d\n", x, y, rad_in, rad_out, start, end); + + // Animate if enabled + spinner_animation(animation_enabled, &start, &end); + #endif - + // Draw elements u8g2_FirstPage(&u8g2); - do - { - //u8g2_SetFontDirection(&u8g2, 0); - //u8g2_DrawStr(&u8g2, x, y, "123"); - - //u8g2_draw_arc(&u8g2, x, y, rad_in, rad_out, angle_start, angle_end, myatan); - u8g2_draw_arc(&u8g2, x, y, rad_in, rad_out, angle_start, angle_end, 0); - + do { + // Draw arc for each radius + for(u8g2_uint_t rad = rad_in; rad <= rad_out; rad++) + { + u8g2_DrawArc(&u8g2, x, y, rad, start, end); + } + + + // Draw instructions + #ifndef HIDE_INSTRUCTIONS + // separator + u8g2_DrawVLine(&u8g2, 62, 0, 64); + + // anim + u8g2_DrawTriangle(&u8g2, 66, 2, 66, 8, 71, 5); + u8g2_DrawLine(&u8g2, 72, 8, 76, 2); + u8g2_DrawBox(&u8g2, 79, 3, 2, 5); + u8g2_DrawBox(&u8g2, 82, 3, 2, 5); + u8g2_DrawUTF8(&u8g2, 87, 6, ":"); + (k == ' ' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 90, 1, 35, 9); + u8g2_DrawUTF8(&u8g2, 95, 6, "SPACE"); + + // + / - icons + u8g2_DrawHLine(&u8g2, 66, 17, 9); + u8g2_DrawHLine(&u8g2, 117, 17, 9); + u8g2_DrawVLine(&u8g2, 121, 13, 9); + + // rad_in + (k == 'y' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 66, 25, 9, 9); + u8g2_DrawUTF8(&u8g2, 69, 30, "Y"); + (k == 'u' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 117, 25, 9, 9); + u8g2_DrawUTF8(&u8g2, 120, 30, "U"); + snprintf(buffer, sizeof(buffer), "IN:%d", rad_in); + u8g2_DrawUTF8(&u8g2, 78, 30, buffer); + + // rad_out + (k == 'h' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 66, 35, 9, 9); + u8g2_DrawUTF8(&u8g2, 69, 40, "H"); + (k == 'j' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 117, 35, 9, 9); + u8g2_DrawUTF8(&u8g2, 120, 40, "J"); + snprintf(buffer, sizeof(buffer), "OUT:%d", rad_out); + u8g2_DrawUTF8(&u8g2, 78, 40, buffer); + + // start + (k == 'i' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 66, 45, 9, 9); + u8g2_DrawUTF8(&u8g2, 69, 50, "I"); + (k == 'o' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 117, 45, 9, 9); + u8g2_DrawUTF8(&u8g2, 120, 50, "O"); + snprintf(buffer, sizeof(buffer), "START:%d", start); + u8g2_DrawUTF8(&u8g2, 78, 50, buffer); + + // end + (k == 'k' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 66, 55, 9, 9); + u8g2_DrawUTF8(&u8g2, 69, 60, "K"); + (k == 'l' ? u8g2_DrawBox : u8g2_DrawFrame)(&u8g2, 117, 55, 9, 9); + u8g2_DrawUTF8(&u8g2, 120, 60, "L"); + snprintf(buffer, sizeof(buffer), "END:%d", end); + u8g2_DrawUTF8(&u8g2, 78, 60, buffer); + #endif + } while( u8g2_NextPage(&u8g2) ); - do - { - k = u8g_sdl_get_key(); - } while( k < 0 ); - - if ( k == 273 ) y -= 7; - if ( k == 274 ) y += 7; - if ( k == 276 ) x -= 7; - if ( k == 275 ) x += 7; - - if ( k == 'o' ) rad_out -= 1; - if ( k == 'p' ) rad_out += 1; - - if ( k == 'u' ) rad_in -= 1; - if ( k == 'i' ) rad_in += 1; - - if ( k == 'k' ) angle_end -= 1; - if ( k == 'l' ) angle_end += 1; - - - if ( k == 'e' ) y -= 1; - if ( k == 'x' ) y += 1; - if ( k == 's' ) x -= 1; - if ( k == 'd' ) x += 1; - if ( k == 'q' ) break; - - if ( k == 't' ) - u8x8_SaveBitmapTGA(u8g2_GetU8x8(&u8g2), "screenshot.tga"); - - printf("rad_in=%d rad_out=%d angle_end=%d\n", rad_in, rad_out, angle_end); } return 0; diff --git a/sys/sdl/draw_arc/u8g2_sdl b/sys/sdl/draw_arc/u8g2_sdl new file mode 100755 index 00000000..7f36609c Binary files /dev/null and b/sys/sdl/draw_arc/u8g2_sdl differ