raysan5/raylib

[rshapes] DrawRectangleRoundedLines and siblings did not produce pixel aligned graphics nor consistent visual

designerfuzzi opened this issue · 3 comments

...new issue verify and check:

  • I tested it on latest raylib version from master branch
  • I checked there is no similar issue already reported
  • I checked the documentation on the wiki
  • My code has no errors or misuse of raylib

Issue description

when drawing a DrawRectangleRoundedLines(view->bounds, 0.2, 6, 0.001, view->color); where my rectangle is (Rectangle){3,3,32,32} i get weirdo graphic alignments where sometime the sides or the rounded corners either do not line up or show different alignments from visual to visual with same rectangle size. Which pointed me in the direction there is an floating alignment issue somewhere in the function. It was worse when i used lineThick == 0 not very surprising it looks like.. (the 16 pink visuals at top of this window)

Issue Screenshot

Bildschirmfoto 2024-03-19 um 13 18 29 Its tiny but regular, irregular, regular, half regular, irregular and regular again. This issue resisted any atttempt to line up or visualise at least all the time the same.. So investigated and indeed found it seems to be an alignment problem..

Environment

Platform: Darwin,
Operating System: macOS10.15,
OpenGL version:
INFO: > Vendor: ATI Technologies Inc.
INFO: > Renderer: AMD Radeon R9 M370X OpenGL Engine
INFO: > Version: 4.1 ATI-2.11.26
INFO: > GLSL: 4.10
raylib 5.1-dev

when i change lineThick==0.001 it became better, seems the math in the function loves this tiny difference.. but thats not how to fix..

Code Example

how i actually fixed it. Made some code tidy up to see what i do and use macros to test..

//rshapes.c: LINE 1995
// Draw rectangle with rounded edges outline
void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, float lineThick, Color color)
{
    if (lineThick < 0) lineThick = 0;

    // Not a rounded rectangle
    if (roundness <= 0.0f)
    {
        DrawRectangleLinesEx((Rectangle){rec.x-lineThick, rec.y-lineThick, rec.width+2*lineThick, rec.height+2*lineThick}, lineThick, color);
        return;
    }

    if (roundness >= 1.0f) roundness = 1.0f;

    // Calculate corner radius
    //float radius = (rec.width > rec.height)? (rec.height*roundness)/2 : (rec.width*roundness)/2;
    float radius = (rec.width > rec.height)? (rec.height*roundness)/2.0 : (rec.width*roundness)/2.0;
    if (radius <= 0.0f) return;

    // Calculate number of segments to use for the corners
    if (segments < 4)
    {
        // Calculate the maximum angle between segments based on the error rate (usually 0.5f)
        float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1);
        segments = (int)(ceilf(2*PI/th)/2.0f);
        if (segments <= 0) segments = 4;
    }

    float stepLength = 90.0f/(float)segments;
    const float outerRadius = radius + lineThick, innerRadius = radius;

    // some helper macros to investigate and fix pixel alignment issue
    //#define RONDO(x, y)  ((Vector2){roundf((float)x),roundf((float)y)})
      #define NIX__(x, y)  ((Vector2){      ((float)x),      ((float)y)})
      #define NIX_F(x, y)  ((Vector2){      ((float)x),floorf((float)y)})
    //#define NIXF_(x, y)  ((Vector2){floorf((float)x),      ((float)y)})
    //#define FIXFC(x, y)  ((Vector2){floorf((float)x), ceilf((float)y)})
    //#define FIXFF(x, y)  ((Vector2){floorf((float)x),floorf((float)y)})
    //#define FIXCF(x, y)  ((Vector2){ ceilf((float)x),floorf((float)y)})
    //#define FIXCC(x, y)  ((Vector2){ ceilf((float)x), ceilf((float)y)})
    /*
    Quick sketch to make sense of all of this,
    marks the 16 + 4(corner centers P16-19) points we'll use
         |............ width ............|
    (X,Y).                               .
     -   •..0 ======================= 1  .
     .   .  //8 .                 . 9\\  .
     .   . //   .                 .   \\ .
     h   7//15  .                 .  10\\2
     e   ||.....•16             17•.....||
     i   ||                             ||
     g   ||14                         11||
     h   6\\....•19             18•....//3
     t     \\   .                 .   //
     .      \\13.                 .12//
     -      5 ====================== 4
    */
    
    const Vector2 point[16] = {
        NIX__( rec.x              + innerRadius             ,  rec.y - lineThick                ), // PO,
        NIX__((rec.x + rec.width) - innerRadius             ,  rec.y - lineThick                ), // P1,
        NIX__( rec.x + rec.width  + lineThick               ,  rec.y + innerRadius              ), // P2,
        NIX_F( rec.x + rec.width  + lineThick               , (rec.y + rec.height) - innerRadius), // P3,
        NIX__((rec.x + rec.width) - innerRadius             ,  rec.y + rec.height  + lineThick  ), // P4,
        NIX__( rec.x              + innerRadius             ,  rec.y + rec.height  + lineThick  ), // P5,
        NIX_F( rec.x              - lineThick               , (rec.y + rec.height) - innerRadius), // P6,
        NIX__( rec.x              - lineThick               ,  rec.y + innerRadius              ), // P7,
        NIX__( rec.x              + innerRadius             ,  rec.y                            ), // P8,
        NIX__((rec.x + rec.width) - innerRadius             ,  rec.y                            ), // P9,
        NIX__( rec.x + rec.width                            ,  rec.y + innerRadius              ), // P10,
        NIX_F( rec.x + rec.width                            , (rec.y + rec.height) - innerRadius), // P11,
        NIX__((rec.x + rec.width) - innerRadius             ,  rec.y + rec.height               ), // P12,
        NIX__( rec.x              + innerRadius             ,  rec.y + rec.height               ), // P13,
        NIX_F( rec.x                                        , (rec.y + rec.height) - innerRadius), // P14,
        NIX__( rec.x                                        ,  rec.y + innerRadius              )  // P15,
    };
    
    const Vector2 centers[4] = {
        NIX__(  rec.x              + innerRadius,   rec.y               + innerRadius), // P16,
        NIX__( (rec.x + rec.width) - innerRadius,   rec.y               + innerRadius), // P17
        NIX_F( (rec.x + rec.width) - innerRadius,  (rec.y + rec.height) - innerRadius), // P18,
        NIX_F(  rec.x              + innerRadius,  (rec.y + rec.height) - innerRadius)  // P19
    };

   /* . . . */
   

If i am right, then center points 18,19, and edge points 3,11, 6,14 need to get help to snap to pixels together.

Makes a tiny difference, but looks now like..
Bildschirmfoto 2024-03-19 um 13 19 14

PS: working on an Automation Event driven ViewModel that allows me to turn around the design pattern but for that i might hop over to discord to discuss.

@designerfuzzi Why was this issue closed?

i decided it is more effective to fork, leave toxic discussion or stop short argumentation out and simply do fixes in that fork.

@designerfuzzi ok, thanks for your answer.