Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Rounded corners [ANSWERED]

BortelsBortels Mod
edited November 2011 in Questions Posts: 1,557

So - I want a rectangle with rounded corners. Before I write my own nasty version with lines and quarter ellipses and such - is there some more elegant way to do it I'm missing? Or has someone perhaps already done this?

Tagged:

Comments

  • SimeonSimeon Admin Mod
    Posts: 5,448

    Nasty way is the only way, for now. (Or custom sprites.)

    Once the image class is available you could compute them in an image. Though it would be nice to have them available as a primitive shape.

  • BortelsBortels Mod
    Posts: 1,557

    FWIW - after futzing around a bit, a rectangle with a circle on either end is close enough to a rounded rectangle for my purposes (ie. it looks good and can be an extended key).

    now to figure out a better/faster way to do the math.

  • Posts: 2,161

    I did this by filling a rectangle and then drawing the edges (one by one) with the line end mode set to ROUND.

  • SimeonSimeon Admin Mod
    Posts: 5,448

    That's a pretty good solution.

  • SimeonSimeon Admin Mod
    edited November 2011 Posts: 5,448

    I really liked Andrew's idea. So I wrote a function to draw them:

    function roundRect(x, y, w, h, r)
        pushStyle()
        insetPos = vec2(x+r,y+r)
        insetSize = vec2(w-2*r,h-2*r)
    
        rectMode(CORNER)
        rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
        if r > 0 then
            smooth()
            lineCapMode(ROUND)
            strokeWidth(r*2)
    
            line(insetPos.x, insetPos.y, 
                 insetPos.x + insetSize.x, insetPos.y)
            line(insetPos.x, insetPos.y,
                 insetPos.x, insetPos.y + insetSize.y)
            line(insetPos.x, insetPos.y + insetSize.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)
            line(insetPos.x + insetSize.x, insetPos.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
        end
        popStyle()
    end
    

    Here's a sample use. (Parameter to adjust the corner radius.)

    -- Main
    function setup()
        parameter("CornerRadius",0,50)
    end
    
    function draw()
        -- This sets the background color to black
        background(0, 0, 0)
    
        -- Stroke and fill must be the same
        stroke(255,0,0)
        fill(255,0,0)
    
        roundRect(0, 0, 100, 100, CornerRadius)
    end
    
  • BortelsBortels Mod
    Posts: 1,557

    Cute! It suffers the "balls!" issue a bit, but that's me being nitpicky - this is, dare I say, elegant.

  • SimeonSimeon Admin Mod
    Posts: 5,448

    It does, we hope to resolve the 'balls' issue on the line shader itself.

  • Posts: 2,161

    It suffers the what issue a bit?

  • SimeonSimeon Admin Mod
    Posts: 5,448

    Hah. BALLS.

  • Posts: 2,161

    *cleans out ears*

    Nope, still not any the wiser. Will it become obvious if I run that code?

  • SimeonSimeon Admin Mod
    edited November 2011 Posts: 5,448

    Thin smooth() lines with ROUND end caps look like they have balls on either end. It's due to the way the line shader draws lines (the caps are something like half a pixel bigger than the line itself, or using a different antialiasing width, to be determined).

  • Posts: 2,161

    Ah, now I understand. Thanks.

  • SimeonSimeon Admin Mod
    Posts: 5,448

    In Codea 1.3 you can retrieve the style information from all style functions by calling without arguments. So I've updated this code to correctly use only the fill colour, as a normal rect would.

    It could even be updated to respect the current rectMode.

        function roundRect(x, y, w, h, r)
            pushStyle()
            insetPos = vec2(x+r,y+r)
            insetSize = vec2(w-2*r,h-2*r)
    
            rectMode(CORNER)
            rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
            r,g,b,a = fill()
            stroke(r,g,b,a)
    
            if r > 0 then
                smooth()
                lineCapMode(ROUND)
                strokeWidth(r*2)
    
                line(insetPos.x, insetPos.y, 
                     insetPos.x + insetSize.x, insetPos.y)
                line(insetPos.x, insetPos.y,
                     insetPos.x, insetPos.y + insetSize.y)
                line(insetPos.x, insetPos.y + insetSize.y,
                     insetPos.x + insetSize.x, insetPos.y + insetSize.y)
                line(insetPos.x + insetSize.x, insetPos.y,
                     insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
            end
            popStyle()
        end
    

    Here's a sample use. (Parameter to adjust the corner radius.)

        -- Main
        function setup()
            parameter("CornerRadius",0,50)
        end
    
        function draw()
            -- This sets the background color to black
            background(0, 0, 0)
    
            -- Set the fill color
            fill(255,0,0)
    
            roundRect(0, 0, 100, 100, CornerRadius)
        end
    
  • Posts: 371

    Here's my version of it (I had to rewire clip() for this)

    _clip = clip
    function clip(x, y, w, h)
        if x ~= nil then
            local m = modelMatrix()
            x = x * m[1] + m[13]
            y = y * m[6] + m[14]
            w = w * m[1]
            h = h * m[6]
            _clip(x, y, w, h)
        else
            _clip()
        end
    end
    
    function roundRect(x, y, w, h, r)
        r = r or math.min(w, h) / 8
        pushStyle()
            noSmooth()
            ellipseMode(RADIUS)
            if rectMode() == CORNERS then
                w = w - x
                h = h - y
            elseif rectMode() == CENTER then
                x = x - w / 2
                y = y - h / 2
            elseif rectMode() == RADIUS then
                x = x - w
                y = y - h
                w = w * 2
                h = h * 2
            end
            if r > math.min(w, h) / 2 then
                r = math.min(w, h) / 2
            end
            rectMode(CORNER)
            pushMatrix()
                translate(x, y)
                clip(r, 0, w - r * 2, h)
                    rect(0, 0, w, h)
                clip(0, r, w, h - r * 2)
                    rect(0, 0, w, h)
                r = r + .25
                if strokeWidth() + .25 > r then
                    fill(stroke())
                    noStroke()
                end
                clip(0, 0, r + 1, r + 1)
                    ellipse(r, r, r + 1)
                clip(w - r - 1, 0, r + 2, r + 1)
                    ellipse(w - r, r, r + 1)
                clip(0, h - r - 1, r + 1, r + 2)
                    ellipse(r, h - r, r + 1)
                clip(w - r - 1, h - r - 1, r + 2, r + 2)
                    ellipse(w - r, h - r, r + 1)
                clip()
            popMatrix()
        popStyle()
    end
    
    function draw()
        rectMode(CORNER)
        fill(255, 0, 0, 255)
        stroke(255, 255, 255, 255)
        strokeWidth(20)
        roundRect(10, 10, WIDTH / 2 - 20, HEIGHT / 2 - 20, 100)
        fill(0, 255, 0, 255)
        stroke(255, 127, 0, 255)
        strokeWidth(55)
        rectMode(CORNERS)
        roundRect(WIDTH / 2 + 10, 10, WIDTH - 10, HEIGHT / 2 - 10, 50)
        fill(0, 0, 255, 255)
        noStroke()
        rectMode(CENTER)
        roundRect(WIDTH / 4, HEIGHT * .75, WIDTH / 2 - 20, HEIGHT / 2 - 20, 75)
        strokeWidth(10)
        stroke(127, 0, 127, 255)
        noFill()
        rectMode(RADIUS)
        translate(WIDTH * .75, HEIGHT * .75)
        roundRect(0, 0, WIDTH / 4 - 10, HEIGHT / 4 - 10)
    end
    
  • Posts: 666

    Anyone notice that when drawing with an aplha channel, that the line caps draw the alpha channel twice? The "caps" look like they have twice the density inside of the line than outside of the line.

  • Posts: 371

    Yep, it looks like a rect with two ellipses drawn on the end, right? I was thinking about rewriting it in a shader to make it smooth, but I got side tracked...

  • Posts: 2,161

    I have a mesh version of rounded rectangles if anyone would like it.

  • Posts: 505

    sure!

  • Posts: 100

    Why not this simple solution ....
    But do not use transparent or the "balls" will appear ;-)

    background(40, 40, 50)
        strokeWidth(60) lineCapMode(ROUND)
        stroke(206, 206, 206, 255)
        line(500,300,600,350)
        strokeWidth(56)
        stroke(28, 100, 150, 255)
        line(500,300,600,350)
    

    Re Peter

  • Posts: 140

    I'd like to see a mesh version of rounded rectangles.

  • Posts: 2,161

    @Ric_Esrey You already have it ... it's in the Utilities.lua file in my library.

    For everyone else, it's:

    --[[
    This is an auxilliary function for drawing a rectangle with possibly
    curved cornders.  The first four parameters specify the rectangle.
    The fifth is the radius of the corner rounding.  The optional sixth is
    a way for specifying which corners should be rounded by passing a
    number between 0 and 15.  The first bit corresponds to the lower-left
    corner and it procedes clockwise from there.
    --]]
    
    local __RRects = {}
    
    function RoundedRectangle(x,y,w,h,s,c,a)
        c = c or 0
        w = w or 0
        h = h or 0
        if w < 0 then
            x = x + w
            w = -w
        end
        if h < 0 then
            y = y + h
            h = -h
        end
        w = math.max(w,2*s)
        h = math.max(h,2*s)
        a = a or 0
        pushMatrix()
        translate(x,y)
        rotate(a)
        local label = table.concat({w,h,s,c},",")
        if __RRects[label] then
            __RRects[label]:setColors(fill())
            __RRects[label]:draw()
        else
        local rr = mesh()
        local v = {}
        local ce = vec2(w/2,h/2)
        local n = 4
        local o,dx,dy
        for j = 1,4 do
            dx = -1 + 2*(j%2)
            dy = -1 + 2*(math.floor(j/2)%2)
            o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
            if math.floor(c/2^(j-1))%2 == 0 then
        for i = 1,n do
            table.insert(v,o)
            table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
            table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
        end
        else
            table.insert(v,o)
            table.insert(v,o + vec2(dx * s,0))
            table.insert(v,o + vec2(dx * s,dy * s))
            table.insert(v,o)
            table.insert(v,o + vec2(0,dy * s))
            table.insert(v,o + vec2(dx * s,dy * s))
        end
        end
        rr.vertices = v
        rr:addRect(ce.x,ce.y,w,h-2*s)
        rr:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
        rr:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
        rr:setColors(fill())
        rr:draw()
        __RRects[label] = rr
        end
        popMatrix()
    end
    
    --[[
    Adds a rounded rectangle to an existing mesh
    --]]
    
    function addRoundedRect(t)
       local m = t.mesh
       local x = t.x
       local y = t.y
       local w = t.width or 0
       local h = t.height or 0
       local s = t.radius or 10
       local c = t.corners or 0
       local a = t.anchor
       local fc = t.colour or fill()
       if a then
          x,y = RectAnchorAt(x,y,w,h,a)
       end
       local v = {}
       local nv = 0
       local ce = vec2(x + w/2,y + h/2)
       local n = 4
       local o,dx,dy
       for j = 1,4 do
          dx = -1 + 2*(j%2)
          dy = -1 + 2*(math.floor(j/2)%2)
          o = ce + vec2(dx * (w/2 - s), dy * (h/2 - s))
          if math.floor(c/2^(j-1))%2 == 0 then
             for i = 1,n do
                table.insert(v,o)
                table.insert(v,o + vec2(dx * s * math.cos((i-1) * math.pi/(2*n)), dy * s * math.sin((i-1) * math.pi/(2*n))))
                table.insert(v,o + vec2(dx * s * math.cos(i * math.pi/(2*n)), dy * s * math.sin(i * math.pi/(2*n))))
             end
             nv = nv + 3*n
          else
             table.insert(v,o)
             table.insert(v,o + vec2(dx * s,0))
             table.insert(v,o + vec2(dx * s,dy * s))
             table.insert(v,o)
             table.insert(v,o + vec2(0,dy * s))
             table.insert(v,o + vec2(dx * s,dy * s))
             nv = nv + 6
          end
       end
       local nrv = m.size
       m:resize(nrv + nv)
       for k,ve in ipairs(v) do
          m:vertex(nrv + k, ve)
          m:color(nrv+k,fc)
       end
       local ri = m:addRect(ce.x,ce.y,w,h-2*s)
       m:setRectColor(ri,fc)
       ri = m:addRect(ce.x,ce.y + (h-s)/2,w-2*s,s)
       m:setRectColor(ri,fc)
       ri = m:addRect(ce.x,ce.y - (h-s)/2,w-2*s,s)
       m:setRectColor(ri,fc)
    end
    
  • Posts: 666

    Oh, thank goodness. It's a simple script! Wow.

    Either way, I'm trying to use it for buttons and rounded, glossy sliders, so I could emulate some of basic IOS look and feel for Cider/2.

    I was thinking meshes, looks like that's the way to go.

  • Posts: 371

    .@aciolino, have you seen my toggle class?

  • Posts: 666

    @Jordan, I don't recall seeing it, though I might have come across it a long time ago...

  • hello,

    I am new to any form of coding. I thought this would be a excellent start for me. but this is very confusing to me. I don't exactly know what I'm asking but this rounded corners is really confusing me. Mostly because i have no clue what I'm looking at. I understand like what the width and height, and kinda what the code is saying but at the same time I don't. is there anyway that someone could help me out? Or send to to a webpage that can explain like every baby step?

  • Posts: 563

    @AudieLittle1111 - you may want to build up to meshes. Here is a tute on how to do corners old school: http://codeatuts.blogspot.com.au/2012/06/interlude-4-rounded-border-class.html , it is probably easier to understand.

    There is also a tute on meshes in the wiki.

  • IgnatzIgnatz Mod
    Posts: 5,396

    @AudieLittle1111 - this is WAY too hard for a beginner. Start with the online documentation for Lua and Codea, and all the tutorials. The FAQ at the top of this forum has some good links.

    Don't be put off - Codea is a really nice programming platform, and way, way simpler than most of the well known languages. You'll have a lot of fun with it.

  • Posts: 2,820

    @Simeon - Is there any chance of a fix for the "balls" on the end of lines? I've had multiple times I've ran into that issue. If it's not coming soon, where could I find a shader that could fix it? Thanks!

  • Posts: 2,820

    Also, just an update to Simeon's code. It wasn't working because he was referencing the r color value that was used in the fill, not the global radius. I also added the width, height, and rectangle color.

    function setup()
        parameter.number("CornerRadius",0,50,10)
        parameter.number("W",0,500,100)
        parameter.number("H",0,500,100)
        parameetr.color("Color",color(255,0,0))
    end
    
    function draw()
        background(0, 0, 0)
        fill(Color)
        roundRect(WIDTH/2-W/2,HEIGHT/2-H/2,W,H, CornerRadius)
    end
    
    function roundRect(x, y, w, h, cr)
        pushStyle()
        insetPos = vec2(x+cr,y+cr)
        insetSize = vec2(w-2*cr,h-2*cr)
    
        rectMode(CORNER)
        rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
        r,g,b,a = fill()
        stroke(r,g,b,a)
    
        if r > 0 then
            smooth()
            lineCapMode(ROUND)
            strokeWidth(cr*2)
    
            line(insetPos.x, insetPos.y, 
                 insetPos.x + insetSize.x, insetPos.y)
            line(insetPos.x, insetPos.y,
                insetPos.x, insetPos.y + insetSize.y)
            line(insetPos.x, insetPos.y + insetSize.y,
                 insetPos.x + insetSize.x, insetPos.y + insetSize.y)
            line(insetPos.x + insetSize.x, insetPos.y,
                insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
        end
        popStyle()
    end
    

    Thanks!

  • Posts: 2,043

    @Zoyt, nice work!

  • Posts: 2,161

    @Zoyt There's a rounded rectangle that uses meshes in my libraries. I think it's in Library Base in CC, but it might be in Library Utilities. It's also standalone - it doesn't need any of my other stuff to use.

  • Posts: 2,820

    @Andrew_Stacey - Thanks. I don't think I'll end up using rounded rectangles. Just didn't fit my app.
    @JakAttak - Thanks.

Sign In or Register to comment.