Rounded corners

natiny.render.rounded_rectangle takes a radius per corner - top-left, top-right, bottom-right, bottom-left, as in CSS. One call covers a card, a tab, a pill and a leaf.

The square's corners each keep their own rhythm, and the blue dots mark their arc centres. Radii too large for the box are scaled down together, not clamped at one corner.


    
main.lua
local natiny = require("natiny")
local demo = require("common.modules.demo")

-- Shared examples palette.
local BG     = {  21 / 255,  30 / 255,  44 / 255 } -- #151E2C
local OBJECT = { 169 / 255, 179 / 255, 197 / 255 } -- #A9B3C5
local ACCENT = { 248 / 255, 249 / 255, 253 / 255 } -- #F8F9FD

local function color(c, a)
    natiny.render.set_color(c[1], c[2], c[3], a or 1.0)
end

natiny.backend.init()

local win = natiny.window.create("Rounded corners", 900, 600)
natiny.window.set_scale_mode(win, natiny.window.SCALE_MODE_STRETCH)

local t = 0.0

local function frame(dt)
    natiny.window.bind(win, function()
        t = t + dt

        natiny.render.clear(BG[1], BG[2], BG[3], 1.0)

        -- ── One square, four corners, four rhythms ──────────────────
        local size = 220
        local x, y = 90, 60

        local r = {}
        for i = 1, 4 do
            -- each corner a quarter of a turn behind the one before it
            local phase = t * 1.3 + (i - 1) * math.pi * 0.5
            r[i] = (0.5 + 0.5 * math.sin(phase)) * (size * 0.5)
        end

        color(ACCENT, 0.14)
        natiny.render.rounded_rectangle(x, y, size, size, r[1], r[2], r[3], r[4])
        color(ACCENT)
        natiny.render.rounded_rectangle_outline(x, y, size, size,
                                             r[1], r[2], r[3], r[4], 3)

        -- the centre of each corner's arc, so which radius is which is
        -- never in doubt
        color(OBJECT)
        natiny.render.circle(x + r[1],        y + r[1],        4)
        natiny.render.circle(x + size - r[2], y + r[2],        4)
        natiny.render.circle(x + size - r[3], y + size - r[3], 4)
        natiny.render.circle(x + r[4],        y + size - r[4], 4)

        -- ── The vocabulary: one call, three shapes ──────────────────
        local vx, vw = 420, 400

        color(OBJECT)                                       -- a tab
        natiny.render.rounded_rectangle(vx, 70, vw, 90, 16, 16, 0, 0)
        color(ACCENT)
        natiny.render.rounded_rectangle_outline(vx, 70, vw, 90, 16, 16, 0, 0, 3)

        color(OBJECT)                                       -- a pill
        natiny.render.rounded_rectangle(vx, 180, vw, 60, 30)
        color(ACCENT)
        natiny.render.rounded_rectangle_outline(vx, 180, vw, 60, 30, 30, 30, 30, 3)

        color(OBJECT)                                       -- a leaf
        natiny.render.rounded_rectangle(vx, 260, vw, 60, 0, 30, 0, 30)
        color(ACCENT)
        natiny.render.rounded_rectangle_outline(vx, 260, vw, 60, 0, 30, 0, 30, 3)

        -- ── A wave of corners ───────────────────────────────────────
        -- One radius given to rounded_rectangle stands for all four, which
        -- is what this row wants: nine tiles, one phase apart.
        local n, tile, gap = 9, 76, 14
        local row = (900 - (n * tile + (n - 1) * gap)) * 0.5

        for i = 0, n - 1 do
            local squish = 0.5 + 0.5 * math.sin(t * 2.2 - i * 0.45)
            color(ACCENT, 0.9)
            natiny.render.rounded_rectangle(row + i * (tile + gap), 400,
                                          tile, tile, squish * tile * 0.5)
        end

        color(OBJECT)
        natiny.render.line(row, 500, row + n * tile + (n - 1) * gap, 500, 2)
    end)
end

demo:run({
    window = win,
    title  = "Rounded corners",
    text   = "Four radii per rectangle, one call",
    loop = frame,
})

natiny.backend.shutdown()