diff --git a/Inter-Regular.otf b/Inter-Regular.otf new file mode 100644 index 0000000..e94fa45 Binary files /dev/null and b/Inter-Regular.otf differ diff --git a/README.md b/README.md index 9abb1a9..b5ec166 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,8 @@ Then launch the game like so: https://love2d.org/wiki/Getting_Started#Running_Ga ![Previewing eases](./screenshot1.png) -![Mixing eases](./screenshot2.png) \ No newline at end of file +![Mixing eases](./screenshot2.png) + +## Attributions + +Inter V Font: by The Inter Project Authors \ No newline at end of file diff --git a/conf.lua b/conf.lua index e4d7dda..0044594 100644 --- a/conf.lua +++ b/conf.lua @@ -2,6 +2,8 @@ function love.conf(t) t.identity = 'box-of-eases' t.version = '11.3' + t.window.width = 1080 + t.window.height = 800 t.window.resizable = true t.window.title = 'Box of Eases' t.window.icon = 'logo.png' diff --git a/dropdown.lua b/dropdown.lua index 36b84fa..ddd79c8 100644 --- a/dropdown.lua +++ b/dropdown.lua @@ -1,8 +1,20 @@ local self = {} +local icon = { + inease = love.graphics.newImage('inease-icon.png'), + outease = love.graphics.newImage('outease-icon.png'), + inoutease = love.graphics.newImage('inoutease-icon.png'), + transientease = love.graphics.newImage('transientease-icon.png'), + unknownease = love.graphics.newImage('unknownease-icon.png'), + linearease = love.graphics.newImage('linearease-icon.png'), + instantease = love.graphics.newImage('instantease-icon.png'), + pulseease = love.graphics.newImage('pulseease-icon.png'), +} + local dropdowns = {} local dropdownValueCache = {} +local dropdownScrollCache = {} local maxDropdown = 16 @@ -19,6 +31,22 @@ local function skeys(t) return k2 end +local function icons(keys) + local e = {} + for i,v in ipairs(keys) do + table.insert(e, (ease.eases[v].type or 'unknown') .. 'ease') + end + return e +end + +local function params(keys) + local e = {} + for i,v in ipairs(keys) do + table.insert(e, ease.eases[v].params) + end + return e +end + function self.get(index) return dropdowns[index] end @@ -69,13 +97,17 @@ function self.createDropdowns() local param1 = {} local param2 = {} + local eases = skeys(ease.eases) + if d[dropdownId].selected == 1 then -- preview ease insertDropdown(d, { x = outerpadding + dropdownWidth + padding, y = outerpadding, width = dropdownWidth, - options = skeys(ease.eases), - name = 'ease1' + options = eases, + name = 'ease1', + icons = icons(eases), + params = params(eases) }) local _e = ease.eases[d[dropdownId].options[d[dropdownId].selected]] param1[1] = slider.kvalue(_e.name .. 'param11') or (_e.params[1] and _e.params[1].default) or 1 @@ -88,15 +120,19 @@ function self.createDropdowns() x = outerpadding + dropdownWidth + padding, y = outerpadding, width = dropdownWidth, - options = skeys(ease.eases), - name = 'ease1' + options = eases, + name = 'ease1', + icons = icons(eases), + params = params(eases) }) insertDropdown(d, { x = outerpadding + dropdownWidth + padding + dropdownWidth + padding, y = outerpadding, width = dropdownWidth, - options = skeys(ease.eases), - name = 'ease2' + options = eases, + name = 'ease2', + icons = icons(eases), + params = params(eases) }) local _e1 = ease.eases[d[dropdownId - 1].options[d[dropdownId - 1].selected]] @@ -111,8 +147,9 @@ function self.createDropdowns() x = outerpadding + dropdownWidth + padding, y = outerpadding, width = dropdownWidth, - options = skeys(ease.eases), - name = 'ease1' + options = eases, + name = 'ease1', + icons = icons(eases) }) end @@ -142,7 +179,7 @@ function self.render() for i,v in ipairs(dropdowns) do local x, y, w, h = v.x, v.y, v.width, fontHeight + margin - love.graphics.setColor(0.06, 0.06, 0.12, 0.3) + love.graphics.setColor(0.06, 0.06, 0.12, 0.6) 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 @@ -151,8 +188,16 @@ function self.render() love.graphics.setColor(1, 1, 1, 1) love.graphics.rectangle('line', x, y, w, h) - love.graphics.print(self.selected(i), x + margin/2, y + margin/2) + + if v.icons and v.icons[v.selected] then + local sprite = icon[v.icons[v.selected]] + love.graphics.draw(sprite, x + 2, y + 2, 0, fontHeight / sprite:getWidth(), fontHeight / sprite:getHeight()) + love.graphics.print(self.selected(i), x + margin/2 + fontHeight, y + margin/2) + else + love.graphics.print(self.selected(i), x + margin/2, y + margin/2) + end -- love.graphics.rectangle('line', x + w - h, y, h, h) + love.graphics.setLineWidth(1) 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 self.openDropdown == i or v.open > 0.01 then @@ -168,9 +213,9 @@ function self.render() -- help local a = (1 - math.min(math.max((1 - (maxDropdown - gi)) * (1 - (math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1))), 0), 1)) * math.max(math.min(gi - 1, 1), 0) * v.open - love.graphics.setColor(0.06, 0.06, 0.12, 0.3 * a) + love.graphics.setColor(0.06, 0.06, 0.12, 0.6 * 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) + love.graphics.setColor(0.4, 0.4, 1, (love.mouse.isDown(1) and 0.8 or 0.7) * a) end love.graphics.rectangle('fill', x, y, w, h) @@ -178,7 +223,32 @@ function self.render() 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) + if i == v.selected then + love.graphics.setColor(0.5, 0.5, 1, 1 * a) + end + + if v.icons and v.icons[i] then + local sprite = icon[v.icons[i]] + love.graphics.draw(sprite, x + 2, y + 2, 0, fontHeight / sprite:getWidth(), fontHeight / sprite:getHeight()) + love.graphics.print(v.options[i], x + 2 + fontHeight, y + 2) + else + love.graphics.print(v.options[i], x + 2, y + 2) + end + + if v.params and v.params[i] then + local str = '' + for _,p in ipairs(v.params[i]) do + str = str .. ' ' .. string.sub(p.name, 1, 1) + end + + love.graphics.push() + love.graphics.translate(x + w, y + lineWidth/2) + love.graphics.shear(-0.2, 0) + love.graphics.scale(0.8, 0.8) + love.graphics.setColor(0.8, 0.8, 1, 0.8 * a) + love.graphics.printf(str, -w, -lineWidth/2, w - 2, 'right') + love.graphics.pop() + end ::continue:: end @@ -188,12 +258,13 @@ function self.render() if #v.options > maxDropdown then local displayed = maxDropdown / (#v.options) local scroll = math.abs(dropdownScrollE) / (#v.options - maxDropdown + 1) - local size = margin + local size = 3 - love.graphics.setColor(1, 1, 1, 0.9 * v.open) + love.graphics.setColor(1, 1, 1, 0.8 * v.open) love.graphics.rectangle('fill', x + w - size, y + h + scroll * (1 - displayed) * (maxDropdown - 1) * h * v.open, size, displayed * (maxDropdown - 1) * h * v.open) end end + love.graphics.setLineWidth(lineWidth) end end @@ -206,8 +277,8 @@ function self.mousepressed(x, y, m) if m == 1 then self.openDropdown = i clickedDropdown = true - dropdownScroll = 0 - dropdownScrollE = 0 + dropdownScroll = dropdownScrollCache[i] or 0 + dropdownScrollE = dropdownScrollCache[i] or 0 elseif m == 3 then dropdowns[i].selected = math.random(1, #dropdowns[i].options) createUI() @@ -221,6 +292,7 @@ function self.mousepressed(x, y, m) end if not clickedDropdown and m == 1 then + dropdownScrollCache[self.openDropdown] = dropdownScroll self.openDropdown = 0 return true end @@ -234,6 +306,7 @@ function self.mousereleased(x, y, m) v.selected = math.floor((y - v.y) / h - dropdownScrollE) self.openDropdown = 0 dropdownValueCache[v.name] = {selected = v.selected} + dropdownScrollCache[i] = dropdownScroll createUI() end end diff --git a/ease.lua b/ease.lua index 8a5fbce..793f8f5 100644 --- a/ease.lua +++ b/ease.lua @@ -37,7 +37,8 @@ for i,v in pairs(easelib) do min = min, i = i, name = v[1], - params = params + params = params, + type = v.type } end diff --git a/easelib.lua b/easelib.lua index 706381e..35aee15 100644 --- a/easelib.lua +++ b/easelib.lua @@ -19,52 +19,52 @@ self = setmetatable(self, { end }) -table.insert(self, {'linear', function(t) return t end}) -table.insert(self, {'instant', function() return 1 end}) +table.insert(self, {'linear', function(t) return t end, type = 'linear'}) +table.insert(self, {'instant', function() return 1 end, type = 'instant'}) -table.insert(self, {'bounce', function(t) return 4 * t * (1 - t) end}) -table.insert(self, {'tri', function(t) return 1 - abs(2 * t - 1) end}) -table.insert(self, {'bell', function(t) return self.inOutQuint(self.tri(t)) end}) -table.insert(self, {'pop', function(t) return 3.5 * (1 - t) * (1 - t) * sqrt(t) end}) -table.insert(self, {'tap', function(t) return 3.5 * t * t * sqrt(1 - t) end}) -table.insert(self, {'pulse', function(t) return t < .5 and self.tap(t * 2) or -self.pop(t * 2 - 1) end}) +table.insert(self, {'bounce', function(t) return 4 * t * (1 - t) end, type = 'transient'}) +table.insert(self, {'tri', function(t) return 1 - abs(2 * t - 1) end, type = 'transient'}) +table.insert(self, {'bell', function(t) return self.inOutQuint(self.tri(t)) end, type = 'transient'}) +table.insert(self, {'pop', function(t) return 3.5 * (1 - t) * (1 - t) * sqrt(t) end, type = 'transient'}) +table.insert(self, {'tap', function(t) return 3.5 * t * t * sqrt(1 - t) end, type = 'transient'}) +table.insert(self, {'pulse', function(t) return t < .5 and self.tap(t * 2) or -self.pop(t * 2 - 1) end, type = 'pulse'}) -table.insert(self, {'spike', function(t) return exp(-10 * abs(2 * t - 1)) end}) -table.insert(self, {'inverse', function(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end}) +table.insert(self, {'spike', function(t) return exp(-10 * abs(2 * t - 1)) end, type = 'transient'}) +table.insert(self, {'inverse', function(t) return t * t * (1 - t) * (1 - t) / (0.5 - t) end, type = 'pulse'}) table.insert(self, {'popElastic', function(t, damp, count) return (1000 ^ -(t ^ damp) - 0.001) * sin(count * pi * t) -end, {1, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}}) +end, {0, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}, type = 'pulse'}) table.insert(self, {'tapElastic', function(t, damp, count) return (1000 ^ -((1 - t) ^ damp) - 0.001) * sin(count * pi * (1 - t)) -end, {1, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}}) +end, {0, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}, type = 'pulse'}) table.insert(self, {'pulseElastic', function(t, damp, count) if t < .5 then return self.tapElastic(t * 2, damp, count) else return -self.popElastic(t * 2 - 1, damp, count) end -end, {1, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}}) +end, {0, 10, 1.4, 'damp'}, {1, 25, 6, 'count'}, type = 'pulse'}) table.insert(self, {'impulse', function(t, damp) t = t ^ damp return t * (1000 ^ -t - 0.001) * 18.6 -end, {0, 10, 0.9, 'damp'}}) +end, {0, 10, 0.9, 'damp'}, type = 'transient'}) table.insert(self, {'inSine', function(x) return 1 - cos(x * (pi * 0.5)) -end}) +end, type = 'in'}) table.insert(self, {'outSine', function(x) return sin(x * (pi * 0.5)) -end}) +end, type = 'out'}) table.insert(self, {'inOutSine', function(x) return 0.5 - 0.5 * cos(x * pi) -end}) +end, type = 'inout'}) -table.insert(self, {'inQuad', function(t) return t * t end}) -table.insert(self, {'outQuad', function(t) return -t * (t - 2) end}) +table.insert(self, {'inQuad', function(t) return t * t end, type = 'in'}) +table.insert(self, {'outQuad', function(t) return -t * (t - 2) end, type = 'out'}) table.insert(self, {'inOutQuad', function(t) t = t * 2 if t < 1 then @@ -72,9 +72,9 @@ table.insert(self, {'inOutQuad', function(t) else return 1 - 0.5 * (2 - t) ^ 2 end -end}) -table.insert(self, {'inCubic', function(t) return t * t * t end}) -table.insert(self, {'outCubic', function(t) return 1 - (1 - t) ^ 3 end}) +end, type = 'inout'}) +table.insert(self, {'inCubic', function(t) return t * t * t end, type = 'in'}) +table.insert(self, {'outCubic', function(t) return 1 - (1 - t) ^ 3 end, type = 'out'}) table.insert(self, {'inOutCubic', function(t) t = t * 2 if t < 1 then @@ -82,9 +82,9 @@ table.insert(self, {'inOutCubic', function(t) else return 1 - 0.5 * (2 - t) ^ 3 end -end}) -table.insert(self, {'inQuart', function(t) return t * t * t * t end}) -table.insert(self, {'outQuart', function(t) return 1 - (1 - t) ^ 4 end}) +end, type = 'inout'}) +table.insert(self, {'inQuart', function(t) return t * t * t * t end, type = 'in'}) +table.insert(self, {'outQuart', function(t) return 1 - (1 - t) ^ 4 end, type = 'out'}) table.insert(self, {'inOutQuart', function(t) t = t * 2 if t < 1 then @@ -92,9 +92,9 @@ table.insert(self, {'inOutQuart', function(t) else return 1 - 0.5 * (2 - t) ^ 4 end -end}) -table.insert(self, {'inQuint', function(t) return t ^ 5 end}) -table.insert(self, {'outQuint', function(t) return 1 - (1 - t) ^ 5 end}) +end, type = 'inout'}) +table.insert(self, {'inQuint', function(t) return t ^ 5 end, type = 'in'}) +table.insert(self, {'outQuint', function(t) return 1 - (1 - t) ^ 5 end, type = 'out'}) table.insert(self, {'inOutQuint', function(t) t = t * 2 if t < 1 then @@ -102,9 +102,9 @@ table.insert(self, {'inOutQuint', function(t) else return 1 - 0.5 * (2 - t) ^ 5 end -end}) -table.insert(self, {'inExpo', function(t) return 1000 ^ (t - 1) - 0.001 end}) -table.insert(self, {'outExpo', function(t) return 1.001 - 1000 ^ -t end}) +end, type = 'inout'}) +table.insert(self, {'inExpo', function(t) return 1000 ^ (t - 1) - 0.001 end, type = 'in'}) +table.insert(self, {'outExpo', function(t) return 1.001 - 1000 ^ -t end, type = 'out'}) table.insert(self, {'inOutExpo', function(t) t = t * 2 if t < 1 then @@ -112,9 +112,9 @@ table.insert(self, {'inOutExpo', function(t) else return 1.0005 - 0.5 * 1000 ^ (1 - t) end -end}) -table.insert(self, {'inCirc', function(t) return 1 - sqrt(1 - t * t) end}) -table.insert(self, {'outCirc', function(t) return sqrt(-t * t + 2 * t) end}) +end, type = 'inout'}) +table.insert(self, {'inCirc', function(t) return 1 - sqrt(1 - t * t) end, type = 'in'}) +table.insert(self, {'outCirc', function(t) return sqrt(-t * t + 2 * t) end, type = 'out'}) table.insert(self, {'inOutCirc', function(t) t = t * 2 if t < 1 then @@ -123,9 +123,9 @@ table.insert(self, {'inOutCirc', function(t) t = t - 2 return 0.5 + 0.5 * sqrt(1 - t * t) end -end}) +end, type = 'inout'}) -table.insert(self, {'inBounce', function(t) return 1 - self.outBounce(1 - t) end}) +table.insert(self, {'inBounce', function(t) return 1 - self.outBounce(1 - t) end, type = 'in'}) table.insert(self, {'outBounce', function(t) if t < 1 / 2.75 then return 7.5625 * t * t @@ -139,37 +139,37 @@ table.insert(self, {'outBounce', function(t) t = t - 2.625 / 2.75 return 7.5625 * t * t + 0.984375 end -end}) +end, type = 'out'}) table.insert(self, {'inOutBounce', function(t) if t < 0.5 then return self.inBounce(t * 2) * 0.5 else return self.outBounce(t * 2 - 1) * 0.5 + 0.5 end -end}) +end, type = 'inout'}) table.insert(self, {'inElastic', function(t, a, p) return 1 - self.outElastic(1 - t, a, p) -end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true}) +end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true, type = 'in'}) table.insert(self, {'outElastic', function(t, a, p) return a * pow(2, -10 * t) * sin((t - p / (2 * pi) * asin(1/a)) * 2 * pi / p) + 1 -end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true}) +end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true, type = 'out'}) table.insert(self, {'inOutElastic', function(t, a, p) return t < 0.5 and 0.5 * self.inElastic(t * 2, a, p) or 0.5 + 0.5 * self.outElastic(t * 2 - 1, a, p) -end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true}) +end, {1, 3, 1, 'a'}, {0, 2, 0.3, 'p'}, overridemin = true, type = 'inout'}) table.insert(self, {'inBack', function(t, a) return t * t * (a * t + t - a) -end, {0, 3, 1.70158, 'a'}}) +end, {0, 3, 1.70158, 'a'}, overridemin = true, type = 'in'}) table.insert(self, {'outBack', function(t, a) t = t - 1 return t * t * ((a + 1) * t + a) + 1 -end, {0, 3, 1.70158, 'a'}}) +end, {0, 3, 1.70158, 'a'}, overridemin = true, type = 'out'}) table.insert(self, {'inOutBack', function(t, a) return t < 0.5 and 0.5 * self.inBack(t * 2, a) or 0.5 + 0.5 * self.outBack(t * 2 - 1, a) -end, {0, 3, 1.70158, 'a'}}) +end, {0, 3, 1.70158, 'a'}, overridemin = true, type = 'inout'}) return self diff --git a/graph.lua b/graph.lua index 30ae18f..4f1b771 100644 --- a/graph.lua +++ b/graph.lua @@ -37,7 +37,7 @@ function self.update(dt) local mx, my = love.mouse.getPosition() if not (love.mouse.isDown(1) and mx > x and mx < x + w and my > y and my < y + h) then - timer = (timer + dt) % 2 + timer = (timer + dt * (slider.kvalue('bpm')/120)) % 2 else timer = mix(timer, (mx - x) / w, dt * 14) end diff --git a/inease-icon.png b/inease-icon.png new file mode 100644 index 0000000..e559a14 Binary files /dev/null and b/inease-icon.png differ diff --git a/inoutease-icon.png b/inoutease-icon.png new file mode 100644 index 0000000..2483479 Binary files /dev/null and b/inoutease-icon.png differ diff --git a/instantease-icon.png b/instantease-icon.png new file mode 100644 index 0000000..7b733c3 Binary files /dev/null and b/instantease-icon.png differ diff --git a/linearease-icon.png b/linearease-icon.png new file mode 100644 index 0000000..5b41296 Binary files /dev/null and b/linearease-icon.png differ diff --git a/main.lua b/main.lua index cc95a1a..3c1042d 100644 --- a/main.lua +++ b/main.lua @@ -3,6 +3,9 @@ for k, v in pairs(_G) do table.insert(default_G, k) end +love.graphics.setDefaultFilter('nearest', 'nearest') +interfaceFont = love.graphics.newFont('Inter-Regular.otf', 20) + ease = require 'ease' slider = require 'slider' @@ -21,7 +24,8 @@ require 'util' -- exports into global table padding = 16 outerpadding = 22 margin = 6 -dropdownWidth = 128 +dropdownWidth = 186 +lineWidth = 2 fontHeight = love.graphics.getFont():getHeight() -- global for convinience's sake @@ -29,6 +33,7 @@ fontHeight = love.graphics.getFont():getHeight() mode = nil function love.load() + love.graphics.setFont(interfaceFont) fontHeight = love.graphics.getFont():getHeight() createUI() end @@ -43,6 +48,8 @@ function love.draw() local sw, sh = love.graphics.getDimensions() local mx, my = love.mouse.getPosition() + love.graphics.setLineWidth(2) + 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) diff --git a/outease-icon.png b/outease-icon.png new file mode 100644 index 0000000..32b6f23 Binary files /dev/null and b/outease-icon.png differ diff --git a/pulseease-icon.png b/pulseease-icon.png new file mode 100644 index 0000000..fcb7692 Binary files /dev/null and b/pulseease-icon.png differ diff --git a/slider.lua b/slider.lua index fd880a6..d2bcb2a 100644 --- a/slider.lua +++ b/slider.lua @@ -29,6 +29,9 @@ local function insertSlider(tab, f) f.oldvalue = (self.kget(f.name) or {oldvalue = f.value}).oldvalue f.spintimer = (self.kget(f.name) or {spintimer = 0}).spintimer f.spin = (self.kget(f.name) or {spin = 0}).spin + if f.snap then + f.value = math.floor(f.value / f.snap) * f.snap + end return table.insert(tab, f) end function self.createSliders() @@ -47,6 +50,19 @@ function self.createSliders() displayname = 'Mix' }) end + if mode == 1 or mode == 2 then -- bpm slider + insertSlider(s, { + x = outerpadding, + y = love.graphics.getHeight() - outerpadding - fontHeight * 3 - padding, + width = dropdownWidth, + min = 80, + max = 220, + default = 120, + name = 'bpm', + displayname = 'BPM', + snap = 1, + }) + end local ease1 = ease.eases[dropdown.kselected('ease1')] local ease2 = ease.eases[dropdown.kselected('ease2')] @@ -108,7 +124,7 @@ function self.render() local mx, my = love.mouse.getPosition() for i, v in ipairs(sliders) do - local x, y, w, h = v.x, v.y, v.width, 32 + local x, y, w, h = v.x, v.y, v.width, fontHeight * 1.25 love.graphics.setColor(0.7, 0.7, 0.7, 0.4) love.graphics.line(x, y + h/2, x + w, y + h/2) @@ -121,7 +137,7 @@ function self.render() love.graphics.line(x + normaldefault * w, y, x + normaldefault * w, y + h) local sx, sy = x + w * normaloldvalue, y + h/2 - local ssize = h * 0.5 + local ssize = h * 0.75 love.graphics.push() @@ -146,7 +162,7 @@ function self.render() love.graphics.pop() - love.graphics.printf(v.displayname, v.x + margin * 2 - ssize * 6, v.y - 8, ssize * 12, 'center') + love.graphics.printf(v.displayname, v.x + margin * 2 - ssize * 6, v.y - ssize, ssize * 12, 'center') if dragging then v.value = ((mx - (x + 1)) / (w - 2)) * (v.max - v.min) + v.min diff --git a/transientease-icon.png b/transientease-icon.png new file mode 100644 index 0000000..2e2c61f Binary files /dev/null and b/transientease-icon.png differ diff --git a/unknownease-icon.png b/unknownease-icon.png new file mode 100644 index 0000000..77a446a Binary files /dev/null and b/unknownease-icon.png differ