Rounded Rectangles

Showcase your libraries, tools and other projects that help your fellow love users.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Rounded Rectangles

Post by Ref »

You could eliminate 3/4 of the trig calls.
If you time one million calls (without the love.graphics.polygon call), the times I get are 7.35 seconds for Robin's function versus 4.57 seconds for the modified code.
Really not too important because most of the time is actually spent filling the rectangle.
Think the code is OK but could contain some unfound bugs.
Plugged it into Robin's demo and got the same figures.
Attachments
radiusRectangle.love
slightly modified Robin's code
(840 Bytes) Downloaded 157 times
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Rounded Rectangles

Post by Robin »

I'd like to update my Gist with your changes, is that okay?
Help us help you: attach a .love.
User avatar
Ref
Party member
Posts: 702
Joined: Wed May 02, 2012 11:05 pm

Re: Rounded Rectangles

Post by Ref »

I have some reservations about my modifications.
Not overly confident that the code is 100% but you are welcome to do what ever you want with it.
Best

Edit: As expected, an error.

Code: Select all

rx = rx >= w / 2 and w / 2 - 1 or rx
ry = ry >= h / 2 and h / 2 - 1 or ry
Have to add 'equal' to avoid the problem with gaps forming when 'line' mode is used with love.polygon on polygons containing redundant points.
The code now is:

Code: Select all

function radiusRectangle( mode, x, y, w, h, rx, ry )
	ry = ry or rx
	local pts = {}
	local precision = math.floor( 0.2 * ( rx + ry ) )
	local  hP =  math.pi / 2
	rx = rx >= w / 2 and w / 2 - 1 or rx
	ry = ry >= h / 2 and h / 2 - 1 or ry
	local sin, cos = math.sin, math.cos
	for i = 0, precision do	-- upper right
		local a = ( i / precision - 1 ) * hP
		pts[#pts+1] = x + w - rx * ( 1 -  cos(a) )
		pts[#pts+1] = y + ry * ( 1 +  sin(a) )
		end
	for i = 2 * precision + 2 , 1, -2 do	-- lower right
		pts[#pts+1] = pts[i-1]
		pts[#pts+1] = 2 * y - pts[i] + h
		end
	for i = 1, 2 * precision + 2, 2 do	-- lower left
		pts[#pts+1] = -pts[i] + 2 * x + w
		pts[#pts+1] = 2 * y - pts[i+1] + h
		end
	for i = 2 * precision+2 , 1, -2 do	-- upper left
		pts[#pts+1]	= -pts[i-1] + 2 * x + w
		pts[#pts+1]	= pts[i]
		end
	love.graphics.polygon( mode, pts )
	end
(Not really sure why you used clockwise point order???)
User avatar
SpotlightKid
Prole
Posts: 6
Joined: Mon Nov 10, 2014 7:57 am
Location: Cologne, Germany

Re: Rounded Rectangles

Post by SpotlightKid »

Here's my version, which I ported from similar code I had for PyGame.

It draws the rectangle as a polygon to a canvas and returns this, so the rectangle can be reused efficiently. It also allows to set a different foreground (border) and background (fill) color. Both may have alpha ~= 255, but if the foreground color has transparency, it will overlap with the background by half the border width (I deemed this acceptable to avoid having to calculate different polygons for the border and the background).

The code could be optimzed as in the above posts by calculating only one of the rounded corners and transposing/rotating it for the others, but since you only need to calculate the rectangle coordinates once and then just redraw the canvas (unless the shape changes), I didn't think it very important.

Here's a demo .love:
roundedrect.love
(252.86 KiB) Downloaded 134 times
Rounded rectangles screenshot
Rounded rectangles screenshot
screenshot-20141110-085615.png (110.59 KiB) Viewed 1873 times
(In the screenshot I used the DroidSans font instead of the default font, but did not include it in the .love. Kudos to Triplenox for the background pic.)

Code: Select all

--- Draw rectangles with rounded corners with LÖVE (http://love2d.org)
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M

local ipairs = ipairs
local lg = love.graphics
local PI = math.pi
local sin = math.sin
local cos = math.cos
local unpack = unpack

if _ENV then
    _ENV = M
else
    setfenv(1, M)
end

local TWOPI = PI * 2
local HALFPI = PI * 0.5
local PI32 = PI + HALFPI

function extend(t1, t2)
    for _, v in ipairs(t2) do
        t1[#t1+1] = v
    end
end

--- Return table of coordinates of a circle arc with step resolution
function arc_coords(x, y, radius, start, stop, step)
    step = step or (TWOPI * 0.01)
    if start > stop then
        step = -step
    end

    local coords = {}
    for angle = start, stop, step do
        coords[#coords+1] = x + cos(angle) * radius
        coords[#coords+1] = y + sin(angle) * radius
    end
    return coords
end

--- Draw rectangle with rounded corners to a canvas and return it
function draw_rect(mode, w, h, radius, color, bgcolor, linewidth, aa, step)
    radius = radius or 0
    linewidth = linewidth or 1
    local hlw = linewidth * 0.5
    local x1 = radius + hlw
    local y1 = radius + hlw
    local x2 = w - radius - hlw
    local y2 = h - radius - hlw

    if radius > 0 then
        coords = arc_coords(x1, y1, radius, PI, PI32, step)
        extend(coords, arc_coords(x2, y1, radius, PI32, TWOPI, step))
        extend(coords, arc_coords(x2, y2, radius, 0, HALFPI, step))
        extend(coords, arc_coords(x1, y2, radius, HALFPI, PI, step))
    else
        coords = {x1, hlw, x2, hlw, w - hlw, y2, x1, h - hlw}
    end

    local canvas = lg.newCanvas(w + linewidth, h + linewidth, "normal", aa)
    lg.setCanvas(canvas)
        canvas:clear()
        lg.setBackgroundColor({255, 255, 255, 0})
        lg.setBlendMode('alpha')
        lg.setLineStyle("smooth")

        if bgcolor then
            lg.setLineWidth(0)
            lg.setColor(unpack(bgcolor))
            lg.polygon("fill", coords)
        end

        lg.setColor(unpack(color))
        lg.setLineWidth(linewidth)

        lg.polygon(mode, coords)
    lg.setCanvas()
    return canvas
end
Also available as a Gist here.
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests