From b21d5bed8d1bbbbc236060a04ce076950faac05c Mon Sep 17 00:00:00 2001 From: Jonathan Dearborn Date: Sat, 5 Feb 2022 14:39:48 -0500 Subject: [PATCH] Updated circle angle coefficient and added a check to fix issues with small radius circles not having enough segments to complete the shape. Thanks GiovanniCmpaner for the report! --- src/renderer_shapes_GL_common.inl | 48 +++++++--- tests/shapes/main.c | 146 +++++++++++++++--------------- 2 files changed, 108 insertions(+), 86 deletions(-) diff --git a/src/renderer_shapes_GL_common.inl b/src/renderer_shapes_GL_common.inl index cbed6728..1a053520 100644 --- a/src/renderer_shapes_GL_common.inl +++ b/src/renderer_shapes_GL_common.inl @@ -76,6 +76,18 @@ See a particular renderer's *.c file for specifics. */ +#define SDL_GPU_CIRCLE_SEGMENT_ANGLE_FACTOR 0.625f + + +#define CALCULATE_CIRCLE_DT_AND_SEGMENTS(radius) \ + dt = SDL_GPU_CIRCLE_SEGMENT_ANGLE_FACTOR/sqrtf(radius); /* s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good */ \ + numSegments = (int)(2*PI/dt) + 1; \ + \ + if(numSegments < 16) \ + { \ + numSegments = 16; \ + dt = 2*PI/(numSegments-1); \ + } static float SetLineThickness(GPU_Renderer* renderer, float thickness) @@ -178,7 +190,7 @@ static void Arc(GPU_Renderer* renderer, GPU_Target* target, float x, float y, fl } - dt = ((end_angle - start_angle)/360)*(1.25f/sqrtf(outer_radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. + dt = ((end_angle - start_angle)/360)*(SDL_GPU_CIRCLE_SEGMENT_ANGLE_FACTOR/sqrtf(outer_radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. numSegments = (int)((fabs(end_angle - start_angle)*PI/180)/dt); if(numSegments == 0) @@ -255,7 +267,7 @@ static void ArcFilled(GPU_Renderer* renderer, GPU_Target* target, float x, float end_angle -= 360; } - dt = ((end_angle - start_angle)/360)*(1.25f/sqrtf(radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. + dt = ((end_angle - start_angle)/360)*(SDL_GPU_CIRCLE_SEGMENT_ANGLE_FACTOR/sqrtf(radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. numSegments = (int)((fabs(end_angle - start_angle)*RAD_PER_DEG)/dt); if(numSegments == 0) @@ -314,8 +326,10 @@ static void Circle(GPU_Renderer* renderer, GPU_Target* target, float x, float y, float t = thickness/2; float inner_radius = radius - t; float outer_radius = radius + t; - float dt = (1.25f/sqrtf(outer_radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. - int numSegments = (int)(2*PI/dt)+1; + float dt; + int numSegments; + + CALCULATE_CIRCLE_DT_AND_SEGMENTS(outer_radius); float tempx; float c = cosf(dt); @@ -345,10 +359,12 @@ static void Circle(GPU_Renderer* renderer, GPU_Target* target, float x, float y, static void CircleFilled(GPU_Renderer* renderer, GPU_Target* target, float x, float y, float radius, SDL_Color color) { - float dt = (1.25f/sqrtf(radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. + float dt; float dx, dy; - int numSegments = (int)(2*PI/dt)+1; + int numSegments; int i; + + CALCULATE_CIRCLE_DT_AND_SEGMENTS(radius); float tempx; float c = cosf(dt); @@ -395,8 +411,10 @@ static void Ellipse(GPU_Renderer* renderer, GPU_Target* target, float x, float y float outer_radius_x = rx + t; float inner_radius_y = ry - t; float outer_radius_y = ry + t; - float dt = (1.25f/sqrtf(outer_radius_x > outer_radius_y? outer_radius_x : outer_radius_y)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. - int numSegments = (int)(2*M_PI/dt)+1; + float dt; + int numSegments; + + CALCULATE_CIRCLE_DT_AND_SEGMENTS(outer_radius_x > outer_radius_y? outer_radius_x : outer_radius_y); float tempx; float c = cosf(dt); @@ -442,8 +460,9 @@ static void EllipseFilled(GPU_Renderer* renderer, GPU_Target* target, float x, f int i; float rot_x = cosf(degrees*RAD_PER_DEG); float rot_y = sinf(degrees*RAD_PER_DEG); - float dt = (1.25f/sqrtf(rx > ry? rx : ry)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. - int numSegments = (int)(2*M_PI/dt)+1; + float dt; + int numSegments; + CALCULATE_CIRCLE_DT_AND_SEGMENTS(rx > ry? rx : ry); float tempx; float c = cosf(dt); @@ -586,7 +605,7 @@ static void SectorFilled(GPU_Renderer* renderer, GPU_Target* target, float x, fl t = start_angle; - dt = ((end_angle - start_angle)/360)*(1.25f/sqrtf(outer_radius)) * DEG_PER_RAD; // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. + dt = ((end_angle - start_angle)/360)*(SDL_GPU_CIRCLE_SEGMENT_ANGLE_FACTOR/sqrtf(outer_radius)) * DEG_PER_RAD; // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. numSegments = (int)(fabs(end_angle - start_angle)/dt); if(numSegments == 0) @@ -808,10 +827,9 @@ static void RectangleRound(GPU_Renderer* renderer, GPU_Target* target, float x1, float t = thickness/2; float inner_radius = radius - t; float outer_radius = radius + t; - float dt = (1.25f/sqrtf(outer_radius)); // s = rA, so dA = ds/r. ds of 1.25*sqrt(radius) is good, use A in degrees. - int numSegments = (int)(2*PI/dt)+1; - if(numSegments < 4) - numSegments = 4; + float dt; + int numSegments; + CALCULATE_CIRCLE_DT_AND_SEGMENTS(outer_radius); // Make a multiple of 4 so we can have even corners numSegments += numSegments % 4; diff --git a/tests/shapes/main.c b/tests/shapes/main.c index cd9ce0ee..8bab51f0 100644 --- a/tests/shapes/main.c +++ b/tests/shapes/main.c @@ -9,6 +9,76 @@ #define M_PI 3.14159f #endif +#define RANDOMIZE_SHAPE_DATA() \ + for(i = 0; i < NUM_COLORS; i++) \ + { \ + colors[i].r = rand() % 256; \ + colors[i].g = rand() % 256; \ + colors[i].b = rand() % 256; \ + GET_ALPHA(colors[i]) = rand() % 256; \ + } \ + \ + for (i = 0; i < NUM_PIXELS; i++) \ + { \ + px[i] = rand() % screen->w; \ + py[i] = rand() % screen->h; \ + } \ + \ + for (i = 0; i < NUM_LINES; i++) \ + { \ + lx1[i] = rand() % screen->w; \ + ly1[i] = rand() % screen->h; \ + lx2[i] = rand() % screen->w; \ + ly2[i] = rand() % screen->h; \ + } \ + \ + for (i = 0; i < NUM_TRIS; i++) \ + { \ + tx1[i] = rand() % screen->w; \ + ty1[i] = rand() % screen->h; \ + tx2[i] = rand() % screen->w; \ + ty2[i] = rand() % screen->h; \ + tx3[i] = rand() % screen->w; \ + ty3[i] = rand() % screen->h; \ + } \ + \ + for (i = 0; i < NUM_RECTS; i++) \ + { \ + rx1[i] = rand() % screen->w; \ + ry1[i] = rand() % screen->h; \ + rx2[i] = rand() % screen->w; \ + ry2[i] = rand() % screen->h; \ + rr[i] = rand() % 10 + 2; \ + } \ + \ + for (i = 0; i < NUM_ARCS; i++) \ + { \ + ax[i] = rand() % screen->w; \ + ay[i] = rand() % screen->h; \ + ar[i] = (rand() % screen->h) / 10.0f; \ + ar2[i] = ((rand() % 101) / 100.0f) * ar[i]; \ + aa1[i] = rand() % 360; \ + aa2[i] = rand() % 360; \ + } \ + \ + for (i = 0; i < NUM_POLYS; i++) \ + { \ + float cx = rand() % screen->w; \ + float cy = rand() % screen->h; \ + float radius = 20 + rand() % (screen->w / 8); \ + \ + int j; \ + \ + pn[i] = rand() % 8 + 3; \ + pv[i] = (float*)malloc(2 * pn[i] * sizeof(float)); \ + \ + for (j = 0; j < pn[i] * 2; j += 2) \ + { \ + pv[i][j] = cx + radius * cos(2 * M_PI * (((float)j) / (pn[i] * 2))) + rand() % ((int)radius / 2); \ + pv[i][j + 1] = cy + radius * sin(2 * M_PI * (((float)j) / (pn[i] * 2))) + rand() % ((int)radius / 2); \ + } \ + } + int main(int argc, char* argv[]) { GPU_Target* screen; @@ -79,77 +149,7 @@ int main(int argc, char* argv[]) shapeType = 0; numShapeTypes = 18; - for(i = 0; i < NUM_COLORS; i++) - { - colors[i].r = rand()%256; - colors[i].g = rand()%256; - colors[i].b = rand()%256; - GET_ALPHA(colors[i]) = rand()%256; - } - - - - for(i = 0; i < NUM_PIXELS; i++) - { - px[i] = rand()%screen->w; - py[i] = rand()%screen->h; - } - - for(i = 0; i < NUM_LINES; i++) - { - lx1[i] = rand()%screen->w; - ly1[i] = rand()%screen->h; - lx2[i] = rand()%screen->w; - ly2[i] = rand()%screen->h; - } - - for(i = 0; i < NUM_TRIS; i++) - { - tx1[i] = rand()%screen->w; - ty1[i] = rand()%screen->h; - tx2[i] = rand()%screen->w; - ty2[i] = rand()%screen->h; - tx3[i] = rand()%screen->w; - ty3[i] = rand()%screen->h; - } - - - for(i = 0; i < NUM_RECTS; i++) - { - rx1[i] = rand()%screen->w; - ry1[i] = rand()%screen->h; - rx2[i] = rand()%screen->w; - ry2[i] = rand()%screen->h; - rr[i] = rand()%10 + 2; - } - - for(i = 0; i < NUM_ARCS; i++) - { - ax[i] = rand()%screen->w; - ay[i] = rand()%screen->h; - ar[i] = (rand()%screen->h)/10.0f; - ar2[i] = ((rand()%101)/100.0f)*ar[i]; - aa1[i] = rand()%360; - aa2[i] = rand()%360; - } - - for(i = 0; i < NUM_POLYS; i++) - { - float cx = rand()%screen->w; - float cy = rand()%screen->h; - float radius = 20 + rand()%(screen->w/8); - - int j; - - pn[i] = rand()%8 + 3; - pv[i] = (float*)malloc(2*pn[i]*sizeof(float)); - - for(j = 0; j < pn[i]*2; j+=2) - { - pv[i][j] = cx + radius*cos(2*M_PI*(((float)j)/(pn[i]*2))) + rand()%((int)radius/2); - pv[i][j+1] = cy + radius*sin(2*M_PI*(((float)j)/(pn[i]*2))) + rand()%((int)radius/2); - } - } + RANDOMIZE_SHAPE_DATA(); blend = 0; thickness = 1.0f; @@ -184,6 +184,10 @@ int main(int argc, char* argv[]) blend = !blend; GPU_SetShapeBlending(blend); } + else if(event.key.keysym.sym == SDLK_RETURN) + { + RANDOMIZE_SHAPE_DATA(); + } else if(event.key.keysym.sym == SDLK_UP || event.key.keysym.sym == SDLK_EQUALS) { thickness += 0.25f;