Bezier Animation Demo

At least this is something that also runs with loveCodea on Linux. The problems under Linux seem to be sprite related, I think.

Visit this inspirational page to see what you can expect from the code below:

http://www.jasondavies.com/animated-bezier/

(And I leave you wondering if this posting is more about loveCodea or Bezier Curves.)


if require then
    require("loveCodea")
end

-- Interactive Bezier Curve Animation, loosely inspired by this:
-- http://www.jasondavies.com/animated-bezier/

-- Tap on circle to create a new node.
-- Drag node to X to delete.

BEZIER_STEP = 0.01
STARTING_DOTS = 4

dots = {}
-- animation progess, 0 ... 1
p = 0
touches = {}
touched_dots = {}

function setup()
    for i = 1, STARTING_DOTS do NewDot() end
    -- for drawing fine lines
    noSmooth()
end

function draw()
    p = p + 0.01
    if p > 1 then p = 0 end
    background(255, 255, 230, 255)
    local curve = {}
    for i,d in ipairs(dots) do
        table.insert(curve, d.x)
        table.insert(curve, d.y)
    end
    Bezier(curve, p)
    -- green O in upper right
    fill(0, 255, 0)
    ellipse(WIDTH - 20, HEIGHT - 20, 40, 40)
    -- red X in lower right
    stroke(255, 0, 0)
    line(WIDTH, 0, WIDTH - 40, 40)
    line(WIDTH, 40, WIDTH - 40, 0)
end

function touched(touch)
    if touch.state == ENDED then
        touches[touch.id] = nil
        touched_dots[touch.id] = nil
        -- Circle touched, create a node
        if touch.x > WIDTH - 40 and touch.y > HEIGHT - 40 then
            NewDot()
        end
    else
        touches[touch.id] = touch
        local td = touched_dots[touch.id]
        -- Touch has hit nothing yet, try finding a dot nearby
        if td == nil then
            touched_dots[touch.id] = FindDot(touch.x, touch.y)
        else
            td.x = touch.x
            td.y = touch.y
            -- Dragging to X, delete node
            if td:Contains(WIDTH - 20, 20) then
                for i,d in ipairs(dots) do
                    if d == td then
                        table.remove(dots, i)
                        break
                    end
                end
            end
        end
    end
end

-- Creates a new dot somewhere
function NewDot()
    table.insert(dots, Dot(math.random(500), math.random(500)))
end

-- Returns if a there's a dot at x/y
function FindDot(x, y)
    local dot = nil
    for i,d in ipairs(dots) do
        if d:Contains(x, y) then dot = d end
    end
    return dot
end

-- Draws Bezier and helper lines.
-- xy_list: list with x/y coordinates of the nodes
-- p: progress (0 ... 1)
function Bezier(xy_list, p)
    DrawBezierOverlay(xy_list, p)
    DrawBezier(xy_list, p)
end

-- Draws Bezier Curve
function DrawBezier(xy_list, p)
    if #xy_list == 0 then return end
    stroke(0, 0, 0)
    strokeWidth(3)
    local x0, y0
    for i = 0, p, BEZIER_STEP do
        local x, y = ReduceChain(xy_list, i)
        if i > 0 then
            line(x0, y0, x, y)
        end
        x0, y0 = x, y
    end
end

-- Reduces the xy_list to a single x/y coordinate at progress p.
function ReduceChain(xy_list, p)
    local n_coords = #xy_list / 2
    while n_coords > 1 do
        xy_list = ReduceChainBy1(xy_list, p)
        n_coords = n_coords - 1
    end
    return xy_list[1], xy_list[2]
end

function DrawBezierOverlay(xy_list, p)
    local n_coords = #xy_list / 2
    local r = 20
    while n_coords > 0 do
        DrawChain(xy_list, r)
        xy_list = ReduceChainBy1(xy_list, p)
        n_coords = n_coords - 1
        r = 10
    end
end

-- Reduces the xy_list by one coordinate at progress p,
-- returning the reduced list.
function ReduceChainBy1(xy_list, p)
    local reduced = {}
    local n_coords = #xy_list / 2
    local x0, y0
    for i = 1, n_coords do
        local x = xy_list[i * 2 - 1]
        local y = xy_list[i * 2]
        -- skip first point, cannot draw a line with only 1 point
        if i > 1 then
            local xi = x0 + (x - x0) * p
            local yi = y0 + (y - y0) * p
            table.insert(reduced, xi)
            table.insert(reduced, yi)
        end
        x0, y0 = x, y
    end
    return reduced
end

-- Draws a chain, i.e. a series of dots linked by lines.
-- Draws the primary nodes as well as the reduced chain.
function DrawChain(xy_list, r)
    local n_coords = #xy_list / 2
    local x0
    local y0
    stroke(255, 100, 0)
    strokeWidth(1)
    fill(255, 100, 0)
    if n_coords == 1 then
    fill(100, 100, 100)
    end
    for i = 1, n_coords do
        local x = xy_list[i * 2 - 1]
        local y = xy_list[i * 2]
        if i > 1 then
            line(x0, y0, x, y)
        end
        ellipse(x, y, r)
        x0, y0 = x, y
    end
end


Dot = class()

function Dot:init(x, y)
    self.x = x
    self.y = y
    self.r = 20
end

function Dot:Contains(x, y)
    local dx = x - self.x
    local dy = y - self.y
    return (dx * dx) + (dy * dy) <= (self.r * self.r)
end

Hi @codeslinger,

Sooooperb!!!

Hope we can get these routines incorporated into the core code so that we can create Bezier designs within Codea. I’ll play around with your code to see if I can make a small demo with it.

Thanks for this.

Bri_G

:slight_smile:

WOW!'Great for entertaining simple minds like mine!

This is my favorite codea forum download yet by far… I just added like 100 points and it goes bannanas!!!