local easelib = require 'easelib' -- utils local function keys(t) local k = {} for n in pairs(t) do table.insert(k, n) end return k end local function skeys(t) local k = {} for n,v in pairs(t) do table.insert(k, {n, v}) end table.sort(k, function(a, b) return a[2].i < b[2].i end) local k2 = {} for _,v in ipairs(k) do table.insert(k2, v[1]) end return k2 end local function mix(x, y, a) return x * (1 - a) + y * a end -- eases local function mixEase(e1, e2, point) if not point then point = 0.5 end return function(a) if a < point then return e1(a / point) * point else return e2((a - point) / (1 - point)) * (1 - point) + point end end end local eases = {} for i,v in pairs(easelib) do local min = 0 local q = 10 for i = 0, q do local s = v[2](i / q) if s < 0 then min = -1 end end eases[v[1]] = { f = v[2], max = 1, min = min, i = i } end local ease local minEase = false -- rendering constants local padding = 6 local margin = 4 local quality = 256 local mixpoint = 0.5 local oldmixpoint = 0.5 local mixpointtimer = 0 -- easter egg thing local mixpointspin = 0 local maxDropdown = 16 -- graph local graph = {} -- dropdown bullshit local dropdowns = {} local dropdownValueCache = {} local openDropdown = 0 local dropdownScroll = 0 local dropdownScrollE = 0 local function selected(index) return dropdowns[index].options[dropdowns[index].selected] end local function kget(key) for _, v in ipairs(dropdowns) do if v.name == key then return v end end end local function kselected(key) for _, v in ipairs(dropdowns) do if v.name == key then return v.options[v.selected] end end end local dropdownId local function insertDropdown(tab, f) dropdownId = dropdownId + 1 f.selected = (kget(f.name) or dropdownValueCache[f.name] or {selected = 1}).selected f.selected = (f.selected - 1) % #f.options + 1 return table.insert(tab, f) end local function createDropdowns() local d = {} dropdownId = 0 insertDropdown(d, { x = padding, y = padding, width = 128, options = { 'Preview Ease', 'Mix Eases', 'Create Ease' }, name = 'mode' }) if d[dropdownId].selected == 1 then -- preview ease insertDropdown(d, { x = padding + 128 + padding, y = padding, width = 128, options = skeys(eases), name = 'ease1' }) ease = eases[d[dropdownId].options[d[dropdownId].selected]].f elseif d[dropdownId].selected == 2 then -- mix eases insertDropdown(d, { x = padding + 128 + padding, y = padding, width = 128, options = skeys(eases), name = 'ease1' }) insertDropdown(d, { x = padding + 128 + padding + 128 + padding, y = padding, width = 128, options = skeys(eases), name = 'ease2' }) ease = mixEase(eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]].f, eases[d[dropdownId].options[d[dropdownId].selected]].f, mixpoint) elseif d[dropdownId].selected == 3 then -- create eases insertDropdown(d, { x = padding + 128 + padding, y = padding, width = 128, options = skeys(eases), name = 'ease1' }) end minEase = (kselected('ease1') and eases[kselected('ease1')].min == -1) or (kselected('ease2') and eases[kselected('ease2')].min == -1) dropdowns = d end -- rendering function love.load() createDropdowns() end function love.update(dt) for i = 1, quality do local a = (i - 1) / (quality - 1) if not graph[i] then graph[i] = ease(a) end end for i,v in ipairs(graph) do local a = (i - 1) / (quality - 1) local b = ease(a) if minEase then b = b / 2 + 0.5 end graph[i] = mix(v, b, math.min(dt * 18, 1)) end mixpointtimer = mix(mixpointtimer + math.abs(mixpoint - oldmixpoint), 0, math.min(dt * 8)) oldmixpoint = mix(oldmixpoint, mixpoint, math.min(dt * 20, 1)) if mixpointtimer > 2 then mixpointtimer = mixpointtimer - 2 mixpointspin = mixpointspin + 4 end mixpointspin = mix(mixpointspin, 0, dt * 3) if openDropdown ~= 0 then dropdownScroll = math.max(dropdownScroll, -(#dropdowns[openDropdown].options - maxDropdown)) dropdownScroll = math.min(dropdownScroll, 0) dropdownScrollE = mix(dropdownScrollE, dropdownScroll, dt * 10) end end function love.draw() local mode = kget('mode').selected local sw, sh = love.graphics.getDimensions() local mx, my = love.mouse.getPosition() love.graphics.setColor(0.09, 0.09, 0.12, 1) love.graphics.rectangle('fill', 0, 0, sw, sh) love.graphics.setColor(0.08, 0.08, 0.1, 1) love.graphics.rectangle('line', 0, 0, sw, sh) love.graphics.setColor(1, 1, 1, 1) love.graphics.print('Box of Eases by oatmealine', padding, sh - love.graphics.getFont():getHeight() - padding) -- sliders -- yeah we do a lil' hardcoding if mode == 2 then local x, y, w, h = padding, padding * 2 + love.graphics.getFont():getHeight() + margin, 128, 32 love.graphics.setColor(0.7, 0.7, 0.7, 0.4) love.graphics.line(x, y + h/2, x + w, y + h/2) local sx, sy = x + w * oldmixpoint, y + h/2 local ssize = h * 0.5 love.graphics.push() love.graphics.translate(sx, sy) love.graphics.rotate((mixpoint - oldmixpoint) * 4 + mixpointspin * math.pi * 2) love.graphics.setColor(0, 0, 0, 1) if mx > sx - ssize/2 and mx < sx + ssize/2 and my > sy - ssize/2 and my < sy + ssize/2 and openDropdown == 0 then love.graphics.setColor(0.2, 0.2, 0.3, 1) end love.graphics.rectangle('fill', -ssize/2, -ssize/2, ssize, ssize) love.graphics.setColor(1, 1, 1, 1) love.graphics.rectangle('line', -ssize/2, -ssize/2, ssize, ssize) love.graphics.rotate((mixpoint - oldmixpoint) * -2) love.graphics.setColor(1, 1, 1, 1) love.graphics.printf(math.floor(mixpoint * 100)/100, -ssize * 6, ssize - 2, ssize * 12, 'center') love.graphics.pop() if mx > x and mx < x + w and my > y and my < y + h and love.mouse.isDown(1) and openDropdown == 0 then mixpoint = (mx - x) / w createDropdowns() end end -- dropdowns for i,v in ipairs(dropdowns) do local x, y, w, h = v.x, v.y, v.width, love.graphics.getFont():getHeight() + margin love.graphics.setColor(0, 0, 0, 0.3) if love.mouse.getX() > x and love.mouse.getX() < x + w and love.mouse.getY() > y and love.mouse.getY() < y + h then love.graphics.setColor(0.8, 0.8, 1, love.mouse.isDown(1) and 0.4 or 0.3) end love.graphics.rectangle('fill', x, y, w, h) love.graphics.setColor(1, 1, 1, 1) love.graphics.rectangle('line', x, y, w, h) love.graphics.print(selected(i), x + margin/2, y + margin/2) love.graphics.rectangle('line', x + w - h, y, h, h) love.graphics.polygon('line', x + w - h/2 + 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2 - 0.3 * h, y + h/2 - 0.3 * h, x + w - h/2, y + h/2 + 0.3 * h) if openDropdown == i then for i,o in ipairs(v.options) do local x, y, w, h = x, y + i * h, w, h y = y + dropdownScrollE * h local gi = y / h if gi > maxDropdown or gi < 1 then goto continue end local a = 1 - math.min(math.max((1 - (maxDropdown - gi)) * (1 - (math.abs(dropdownScrollE) /(#v.options - maxDropdown))), 0), 1) love.graphics.setColor(0, 0, 0, 0.3 * a) if mx > x and mx < x + w and my > y and my < y + h then love.graphics.setColor(0.8, 0.8, 1, (love.mouse.isDown(1) and 0.4 or 0.3) * a) end love.graphics.rectangle('fill', x, y, w, h) love.graphics.setColor(1, 1, 1, 0.75 * a) love.graphics.rectangle('line', x, y, w, h) love.graphics.setColor(1, 1, 1, 1 * a) love.graphics.print(v.options[i], x + 2, y + 2) ::continue:: end -- scrollwheel if #v.options > maxDropdown then local displayed = maxDropdown / #v.options local scroll = math.abs(dropdownScrollE) / (#v.options - maxDropdown) local size = margin love.graphics.setColor(1, 1, 1, 0.9) love.graphics.rectangle('fill', x + w - size, y + h + scroll * (1 - displayed) * (maxDropdown - 1) * h, size, displayed * (maxDropdown - 1) * h) end end end -- graph if mode == 1 or mode == 2 then local csize = 10 -- preview point size local size = math.min((sw - padding) - ((kget('ease2') or kget('ease1')).x + 128 + padding), sh - padding * 5 - csize) local x, y, w, h = sw - padding - size, padding, size, size love.graphics.setColor(1, 1, 1, 1) love.graphics.rectangle('line', x, y, w, h) -- grid love.graphics.setColor(0.2, 0.2, 0.4, 0.2) local gridsize = 64 for gx = 1, gridsize - 2 do love.graphics.line(x + margin + gx * w/gridsize, y + margin, x + margin + gx * w/gridsize, y + h - margin) end for gy = 1, gridsize - 2 do love.graphics.line(x + margin, y + margin + gy * h/gridsize, x + w - margin, y + margin + gy * h/gridsize) end -- mixease point if mode == 2 then love.graphics.setColor(1, 1, 1, 0.8) love.graphics.line(x + margin + mixpoint * w, y, x + margin + mixpoint * w, y + h) end -- preview point local t = love.timer.getTime() % 1 love.graphics.setColor(0.4, 0.4, 1, 0.4) love.graphics.line(x + margin + t * w, y, x + margin + t * w, y + h) -- y = 0 point -- todo: this will break with eases that dont have the first point at y0 local py = graph[1] love.graphics.setColor(0.7, 0.7, 0.7, 0.4 * (1 - math.abs(py - 0.5) / 0.5)) love.graphics.line(x, y + h - py * h, x + w, y + py * h) -- polygone -- this isnt done with a polygon because else itd waste a Bunch of ram and i kinda, dont want to do that? love.graphics.setColor(1, 1, 1, 1) local last = graph[1] or 0 for gx = 1, quality - 1 do local a = gx/quality local b = graph[gx + 1] or 0 local px, py = x + margin + gx * ((w - margin)/quality), y + h - margin - b * (h - margin * 2) local ox, oy = x + margin + (gx - 1) * ((w - margin)/quality), y + h - margin - last * (h - margin * 2) if math.abs(b - last) < 1 then love.graphics.line(ox, oy, px, py) end last = b end -- preview love.graphics.setColor(1, 1, 1, 0.2) love.graphics.line(x + margin, y + h + padding * 2 + csize/2, x + w - margin, y + h + padding * 2 + csize/2) love.graphics.setColor(0.4, 0.4, 1, 1) local a1 = ease(t) local a2 = ease(math.max(math.min(t - 0.1, 1), 0)) local da = a1 if love.timer.getTime() % 2 < 1 and math.floor(ease(0) + 0.5) ~= math.floor(ease(1) + 0.5) then da = 1 - da end if minEase then da = da / 2 + 0.5 end love.graphics.ellipse('fill', x + margin + (w - margin * 2) * da, y + h + padding * 2 + csize/2, csize * (1 + math.min(math.abs(a1 - a2), 3) * 1.2), csize) end end function love.mousepressed(x, y, m) local clickedDropdown = false for i,v in ipairs(dropdowns) do local h = love.graphics.getFont():getHeight() + margin if openDropdown == 0 then if x > v.x and x < v.x + v.width and y > v.y and y < v.y + h + margin then if m == 1 then openDropdown = i clickedDropdown = true dropdownScroll = 0 dropdownScrollE = 0 elseif m == 3 then dropdowns[i].selected = math.random(1, #dropdowns[i].options) createDropdowns() end end end if openDropdown == i then if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then clickedDropdown = true end end end if not clickedDropdown and m == 1 then openDropdown = 0 end end function love.mousereleased(x, y, m) for i,v in ipairs(dropdowns) do local h = love.graphics.getFont():getHeight() + margin if openDropdown == i then if x > v.x and x < v.x + v.width and y > v.y + h and y < v.y + h * (math.min(#v.options, maxDropdown) + 1) and m == 1 then v.selected = math.floor((y - v.y) / h - dropdownScrollE) openDropdown = 0 dropdownValueCache[v.name] = {selected = v.selected} createDropdowns() end end end end function love.wheelmoved(x, y) if y == 0 then return end if openDropdown ~= 0 then dropdownScroll = dropdownScroll + y else local mx, my = love.mouse.getPosition() for i,v in ipairs(dropdowns) do local h = love.graphics.getFont():getHeight() + margin if mx > v.x and mx < v.x + v.width and my > v.y and my < v.y + h + margin then dropdowns[i].selected = dropdowns[i].selected - math.floor(y) dropdowns[i].selected = (dropdowns[i].selected - 1) % #dropdowns[i].options + 1 createDropdowns() end end end end