-2

I have been looking for quite a while for an already pre-made function for creating a rounded rectangle that doesn't cause tearing/glitching like the one below. Credits to @György Kőszeg. This function works fine if the rectangle is big enough. When you start making the rectangle smaller you run into issues like the below image. I am looking for an easy fix for this.

If this issue is on this website and I have missed it, I do apologize for re-asking. (I remember asking this a while back either on here or on another website and receiving an answer that worked amazingly) This issue has been paining me for quite awhile (again).

public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
    {
        int diameter = radius * 2;
        Size size = new Size(diameter, diameter);
        Rectangle arc = new Rectangle(bounds.Location, size);
        GraphicsPath path = new GraphicsPath();

        if (radius == 0)
        {
            path.AddRectangle(bounds);
            return path;
        }

        // top left arc  
        path.AddArc(arc, 180, 90);

        // top right arc  
        arc.X = bounds.Right - diameter;
        path.AddArc(arc, 270, 90);

        // bottom right arc  
        arc.Y = bounds.Bottom - diameter;
        path.AddArc(arc, 0, 90);

        // bottom left arc 
        arc.X = bounds.Left;
        path.AddArc(arc, 90, 90);

        path.CloseFigure();
        return path;
    }

Error Bar Good Bar


UPDATE:

In the meantime I have started to use the code below which just overrides the rectangle if the width is less than the height. This creates a perfect circle (the smallest the rounded rectangle can be without the glitching/tearing). A comment was placed on this stating I should not use "tearing" because its simply caused by the math which I understand but I really have no idea what else to call the glitchy rectangle in the image.

Basically I want a oval instead of a circle to correctly reflex the "Exp" value.

    public static GraphicsPath RoundedRect(Rectangle bounds, int radius)
    {
        int diameter = radius * 2;
        Size size = new Size(diameter, diameter);
        Rectangle arc = new Rectangle(bounds.Location, size);
        GraphicsPath path = new GraphicsPath();
        
        //new code here//
        if(bounds.Height >= bounds.Width)
        {
            bounds.Width = bounds.Height;
        }

        if (radius >= diameter) { 
            path.AddRectangle(bounds); 
            return path; 
        }

        // top left arc   
        path.AddArc(arc, 180, 90); 
        
        // top right arc   
        arc.X = bounds.Right - diameter; 
        path.AddArc(arc, 270, 90); 
        
        // bottom right arc   
        arc.Y = bounds.Bottom - diameter; 
        path.AddArc(arc, 0, 90); 
        
        // bottom left arc  
        arc.X = bounds.Left;
        path.AddArc(arc, 90, 90);
        path.CloseFigure();
        return path;
    }
  • The image you show doesn't look like tearing at all. It looks like a calculation error.. - Also: What __should__ happen when the width is samller than the diameter?? – TaW Aug 31 '20 at 10:09
  • @TaW, I didn't say my wording was a hundred percent correct. I see its not "tearing", but its obvious its not rendering the way it should. If the width is smaller than the diameter then it should be showing a little dot in the bar rather than a weird looking "I". I have corrected this in the past but it required alot of shitty calculations that I have not saved. I am looking for a function that basically resolves this on its own. (fixes the "calculation error" in your words.) – ViperzSmurfz Aug 31 '20 at 18:50
  • _its not rendering the way it should._ i'd say the path is not set correctly. do not let the rectangle.Width get smaller than diameter. – TaW Aug 31 '20 at 20:01
  • Or try this : `if (radius >= diameter) { path.AddRectangle(bounds); return path; } // top left arc arc.X = bounds.Left - diameter; path.AddArc(arc, 180, 90); // top right arc arc.X = bounds.Right - diameter; path.AddArc(arc, 270, 90); // bottom right arc arc.Y = bounds.Bottom - diameter; path.AddArc(arc, 0, 90); // bottom left arc arc.X = bounds.Left - diameter; path.AddArc(arc, 90, 90);` – TaW Sep 01 '20 at 08:12
  • That causes the rounded rectangle to be placed offset of where I wanted it. Although I wanted the bar to stay round and shrink farther than the diameter of itself, I will just suffice with the minimum the bar can be is the height. `if(bounds.Height >= bounds.Width){bounds.Width = bounds.Height;}` (just override when width is smaller than height.) – ViperzSmurfz Sep 03 '20 at 00:18
  • You can always offset where you draw the path. (`graphics.TranslateTransform(-radius, 0);` ) – TaW Sep 03 '20 at 07:29

1 Answers1

1

Something like this may work better. Try it with various values for curveSize. Note that curveSize must be between 1 and the minimum of rect.Width/4 and rect.Height/4:

public static GraphicsPath RoundedRect(Rectangle rc, float curveSize)
{
    if (curveSize < 0 || curveSize > rc.Height / 4.0f || curveSize > rc.Width / 4.0f)
        curveSize = 0;

    var result = new GraphicsPath();

    if (curveSize > 0)
    {
        float size4 = curveSize * 4;

        result.AddArc(rc.Right - size4, rc.Top, size4, size4, 270, 90);
        result.AddArc(rc.Right - size4, rc.Bottom - size4, size4, size4, 0, 90);
        result.AddArc(rc.Left, rc.Bottom - size4, size4, size4, 90, 90);
        result.AddArc(rc.Left, rc.Top, size4, size4, 180, 90);

        result.CloseFigure();
    }
    else if (rc.Width > 0 && rc.Height > 0)
    {
        result.AddRectangle(rc);
    }

    return result;
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • Although this resolves the tearing issue, I can't seem to make a rounded rectangle like the images. I can only manage to get the rectangle to be curved a certain amount. (More Box Like) – ViperzSmurfz Aug 31 '20 at 08:16
  • @ViperzSmurfz Unfortunately I can't see the images because that site is blocked on my work network. – Matthew Watson Aug 31 '20 at 08:40