mirror of
https://github.com/kepler155c/opus
synced 2025-10-18 01:07:40 +00:00
format and installer branches
This commit is contained in:
@@ -14,357 +14,485 @@ Canvas.darkPalette = { }
|
||||
Canvas.grayscalePalette = { }
|
||||
|
||||
for n = 1, 16 do
|
||||
Canvas.colorPalette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n)
|
||||
Canvas.grayscalePalette[2 ^ (n - 1)] = _sub("088888878877787f", n, n)
|
||||
Canvas.darkPalette[2 ^ (n - 1)] = _sub("8777777f77fff77f", n, n)
|
||||
Canvas.colorPalette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n)
|
||||
Canvas.grayscalePalette[2 ^ (n - 1)] = _sub("088888878877787f", n, n)
|
||||
Canvas.darkPalette[2 ^ (n - 1)] = _sub("8777777f77fff77f", n, n)
|
||||
end
|
||||
|
||||
function Canvas:init(args)
|
||||
self.x = 1
|
||||
self.y = 1
|
||||
self.layers = { }
|
||||
self.x = 1
|
||||
self.y = 1
|
||||
self.layers = { }
|
||||
|
||||
Util.merge(self, args)
|
||||
Util.merge(self, args)
|
||||
|
||||
self.ex = self.x + self.width - 1
|
||||
self.ey = self.y + self.height - 1
|
||||
self.ex = self.x + self.width - 1
|
||||
self.ey = self.y + self.height - 1
|
||||
|
||||
if not self.palette then
|
||||
if self.isColor then
|
||||
self.palette = Canvas.colorPalette
|
||||
else
|
||||
self.palette = Canvas.grayscalePalette
|
||||
end
|
||||
end
|
||||
if not self.palette then
|
||||
if self.isColor then
|
||||
self.palette = Canvas.colorPalette
|
||||
else
|
||||
self.palette = Canvas.grayscalePalette
|
||||
end
|
||||
end
|
||||
|
||||
self.lines = { }
|
||||
for i = 1, self.height do
|
||||
self.lines[i] = { }
|
||||
end
|
||||
self.lines = { }
|
||||
for i = 1, self.height do
|
||||
self.lines[i] = { }
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:move(x, y)
|
||||
self.x, self.y = x, y
|
||||
self.ex = self.x + self.width - 1
|
||||
self.ey = self.y + self.height - 1
|
||||
self.x, self.y = x, y
|
||||
self.ex = self.x + self.width - 1
|
||||
self.ey = self.y + self.height - 1
|
||||
end
|
||||
|
||||
function Canvas:resize(w, h)
|
||||
for i = self.height, h do
|
||||
self.lines[i] = { }
|
||||
end
|
||||
for i = self.height, h do
|
||||
self.lines[i] = { }
|
||||
end
|
||||
|
||||
while #self.lines > h do
|
||||
table.remove(self.lines, #self.lines)
|
||||
end
|
||||
while #self.lines > h do
|
||||
table.remove(self.lines, #self.lines)
|
||||
end
|
||||
|
||||
if w ~= self.width then
|
||||
for i = 1, self.height do
|
||||
self.lines[i] = { dirty = true }
|
||||
end
|
||||
end
|
||||
if w ~= self.width then
|
||||
for i = 1, self.height do
|
||||
self.lines[i] = { dirty = true }
|
||||
end
|
||||
end
|
||||
|
||||
self.ex = self.x + w - 1
|
||||
self.ey = self.y + h - 1
|
||||
self.width = w
|
||||
self.height = h
|
||||
self.ex = self.x + w - 1
|
||||
self.ey = self.y + h - 1
|
||||
self.width = w
|
||||
self.height = h
|
||||
end
|
||||
|
||||
function Canvas:copy()
|
||||
local b = Canvas({
|
||||
x = self.x,
|
||||
y = self.y,
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
isColor = self.isColor,
|
||||
})
|
||||
for i = 1, self.height do
|
||||
b.lines[i].text = self.lines[i].text
|
||||
b.lines[i].fg = self.lines[i].fg
|
||||
b.lines[i].bg = self.lines[i].bg
|
||||
end
|
||||
return b
|
||||
local b = Canvas({
|
||||
x = self.x,
|
||||
y = self.y,
|
||||
width = self.width,
|
||||
height = self.height,
|
||||
isColor = self.isColor,
|
||||
})
|
||||
for i = 1, self.height do
|
||||
b.lines[i].text = self.lines[i].text
|
||||
b.lines[i].fg = self.lines[i].fg
|
||||
b.lines[i].bg = self.lines[i].bg
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
function Canvas:addLayer(layer)
|
||||
local canvas = Canvas({
|
||||
x = layer.x,
|
||||
y = layer.y,
|
||||
width = layer.width,
|
||||
height = layer.height,
|
||||
isColor = self.isColor,
|
||||
})
|
||||
canvas.parent = self
|
||||
table.insert(self.layers, canvas)
|
||||
return canvas
|
||||
local canvas = Canvas({
|
||||
x = layer.x,
|
||||
y = layer.y,
|
||||
width = layer.width,
|
||||
height = layer.height,
|
||||
isColor = self.isColor,
|
||||
})
|
||||
canvas.parent = self
|
||||
table.insert(self.layers, canvas)
|
||||
return canvas
|
||||
end
|
||||
|
||||
function Canvas:removeLayer()
|
||||
for k, layer in pairs(self.parent.layers) do
|
||||
if layer == self then
|
||||
self:setVisible(false)
|
||||
table.remove(self.parent.layers, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
for k, layer in pairs(self.parent.layers) do
|
||||
if layer == self then
|
||||
self:setVisible(false)
|
||||
table.remove(self.parent.layers, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:setVisible(visible)
|
||||
self.visible = visible
|
||||
if not visible then
|
||||
self.parent:dirty()
|
||||
-- set parent's lines to dirty for each line in self
|
||||
end
|
||||
self.visible = visible
|
||||
if not visible then
|
||||
self.parent:dirty()
|
||||
-- set parent's lines to dirty for each line in self
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:write(x, y, text, bg, fg)
|
||||
if bg then
|
||||
bg = _rep(self.palette[bg], #text)
|
||||
end
|
||||
if fg then
|
||||
fg = _rep(self.palette[fg], #text)
|
||||
end
|
||||
self:writeBlit(x, y, text, bg, fg)
|
||||
if bg then
|
||||
bg = _rep(self.palette[bg], #text)
|
||||
end
|
||||
if fg then
|
||||
fg = _rep(self.palette[fg], #text)
|
||||
end
|
||||
self:writeBlit(x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function Canvas:writeBlit(x, y, text, bg, fg)
|
||||
if y > 0 and y <= self.height and x <= self.width then
|
||||
local width = #text
|
||||
if y > 0 and y <= #self.lines and x <= self.width then
|
||||
local width = #text
|
||||
|
||||
-- fix ffs
|
||||
if x < 1 then
|
||||
text = _sub(text, 2 - x)
|
||||
if bg then
|
||||
bg = _sub(bg, 2 - x)
|
||||
end
|
||||
if bg then
|
||||
fg = _sub(fg, 2 - x)
|
||||
end
|
||||
width = width + x - 1
|
||||
x = 1
|
||||
end
|
||||
-- fix ffs
|
||||
if x < 1 then
|
||||
text = _sub(text, 2 - x)
|
||||
if bg then
|
||||
bg = _sub(bg, 2 - x)
|
||||
end
|
||||
if bg then
|
||||
fg = _sub(fg, 2 - x)
|
||||
end
|
||||
width = width + x - 1
|
||||
x = 1
|
||||
end
|
||||
|
||||
if x + width - 1 > self.width then
|
||||
text = _sub(text, 1, self.width - x + 1)
|
||||
if bg then
|
||||
bg = _sub(bg, 1, self.width - x + 1)
|
||||
end
|
||||
if bg then
|
||||
fg = _sub(fg, 1, self.width - x + 1)
|
||||
end
|
||||
width = #text
|
||||
end
|
||||
if x + width - 1 > self.width then
|
||||
text = _sub(text, 1, self.width - x + 1)
|
||||
if bg then
|
||||
bg = _sub(bg, 1, self.width - x + 1)
|
||||
end
|
||||
if bg then
|
||||
fg = _sub(fg, 1, self.width - x + 1)
|
||||
end
|
||||
width = #text
|
||||
end
|
||||
|
||||
if width > 0 then
|
||||
if width > 0 then
|
||||
|
||||
local function replace(sstr, pos, rstr, width)
|
||||
if pos == 1 and width == self.width then
|
||||
return rstr
|
||||
elseif pos == 1 then
|
||||
return rstr .. _sub(sstr, pos+width)
|
||||
elseif pos + width > self.width then
|
||||
return _sub(sstr, 1, pos-1) .. rstr
|
||||
end
|
||||
return _sub(sstr, 1, pos-1) .. rstr .. _sub(sstr, pos+width)
|
||||
end
|
||||
local function replace(sstr, pos, rstr, width)
|
||||
if pos == 1 and width == self.width then
|
||||
return rstr
|
||||
elseif pos == 1 then
|
||||
return rstr .. _sub(sstr, pos+width)
|
||||
elseif pos + width > self.width then
|
||||
return _sub(sstr, 1, pos-1) .. rstr
|
||||
end
|
||||
return _sub(sstr, 1, pos-1) .. rstr .. _sub(sstr, pos+width)
|
||||
end
|
||||
|
||||
local line = self.lines[y]
|
||||
line.dirty = true
|
||||
line.text = replace(line.text, x, text, width)
|
||||
if fg then
|
||||
line.fg = replace(line.fg, x, fg, width)
|
||||
end
|
||||
if bg then
|
||||
line.bg = replace(line.bg, x, bg, width)
|
||||
end
|
||||
end
|
||||
end
|
||||
local line = self.lines[y]
|
||||
line.dirty = true
|
||||
line.text = replace(line.text, x, text, width)
|
||||
if fg then
|
||||
line.fg = replace(line.fg, x, fg, width)
|
||||
end
|
||||
if bg then
|
||||
line.bg = replace(line.bg, x, bg, width)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:writeLine(y, text, fg, bg)
|
||||
self.lines[y].dirty = true
|
||||
self.lines[y].text = text
|
||||
self.lines[y].fg = fg
|
||||
self.lines[y].bg = bg
|
||||
self.lines[y].dirty = true
|
||||
self.lines[y].text = text
|
||||
self.lines[y].fg = fg
|
||||
self.lines[y].bg = bg
|
||||
end
|
||||
|
||||
function Canvas:reset()
|
||||
self.regions = nil
|
||||
self.regions = nil
|
||||
end
|
||||
|
||||
function Canvas:clear(bg, fg)
|
||||
local text = _rep(' ', self.width)
|
||||
fg = _rep(self.palette[fg or colors.white], self.width)
|
||||
bg = _rep(self.palette[bg or colors.black], self.width)
|
||||
for i = 1, self.height do
|
||||
self:writeLine(i, text, fg, bg)
|
||||
end
|
||||
local text = _rep(' ', self.width)
|
||||
fg = _rep(self.palette[fg or colors.white], self.width)
|
||||
bg = _rep(self.palette[bg or colors.black], self.width)
|
||||
for i = 1, self.height do
|
||||
self:writeLine(i, text, fg, bg)
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:punch(rect)
|
||||
if not self.regions then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
end
|
||||
self.regions:subRect(rect.x, rect.y, rect.ex, rect.ey)
|
||||
if not self.regions then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
end
|
||||
self.regions:subRect(rect.x, rect.y, rect.ex, rect.ey)
|
||||
end
|
||||
|
||||
function Canvas:blitClipped(device)
|
||||
for _,region in ipairs(self.regions.region) do
|
||||
self:blit(device,
|
||||
{ x = region[1] - self.x + 1,
|
||||
y = region[2] - self.y + 1,
|
||||
ex = region[3]- self.x + 1,
|
||||
ey = region[4] - self.y + 1 },
|
||||
{ x = region[1], y = region[2] })
|
||||
end
|
||||
for _,region in ipairs(self.regions.region) do
|
||||
self:blit(device,
|
||||
{ x = region[1] - self.x + 1,
|
||||
y = region[2] - self.y + 1,
|
||||
ex = region[3]- self.x + 1,
|
||||
ey = region[4] - self.y + 1 },
|
||||
{ x = region[1], y = region[2] })
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:redraw(device)
|
||||
self:reset()
|
||||
if #self.layers > 0 then
|
||||
for _,layer in pairs(self.layers) do
|
||||
self:punch(layer)
|
||||
end
|
||||
self:blitClipped(device)
|
||||
else
|
||||
self:blit(device)
|
||||
end
|
||||
self:clean()
|
||||
self:reset()
|
||||
if #self.layers > 0 then
|
||||
for _,layer in pairs(self.layers) do
|
||||
self:punch(layer)
|
||||
end
|
||||
self:blitClipped(device)
|
||||
else
|
||||
self:blit(device)
|
||||
end
|
||||
self:clean()
|
||||
end
|
||||
|
||||
function Canvas:isDirty()
|
||||
for _, line in pairs(self.lines) do
|
||||
if line.dirty then
|
||||
return true
|
||||
end
|
||||
end
|
||||
for _, line in pairs(self.lines) do
|
||||
if line.dirty then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:dirty()
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = true
|
||||
end
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = true
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:clean()
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = false
|
||||
end
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:render(device, layers) --- redrawAll ?
|
||||
layers = layers or self.layers
|
||||
if #layers > 0 then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
local l = Util.shallowCopy(layers)
|
||||
for _, canvas in ipairs(layers) do
|
||||
table.remove(l, 1)
|
||||
if canvas.visible then
|
||||
self:punch(canvas)
|
||||
canvas:render(device, l)
|
||||
end
|
||||
end
|
||||
self:blitClipped(device)
|
||||
self:reset()
|
||||
else
|
||||
self:blit(device)
|
||||
end
|
||||
self:clean()
|
||||
layers = layers or self.layers
|
||||
if #layers > 0 then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
local l = Util.shallowCopy(layers)
|
||||
for _, canvas in ipairs(layers) do
|
||||
table.remove(l, 1)
|
||||
if canvas.visible then
|
||||
self:punch(canvas)
|
||||
canvas:render(device, l)
|
||||
end
|
||||
end
|
||||
self:blitClipped(device)
|
||||
self:reset()
|
||||
else
|
||||
self:blit(device)
|
||||
end
|
||||
self:clean()
|
||||
end
|
||||
|
||||
function Canvas:blit(device, src, tgt)
|
||||
src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 }
|
||||
tgt = tgt or self
|
||||
src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 }
|
||||
tgt = tgt or self
|
||||
|
||||
for i = 0, src.ey - src.y do
|
||||
local line = self.lines[src.y + i]
|
||||
if line and line.dirty then
|
||||
local t, fg, bg = line.text, line.fg, line.bg
|
||||
if src.x > 1 or src.ex < self.ex then
|
||||
t = _sub(t, src.x, src.ex)
|
||||
fg = _sub(fg, src.x, src.ex)
|
||||
bg = _sub(bg, src.x, src.ex)
|
||||
end
|
||||
--if tgt.y + i > self.ey then -- wrong place to do clipping ??
|
||||
-- break
|
||||
--end
|
||||
device.setCursorPos(tgt.x, tgt.y + i)
|
||||
device.blit(t, fg, bg)
|
||||
end
|
||||
end
|
||||
for i = 0, src.ey - src.y do
|
||||
local line = self.lines[src.y + i]
|
||||
if line and line.dirty then
|
||||
local t, fg, bg = line.text, line.fg, line.bg
|
||||
if src.x > 1 or src.ex < self.ex then
|
||||
t = _sub(t, src.x, src.ex)
|
||||
fg = _sub(fg, src.x, src.ex)
|
||||
bg = _sub(bg, src.x, src.ex)
|
||||
end
|
||||
--if tgt.y + i > self.ey then -- wrong place to do clipping ??
|
||||
-- break
|
||||
--end
|
||||
device.setCursorPos(tgt.x, tgt.y + i)
|
||||
device.blit(t, fg, bg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:applyPalette(palette)
|
||||
|
||||
local lookup = { }
|
||||
for n = 1, 16 do
|
||||
lookup[self.palette[2 ^ (n - 1)]] = palette[2 ^ (n - 1)]
|
||||
end
|
||||
local lookup = { }
|
||||
for n = 1, 16 do
|
||||
lookup[self.palette[2 ^ (n - 1)]] = palette[2 ^ (n - 1)]
|
||||
end
|
||||
|
||||
for _, l in pairs(self.lines) do
|
||||
l.fg = _gsub(l.fg, '%w', lookup)
|
||||
l.bg = _gsub(l.bg, '%w', lookup)
|
||||
l.dirty = true
|
||||
end
|
||||
for _, l in pairs(self.lines) do
|
||||
l.fg = _gsub(l.fg, '%w', lookup)
|
||||
l.bg = _gsub(l.bg, '%w', lookup)
|
||||
l.dirty = true
|
||||
end
|
||||
|
||||
self.palette = palette
|
||||
self.palette = palette
|
||||
end
|
||||
|
||||
function Canvas.convertWindow(win, parent, wx, wy)
|
||||
local w, h = win.getSize()
|
||||
local w, h = win.getSize()
|
||||
|
||||
win.canvas = Canvas({
|
||||
x = wx,
|
||||
y = wy,
|
||||
width = w,
|
||||
height = h,
|
||||
isColor = win.isColor(),
|
||||
})
|
||||
win.canvas = Canvas({
|
||||
x = wx,
|
||||
y = wy,
|
||||
width = w,
|
||||
height = h,
|
||||
isColor = win.isColor(),
|
||||
})
|
||||
|
||||
function win.clear()
|
||||
win.canvas:clear(win.getBackgroundColor(), win.getTextColor())
|
||||
end
|
||||
function win.clear()
|
||||
win.canvas:clear(win.getBackgroundColor(), win.getTextColor())
|
||||
end
|
||||
|
||||
function win.clearLine()
|
||||
local _, y = win.getCursorPos()
|
||||
win.canvas:write(1,
|
||||
y,
|
||||
_rep(' ', win.canvas.width),
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
end
|
||||
function win.clearLine()
|
||||
local _, y = win.getCursorPos()
|
||||
win.canvas:write(1,
|
||||
y,
|
||||
_rep(' ', win.canvas.width),
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
end
|
||||
|
||||
function win.write(str)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:write(x,
|
||||
y,
|
||||
str,
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
win.setCursorPos(x + #str, y)
|
||||
end
|
||||
function win.write(str)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:write(x,
|
||||
y,
|
||||
str,
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
win.setCursorPos(x + #str, y)
|
||||
end
|
||||
|
||||
function win.blit(text, fg, bg)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:writeBlit(x, y, text, bg, fg)
|
||||
end
|
||||
function win.blit(text, fg, bg)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:writeBlit(x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function win.redraw()
|
||||
win.canvas:redraw(parent)
|
||||
end
|
||||
function win.redraw()
|
||||
win.canvas:redraw(parent)
|
||||
end
|
||||
|
||||
function win.scroll(n)
|
||||
table.insert(win.canvas.lines, table.remove(win.canvas.lines, 1))
|
||||
win.canvas.lines[#win.canvas.lines].text = _rep(' ', win.canvas.width)
|
||||
win.canvas:dirty()
|
||||
end
|
||||
function win.scroll(n)
|
||||
table.insert(win.canvas.lines, table.remove(win.canvas.lines, 1))
|
||||
win.canvas.lines[#win.canvas.lines].text = _rep(' ', win.canvas.width)
|
||||
win.canvas:dirty()
|
||||
end
|
||||
|
||||
function win.reposition(x, y, width, height)
|
||||
win.canvas.x, win.canvas.y = x, y
|
||||
win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
|
||||
end
|
||||
function win.reposition(x, y, width, height)
|
||||
win.canvas.x, win.canvas.y = x, y
|
||||
win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
|
||||
end
|
||||
|
||||
win.clear()
|
||||
win.clear()
|
||||
end
|
||||
|
||||
function Canvas.scrollingWindow(win, wx, wy)
|
||||
local w, h = win.getSize()
|
||||
local scrollPos = 0
|
||||
local maxScroll = h
|
||||
|
||||
-- canvas lines are are a sliding window within the local lines table
|
||||
local lines = { }
|
||||
|
||||
local parent
|
||||
local canvas = Canvas({
|
||||
x = wx,
|
||||
y = wy,
|
||||
width = w,
|
||||
height = h,
|
||||
isColor = win.isColor(),
|
||||
})
|
||||
win.canvas = canvas
|
||||
|
||||
local function scrollTo(p, forceRedraw)
|
||||
local ms = #lines - canvas.height -- max scroll
|
||||
p = math.min(math.max(p, 0), ms) -- normalize
|
||||
|
||||
if p ~= scrollPos or forceRedraw then
|
||||
scrollPos = p
|
||||
for i = 1, canvas.height do
|
||||
canvas.lines[i] = lines[i + scrollPos]
|
||||
end
|
||||
canvas:dirty()
|
||||
end
|
||||
end
|
||||
|
||||
function win.blit(text, fg, bg)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:writeBlit(x, y, text, bg, fg)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.clear()
|
||||
lines = { }
|
||||
for i = 1, canvas.height do
|
||||
lines[i] = canvas.lines[i]
|
||||
end
|
||||
scrollPos = 0
|
||||
canvas:clear(win.getBackgroundColor(), win.getTextColor())
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.clearLine()
|
||||
local _, y = win.getCursorPos()
|
||||
|
||||
scrollTo(#lines - canvas.height)
|
||||
win.canvas:write(1,
|
||||
y,
|
||||
_rep(' ', win.canvas.width),
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.redraw()
|
||||
if parent and canvas.visible then
|
||||
local x, y = win.getCursorPos()
|
||||
for i = 1, canvas.height do
|
||||
local line = canvas.lines[i]
|
||||
if line and line.dirty then
|
||||
parent.setCursorPos(canvas.x, canvas.y + i - 1)
|
||||
parent.blit(line.text, line.fg, line.bg)
|
||||
line.dirty = false
|
||||
end
|
||||
end
|
||||
win.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
-- doesn't support negative scrolling...
|
||||
function win.scroll(n)
|
||||
for _ = 1, n do
|
||||
lines[#lines + 1] = {
|
||||
text = _rep(' ', canvas.width),
|
||||
fg = _rep(canvas.palette[win.getTextColor()], canvas.width),
|
||||
bg = _rep(canvas.palette[win.getBackgroundColor()], canvas.width),
|
||||
}
|
||||
end
|
||||
|
||||
while #lines > maxScroll do
|
||||
table.remove(lines, 1)
|
||||
end
|
||||
|
||||
scrollTo(maxScroll, true)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.scrollDown()
|
||||
scrollTo(scrollPos + 1)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.scrollUp()
|
||||
scrollTo(scrollPos - 1)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.setMaxScroll(ms)
|
||||
maxScroll = ms
|
||||
end
|
||||
|
||||
function win.setParent(p)
|
||||
parent = p
|
||||
end
|
||||
|
||||
function win.write(str)
|
||||
str = tostring(str) or ''
|
||||
|
||||
local x, y = win.getCursorPos()
|
||||
scrollTo(#lines - canvas.height)
|
||||
win.blit(str,
|
||||
_rep(canvas.palette[win.getTextColor()], #str),
|
||||
_rep(canvas.palette[win.getBackgroundColor()], #str))
|
||||
win.setCursorPos(x + #str, y)
|
||||
end
|
||||
|
||||
function win.reposition(x, y, width, height)
|
||||
win.canvas.x, win.canvas.y = x, y
|
||||
win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
|
||||
end
|
||||
|
||||
win.clear()
|
||||
end
|
||||
return Canvas
|
||||
|
@@ -6,140 +6,140 @@ local fs = _G.fs
|
||||
|
||||
return function(args)
|
||||
|
||||
local columns = {
|
||||
{ heading = 'Name', key = 'name' },
|
||||
}
|
||||
local columns = {
|
||||
{ heading = 'Name', key = 'name' },
|
||||
}
|
||||
|
||||
if UI.term.width > 28 then
|
||||
table.insert(columns,
|
||||
{ heading = 'Size', key = 'size', width = 5 }
|
||||
)
|
||||
end
|
||||
if UI.term.width > 28 then
|
||||
table.insert(columns,
|
||||
{ heading = 'Size', key = 'size', width = 5 }
|
||||
)
|
||||
end
|
||||
|
||||
args = args or { }
|
||||
args = args or { }
|
||||
|
||||
local selectFile = UI.Dialog {
|
||||
x = args.x or 3,
|
||||
y = args.y or 2,
|
||||
z = args.z or 2,
|
||||
local selectFile = UI.Dialog {
|
||||
x = args.x or 3,
|
||||
y = args.y or 2,
|
||||
z = args.z or 2,
|
||||
-- rex = args.rex or -3,
|
||||
-- rey = args.rey or -3,
|
||||
height = args.height,
|
||||
width = args.width,
|
||||
title = 'Select File',
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2,
|
||||
y = 2,
|
||||
ex = -2,
|
||||
ey = -4,
|
||||
path = '',
|
||||
sortColumn = 'name',
|
||||
columns = columns,
|
||||
},
|
||||
path = UI.TextEntry {
|
||||
x = 2,
|
||||
y = -2,
|
||||
ex = -11,
|
||||
limit = 256,
|
||||
accelerators = {
|
||||
enter = 'path_enter',
|
||||
}
|
||||
},
|
||||
cancel = UI.Button {
|
||||
text = 'Cancel',
|
||||
x = -9,
|
||||
y = -2,
|
||||
event = 'cancel',
|
||||
},
|
||||
}
|
||||
height = args.height,
|
||||
width = args.width,
|
||||
title = 'Select File',
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2,
|
||||
y = 2,
|
||||
ex = -2,
|
||||
ey = -4,
|
||||
path = '',
|
||||
sortColumn = 'name',
|
||||
columns = columns,
|
||||
},
|
||||
path = UI.TextEntry {
|
||||
x = 2,
|
||||
y = -2,
|
||||
ex = -11,
|
||||
limit = 256,
|
||||
accelerators = {
|
||||
enter = 'path_enter',
|
||||
}
|
||||
},
|
||||
cancel = UI.Button {
|
||||
text = 'Cancel',
|
||||
x = -9,
|
||||
y = -2,
|
||||
event = 'cancel',
|
||||
},
|
||||
}
|
||||
|
||||
function selectFile:enable(path, fn)
|
||||
self:setPath(path)
|
||||
self.fn = fn
|
||||
UI.Dialog.enable(self)
|
||||
end
|
||||
function selectFile:enable(path, fn)
|
||||
self:setPath(path)
|
||||
self.fn = fn
|
||||
UI.Dialog.enable(self)
|
||||
end
|
||||
|
||||
function selectFile:setPath(path)
|
||||
self.grid.dir = path
|
||||
while not fs.isDir(self.grid.dir) do
|
||||
self.grid.dir = fs.getDir(self.grid.dir)
|
||||
end
|
||||
function selectFile:setPath(path)
|
||||
self.grid.dir = path
|
||||
while not fs.isDir(self.grid.dir) do
|
||||
self.grid.dir = fs.getDir(self.grid.dir)
|
||||
end
|
||||
|
||||
self.path.value = self.grid.dir
|
||||
end
|
||||
self.path.value = self.grid.dir
|
||||
end
|
||||
|
||||
function selectFile.grid:draw()
|
||||
local files = fs.listEx(self.dir)
|
||||
if #self.dir > 0 then
|
||||
table.insert(files, {
|
||||
name = '..',
|
||||
isDir = true,
|
||||
})
|
||||
end
|
||||
self:setValues(files)
|
||||
self:setIndex(1)
|
||||
UI.Grid.draw(self)
|
||||
end
|
||||
function selectFile.grid:draw()
|
||||
local files = fs.listEx(self.dir)
|
||||
if #self.dir > 0 then
|
||||
table.insert(files, {
|
||||
name = '..',
|
||||
isDir = true,
|
||||
})
|
||||
end
|
||||
self:setValues(files)
|
||||
self:setIndex(1)
|
||||
UI.Grid.draw(self)
|
||||
end
|
||||
|
||||
function selectFile.grid:getDisplayValues(row)
|
||||
if row.size then
|
||||
row = Util.shallowCopy(row)
|
||||
row.size = Util.toBytes(row.size)
|
||||
end
|
||||
return row
|
||||
end
|
||||
function selectFile.grid:getDisplayValues(row)
|
||||
if row.size then
|
||||
row = Util.shallowCopy(row)
|
||||
row.size = Util.toBytes(row.size)
|
||||
end
|
||||
return row
|
||||
end
|
||||
|
||||
function selectFile.grid:getRowTextColor(file)
|
||||
if file.isDir then
|
||||
return colors.cyan
|
||||
end
|
||||
if file.isReadOnly then
|
||||
return colors.pink
|
||||
end
|
||||
return colors.white
|
||||
end
|
||||
function selectFile.grid:getRowTextColor(file)
|
||||
if file.isDir then
|
||||
return colors.cyan
|
||||
end
|
||||
if file.isReadOnly then
|
||||
return colors.pink
|
||||
end
|
||||
return colors.white
|
||||
end
|
||||
|
||||
function selectFile.grid:sortCompare(a, b)
|
||||
if self.sortColumn == 'size' then
|
||||
return a.size < b.size
|
||||
end
|
||||
if a.isDir == b.isDir then
|
||||
return a.name:lower() < b.name:lower()
|
||||
end
|
||||
return a.isDir
|
||||
end
|
||||
function selectFile.grid:sortCompare(a, b)
|
||||
if self.sortColumn == 'size' then
|
||||
return a.size < b.size
|
||||
end
|
||||
if a.isDir == b.isDir then
|
||||
return a.name:lower() < b.name:lower()
|
||||
end
|
||||
return a.isDir
|
||||
end
|
||||
|
||||
function selectFile:eventHandler(event)
|
||||
function selectFile:eventHandler(event)
|
||||
|
||||
if event.type == 'grid_select' then
|
||||
self.grid.dir = fs.combine(self.grid.dir, event.selected.name)
|
||||
self.path.value = self.grid.dir
|
||||
if event.selected.isDir then
|
||||
self.grid:draw()
|
||||
self.path:draw()
|
||||
else
|
||||
UI:setPreviousPage()
|
||||
self.fn(self.path.value)
|
||||
end
|
||||
if event.type == 'grid_select' then
|
||||
self.grid.dir = fs.combine(self.grid.dir, event.selected.name)
|
||||
self.path.value = self.grid.dir
|
||||
if event.selected.isDir then
|
||||
self.grid:draw()
|
||||
self.path:draw()
|
||||
else
|
||||
UI:setPreviousPage()
|
||||
self.fn(self.path.value)
|
||||
end
|
||||
|
||||
elseif event.type == 'path_enter' then
|
||||
if fs.isDir(self.path.value) then
|
||||
self:setPath(self.path.value)
|
||||
self.grid:draw()
|
||||
self.path:draw()
|
||||
else
|
||||
UI:setPreviousPage()
|
||||
self.fn(self.path.value)
|
||||
end
|
||||
elseif event.type == 'path_enter' then
|
||||
if fs.isDir(self.path.value) then
|
||||
self:setPath(self.path.value)
|
||||
self.grid:draw()
|
||||
self.path:draw()
|
||||
else
|
||||
UI:setPreviousPage()
|
||||
self.fn(self.path.value)
|
||||
end
|
||||
|
||||
elseif event.type == 'cancel' then
|
||||
UI:setPreviousPage()
|
||||
self.fn()
|
||||
else
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
elseif event.type == 'cancel' then
|
||||
UI:setPreviousPage()
|
||||
self.fn()
|
||||
else
|
||||
return UI.Dialog.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return selectFile
|
||||
return selectFile
|
||||
end
|
||||
|
@@ -7,190 +7,190 @@ local Peripheral = require('peripheral')
|
||||
local Glasses = class()
|
||||
function Glasses:init(args)
|
||||
|
||||
local defaults = {
|
||||
backgroundColor = colors.black,
|
||||
textColor = colors.white,
|
||||
textScale = .5,
|
||||
backgroundOpacity = .5,
|
||||
multiplier = 2.6665,
|
||||
local defaults = {
|
||||
backgroundColor = colors.black,
|
||||
textColor = colors.white,
|
||||
textScale = .5,
|
||||
backgroundOpacity = .5,
|
||||
multiplier = 2.6665,
|
||||
-- multiplier = 2.333,
|
||||
}
|
||||
defaults.width, defaults.height = term.getSize()
|
||||
}
|
||||
defaults.width, defaults.height = term.getSize()
|
||||
|
||||
UI:setProperties(defaults, args)
|
||||
UI:setProperties(self, defaults)
|
||||
UI:setProperties(defaults, args)
|
||||
UI:setProperties(self, defaults)
|
||||
|
||||
self.bridge = Peripheral.get({
|
||||
type = 'openperipheral_bridge',
|
||||
method = 'addBox',
|
||||
})
|
||||
self.bridge.clear()
|
||||
self.bridge = Peripheral.get({
|
||||
type = 'openperipheral_bridge',
|
||||
method = 'addBox',
|
||||
})
|
||||
self.bridge.clear()
|
||||
|
||||
self.setBackgroundColor = function(...) end
|
||||
self.setTextColor = function(...) end
|
||||
self.setBackgroundColor = function(...) end
|
||||
self.setTextColor = function(...) end
|
||||
|
||||
self.t = { }
|
||||
for i = 1, self.height do
|
||||
self.t[i] = {
|
||||
text = string.rep(' ', self.width+1),
|
||||
--text = self.bridge.addText(0, 40+i*4, string.rep(' ', self.width+1), 0xffffff),
|
||||
bg = { },
|
||||
textFields = { },
|
||||
}
|
||||
end
|
||||
self.t = { }
|
||||
for i = 1, self.height do
|
||||
self.t[i] = {
|
||||
text = string.rep(' ', self.width+1),
|
||||
--text = self.bridge.addText(0, 40+i*4, string.rep(' ', self.width+1), 0xffffff),
|
||||
bg = { },
|
||||
textFields = { },
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Glasses:setBackgroundBox(boxes, ax, bx, y, bgColor)
|
||||
local colors = {
|
||||
[ colors.black ] = 0x000000,
|
||||
[ colors.brown ] = 0x7F664C,
|
||||
[ colors.blue ] = 0x253192,
|
||||
[ colors.red ] = 0xFF0000,
|
||||
[ colors.gray ] = 0x272727,
|
||||
[ colors.lime ] = 0x426A0D,
|
||||
[ colors.green ] = 0x2D5628,
|
||||
[ colors.white ] = 0xFFFFFF
|
||||
}
|
||||
local colors = {
|
||||
[ colors.black ] = 0x000000,
|
||||
[ colors.brown ] = 0x7F664C,
|
||||
[ colors.blue ] = 0x253192,
|
||||
[ colors.red ] = 0xFF0000,
|
||||
[ colors.gray ] = 0x272727,
|
||||
[ colors.lime ] = 0x426A0D,
|
||||
[ colors.green ] = 0x2D5628,
|
||||
[ colors.white ] = 0xFFFFFF
|
||||
}
|
||||
|
||||
local function overlap(box, ax, bx)
|
||||
if bx < box.ax or ax > box.bx then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
local function overlap(box, ax, bx)
|
||||
if bx < box.ax or ax > box.bx then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
for _,box in pairs(boxes) do
|
||||
if overlap(box, ax, bx) then
|
||||
if box.bgColor == bgColor then
|
||||
ax = math.min(ax, box.ax)
|
||||
bx = math.max(bx, box.bx)
|
||||
box.ax = box.bx + 1
|
||||
elseif ax == box.ax then
|
||||
box.ax = bx + 1
|
||||
elseif ax > box.ax then
|
||||
if bx < box.bx then
|
||||
table.insert(boxes, { -- split
|
||||
ax = bx + 1,
|
||||
bx = box.bx,
|
||||
bgColor = box.bgColor
|
||||
})
|
||||
box.bx = ax - 1
|
||||
break
|
||||
else
|
||||
box.ax = box.bx + 1
|
||||
end
|
||||
elseif ax < box.ax then
|
||||
if bx > box.bx then
|
||||
box.ax = box.bx + 1 -- delete
|
||||
else
|
||||
box.ax = bx + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if bgColor ~= colors.black then
|
||||
table.insert(boxes, {
|
||||
ax = ax,
|
||||
bx = bx,
|
||||
bgColor = bgColor
|
||||
})
|
||||
end
|
||||
for _,box in pairs(boxes) do
|
||||
if overlap(box, ax, bx) then
|
||||
if box.bgColor == bgColor then
|
||||
ax = math.min(ax, box.ax)
|
||||
bx = math.max(bx, box.bx)
|
||||
box.ax = box.bx + 1
|
||||
elseif ax == box.ax then
|
||||
box.ax = bx + 1
|
||||
elseif ax > box.ax then
|
||||
if bx < box.bx then
|
||||
table.insert(boxes, { -- split
|
||||
ax = bx + 1,
|
||||
bx = box.bx,
|
||||
bgColor = box.bgColor
|
||||
})
|
||||
box.bx = ax - 1
|
||||
break
|
||||
else
|
||||
box.ax = box.bx + 1
|
||||
end
|
||||
elseif ax < box.ax then
|
||||
if bx > box.bx then
|
||||
box.ax = box.bx + 1 -- delete
|
||||
else
|
||||
box.ax = bx + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if bgColor ~= colors.black then
|
||||
table.insert(boxes, {
|
||||
ax = ax,
|
||||
bx = bx,
|
||||
bgColor = bgColor
|
||||
})
|
||||
end
|
||||
|
||||
local deleted
|
||||
repeat
|
||||
deleted = false
|
||||
for k,box in pairs(boxes) do
|
||||
if box.ax > box.bx then
|
||||
if box.box then
|
||||
box.box.delete()
|
||||
end
|
||||
table.remove(boxes, k)
|
||||
deleted = true
|
||||
break
|
||||
end
|
||||
if not box.box then
|
||||
box.box = self.bridge.addBox(
|
||||
math.floor(self.x + (box.ax - 1) * self.multiplier),
|
||||
self.y + y * 4,
|
||||
math.ceil((box.bx - box.ax + 1) * self.multiplier),
|
||||
4,
|
||||
colors[bgColor],
|
||||
self.backgroundOpacity)
|
||||
else
|
||||
box.box.setX(self.x + math.floor((box.ax - 1) * self.multiplier))
|
||||
box.box.setWidth(math.ceil((box.bx - box.ax + 1) * self.multiplier))
|
||||
end
|
||||
end
|
||||
until not deleted
|
||||
local deleted
|
||||
repeat
|
||||
deleted = false
|
||||
for k,box in pairs(boxes) do
|
||||
if box.ax > box.bx then
|
||||
if box.box then
|
||||
box.box.delete()
|
||||
end
|
||||
table.remove(boxes, k)
|
||||
deleted = true
|
||||
break
|
||||
end
|
||||
if not box.box then
|
||||
box.box = self.bridge.addBox(
|
||||
math.floor(self.x + (box.ax - 1) * self.multiplier),
|
||||
self.y + y * 4,
|
||||
math.ceil((box.bx - box.ax + 1) * self.multiplier),
|
||||
4,
|
||||
colors[bgColor],
|
||||
self.backgroundOpacity)
|
||||
else
|
||||
box.box.setX(self.x + math.floor((box.ax - 1) * self.multiplier))
|
||||
box.box.setWidth(math.ceil((box.bx - box.ax + 1) * self.multiplier))
|
||||
end
|
||||
end
|
||||
until not deleted
|
||||
end
|
||||
|
||||
function Glasses:write(x, y, text, bg)
|
||||
|
||||
if x < 1 then
|
||||
error(' less ', 6)
|
||||
end
|
||||
if y <= #self.t then
|
||||
local line = self.t[y]
|
||||
local str = line.text
|
||||
str = str:sub(1, x-1) .. text .. str:sub(x + #text)
|
||||
self.t[y].text = str
|
||||
if x < 1 then
|
||||
error(' less ', 6)
|
||||
end
|
||||
if y <= #self.t then
|
||||
local line = self.t[y]
|
||||
local str = line.text
|
||||
str = str:sub(1, x-1) .. text .. str:sub(x + #text)
|
||||
self.t[y].text = str
|
||||
|
||||
for _,tf in pairs(line.textFields) do
|
||||
tf.delete()
|
||||
end
|
||||
line.textFields = { }
|
||||
for _,tf in pairs(line.textFields) do
|
||||
tf.delete()
|
||||
end
|
||||
line.textFields = { }
|
||||
|
||||
local function split(st)
|
||||
local words = { }
|
||||
local offset = 0
|
||||
while true do
|
||||
local b,e,w = st:find('(%S+)')
|
||||
if not b then
|
||||
break
|
||||
end
|
||||
table.insert(words, {
|
||||
offset = b + offset - 1,
|
||||
text = w,
|
||||
})
|
||||
offset = offset + e
|
||||
st = st:sub(e + 1)
|
||||
end
|
||||
return words
|
||||
end
|
||||
local function split(st)
|
||||
local words = { }
|
||||
local offset = 0
|
||||
while true do
|
||||
local b,e,w = st:find('(%S+)')
|
||||
if not b then
|
||||
break
|
||||
end
|
||||
table.insert(words, {
|
||||
offset = b + offset - 1,
|
||||
text = w,
|
||||
})
|
||||
offset = offset + e
|
||||
st = st:sub(e + 1)
|
||||
end
|
||||
return words
|
||||
end
|
||||
|
||||
local words = split(str)
|
||||
for _,word in pairs(words) do
|
||||
local tf = self.bridge.addText(self.x + word.offset * self.multiplier,
|
||||
self.y+y*4, '', 0xffffff)
|
||||
tf.setScale(self.textScale)
|
||||
tf.setZ(1)
|
||||
tf.setText(word.text)
|
||||
table.insert(line.textFields, tf)
|
||||
end
|
||||
local words = split(str)
|
||||
for _,word in pairs(words) do
|
||||
local tf = self.bridge.addText(self.x + word.offset * self.multiplier,
|
||||
self.y+y*4, '', 0xffffff)
|
||||
tf.setScale(self.textScale)
|
||||
tf.setZ(1)
|
||||
tf.setText(word.text)
|
||||
table.insert(line.textFields, tf)
|
||||
end
|
||||
|
||||
self:setBackgroundBox(line.bg, x, x + #text - 1, y, bg)
|
||||
end
|
||||
self:setBackgroundBox(line.bg, x, x + #text - 1, y, bg)
|
||||
end
|
||||
end
|
||||
|
||||
function Glasses:clear(bg)
|
||||
for _,line in pairs(self.t) do
|
||||
for _,tf in pairs(line.textFields) do
|
||||
tf.delete()
|
||||
end
|
||||
line.textFields = { }
|
||||
line.text = string.rep(' ', self.width+1)
|
||||
for _,line in pairs(self.t) do
|
||||
for _,tf in pairs(line.textFields) do
|
||||
tf.delete()
|
||||
end
|
||||
line.textFields = { }
|
||||
line.text = string.rep(' ', self.width+1)
|
||||
-- self.t[i].text.setText('')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Glasses:reset()
|
||||
self:clear()
|
||||
self.bridge.clear()
|
||||
self.bridge.sync()
|
||||
self:clear()
|
||||
self.bridge.clear()
|
||||
self.bridge.sync()
|
||||
end
|
||||
|
||||
function Glasses:sync()
|
||||
self.bridge.sync()
|
||||
self.bridge.sync()
|
||||
end
|
||||
|
||||
return Glasses
|
||||
|
@@ -1,57 +1,18 @@
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- tek.lib.region
|
||||
-- Written by Timm S. Mueller <tmueller at schulze-mueller.de>
|
||||
--
|
||||
--
|
||||
-- Copyright 2008 - 2016 by the authors and contributors:
|
||||
--
|
||||
--
|
||||
-- * Timm S. Muller <tmueller at schulze-mueller.de>
|
||||
-- * Franciska Schulze <fschulze at schulze-mueller.de>
|
||||
-- * Tobias Schwinger <tschwinger at isonews2.com>
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining
|
||||
-- a copy of this software and associated documentation files (the
|
||||
-- "Software"), to deal in the Software without restriction, including
|
||||
-- without limitation the rights to use, copy, modify, merge, publish,
|
||||
-- distribute, sublicense, and/or sell copies of the Software, and to
|
||||
-- permit persons to whom the Software is furnished to do so, subject to
|
||||
-- the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be
|
||||
-- included in all copies or substantial portions of the Software.
|
||||
--
|
||||
-- === Disclaimer ===
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
--
|
||||
-- OVERVIEW::
|
||||
-- This library implements the management of regions, which are
|
||||
-- collections of non-overlapping rectangles.
|
||||
-- https://opensource.org/licenses/MIT
|
||||
--
|
||||
-- FUNCTIONS::
|
||||
-- - Region:andRect() - ''And''s a rectangle to a region
|
||||
-- - Region:andRegion() - ''And''s a region to a region
|
||||
-- - Region:checkIntersect() - Checks if a rectangle intersects a region
|
||||
-- - Region:forEach() - Calls a function for each rectangle in a region
|
||||
-- - Region:get() - Get region's min/max extents
|
||||
-- - Region.intersect() - Returns the intersection of two rectangles
|
||||
-- - Region:isEmpty() - Checks if a Region is empty
|
||||
-- - Region.new() - Creates a new Region
|
||||
-- - Region:orRect() - ''Or''s a rectangle to a region
|
||||
-- - Region:orRegion() - ''Or''s a region to a region
|
||||
-- - Region:setRect() - Resets a region to the given rectangle
|
||||
-- - Region:shift() - Displaces a region
|
||||
-- - Region:subRect() - Subtracts a rectangle from a region
|
||||
-- - Region:subRegion() - Subtracts a region from a region
|
||||
-- - Region:xorRect() - ''Exclusive Or''s a rectangle to a region
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
-- Some comments have been removed to reduce file size, see:
|
||||
-- https://github.com/technosaurus/tekui/blob/master/etc/region.lua
|
||||
-- for the full source
|
||||
|
||||
local insert = table.insert
|
||||
local ipairs = ipairs
|
||||
@@ -65,24 +26,18 @@ Region._VERSION = "Region 11.3"
|
||||
|
||||
Region.__index = Region
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- x0, y0, x1, y1 = Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4):
|
||||
-- Returns the coordinates of a rectangle where a rectangle specified by
|
||||
-- the coordinates s1, s2, s3, s4 overlaps with the rectangle specified
|
||||
-- by the coordinates d1, d2, d3, d4. The return value is '''nil''' if
|
||||
-- the rectangles do not overlap.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4)
|
||||
if s3 >= d1 and s1 <= d3 and s4 >= d2 and s2 <= d4 then
|
||||
return max(s1, d1), max(s2, d2), min(s3, d3), min(s4, d4)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- insertrect: insert rect to table, merging with an existing one if possible
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function insertrect(d, s1, s2, s3, s4)
|
||||
for i = 1, min(4, #d) do
|
||||
local a = d[i]
|
||||
@@ -108,10 +63,7 @@ local function insertrect(d, s1, s2, s3, s4)
|
||||
insert(d, 1, { s1, s2, s3, s4 })
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- cutrect: cut rect d into table of new rects, using rect s as a punch
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function cutrect(d1, d2, d3, d4, s1, s2, s3, s4)
|
||||
if not Region.intersect(d1, d2, d3, d4, s1, s2, s3, s4) then
|
||||
return { { d1, d2, d3, d4 } }
|
||||
@@ -135,10 +87,7 @@ local function cutrect(d1, d2, d3, d4, s1, s2, s3, s4)
|
||||
return r
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- cutregion: cut region d, using s as a punch
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function cutregion(d, s1, s2, s3, s4)
|
||||
local r = { }
|
||||
for _, dr in ipairs(d) do
|
||||
@@ -150,11 +99,8 @@ local function cutregion(d, s1, s2, s3, s4)
|
||||
return r
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region = Region.new(r1, r2, r3, r4): Creates a new region from the given
|
||||
-- coordinates.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region.new(r1, r2, r3, r4)
|
||||
if r1 then
|
||||
return setmetatable({ region = { { r1, r2, r3, r4 } } }, Region)
|
||||
@@ -162,39 +108,27 @@ function Region.new(r1, r2, r3, r4)
|
||||
return setmetatable({ region = { } }, Region)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- self = region:setRect(r1, r2, r3, r4): Resets an existing region
|
||||
-- to the specified rectangle.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:setRect(r1, r2, r3, r4)
|
||||
self.region = { { r1, r2, r3, r4 } }
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:orRect(r1, r2, r3, r4): Logical ''or''s a rectangle to a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:orRect(s1, s2, s3, s4)
|
||||
self.region = cutregion(self.region, s1, s2, s3, s4)
|
||||
insertrect(self.region, s1, s2, s3, s4)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:orRegion(region): Logical ''or''s another region to a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:orRegion(s)
|
||||
for _, r in ipairs(s) do
|
||||
self:orRect(r[1], r[2], r[3], r[4])
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:andRect(r1, r2, r3, r4): Logical ''and''s a rectange to a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:andRect(s1, s2, s3, s4)
|
||||
local r = { }
|
||||
for _, d in ipairs(self.region) do
|
||||
@@ -207,10 +141,7 @@ function Region:andRect(s1, s2, s3, s4)
|
||||
self.region = r
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:xorRect(r1, r2, r3, r4): Logical ''xor''s a rectange to a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:xorRect(s1, s2, s3, s4)
|
||||
local r1 = { }
|
||||
local r2 = { { s1, s2, s3, s4 } }
|
||||
@@ -225,10 +156,7 @@ function Region:xorRect(s1, s2, s3, s4)
|
||||
self:orRegion(r2)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- self = region:subRect(r1, r2, r3, r4): Subtracts a rectangle from a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:subRect(s1, s2, s3, s4)
|
||||
local r1 = { }
|
||||
for _, d in ipairs(self.region) do
|
||||
@@ -241,10 +169,7 @@ function Region:subRect(s1, s2, s3, s4)
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:getRect - gets an iterator on the rectangles in a region [internal]
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:getRects()
|
||||
local index = 0
|
||||
return function(object)
|
||||
@@ -255,12 +180,9 @@ function Region:getRects()
|
||||
end, self.region
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- success = region:checkIntersect(x0, y0, x1, y1): Returns a boolean
|
||||
-- indicating whether a rectangle specified by its coordinates overlaps
|
||||
-- with a region.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:checkIntersect(s1, s2, s3, s4)
|
||||
for _, d in ipairs(self.region) do
|
||||
if Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4) then
|
||||
@@ -270,10 +192,7 @@ function Region:checkIntersect(s1, s2, s3, s4)
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:subRegion(region2): Subtracts {{region2}} from {{region}}.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:subRegion(region)
|
||||
if region then
|
||||
for r1, r2, r3, r4 in region:getRects() do
|
||||
@@ -282,10 +201,7 @@ function Region:subRegion(region)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:andRegion(r): Logically ''and''s a region to a region
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:andRegion(s)
|
||||
local r = { }
|
||||
for _, s in ipairs(s.region) do
|
||||
@@ -301,23 +217,17 @@ function Region:andRegion(s)
|
||||
self.region = r
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:forEach(func, obj, ...): For each rectangle in a region, calls the
|
||||
-- specified function according the following scheme:
|
||||
-- func(obj, x0, y0, x1, y1, ...)
|
||||
-- Extra arguments are passed through to the function.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:forEach(func, obj, ...)
|
||||
for x0, y0, x1, y1 in self:getRects() do
|
||||
func(obj, x0, y0, x1, y1, ...)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:shift(dx, dy): Shifts a region by delta x and y.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:shift(dx, dy)
|
||||
for _, r in ipairs(self.region) do
|
||||
r[1] = r[1] + dx
|
||||
@@ -327,18 +237,12 @@ function Region:shift(dx, dy)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:isEmpty(): Returns '''true''' if a region is empty.
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:isEmpty()
|
||||
return #self.region == 0
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- minx, miny, maxx, maxy = region:get(): Get region's min/max extents
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:get()
|
||||
if #self.region > 0 then
|
||||
local minx = 1000000 -- ui.HUGE
|
||||
|
@@ -3,88 +3,88 @@ local Tween = require('ui.tween')
|
||||
local Transition = { }
|
||||
|
||||
function Transition.slideLeft(args)
|
||||
local ticks = args.ticks or 6
|
||||
local easing = args.easing or 'outQuint'
|
||||
local pos = { x = args.ex }
|
||||
local tween = Tween.new(ticks, pos, { x = args.x }, easing)
|
||||
local lastScreen = args.canvas:copy()
|
||||
local ticks = args.ticks or 6
|
||||
local easing = args.easing or 'outQuint'
|
||||
local pos = { x = args.ex }
|
||||
local tween = Tween.new(ticks, pos, { x = args.x }, easing)
|
||||
local lastScreen = args.canvas:copy()
|
||||
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local x = math.floor(pos.x)
|
||||
lastScreen:dirty()
|
||||
lastScreen:blit(device, {
|
||||
x = args.ex - x + args.x,
|
||||
y = args.y,
|
||||
ex = args.ex,
|
||||
ey = args.ey },
|
||||
{ x = args.x, y = args.y })
|
||||
args.canvas:blit(device, {
|
||||
x = args.x,
|
||||
y = args.y,
|
||||
ex = args.ex - x + args.x,
|
||||
ey = args.ey },
|
||||
{ x = x, y = args.y })
|
||||
return not finished
|
||||
end
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local x = math.floor(pos.x)
|
||||
lastScreen:dirty()
|
||||
lastScreen:blit(device, {
|
||||
x = args.ex - x + args.x,
|
||||
y = args.y,
|
||||
ex = args.ex,
|
||||
ey = args.ey },
|
||||
{ x = args.x, y = args.y })
|
||||
args.canvas:blit(device, {
|
||||
x = args.x,
|
||||
y = args.y,
|
||||
ex = args.ex - x + args.x,
|
||||
ey = args.ey },
|
||||
{ x = x, y = args.y })
|
||||
return not finished
|
||||
end
|
||||
end
|
||||
|
||||
function Transition.slideRight(args)
|
||||
local ticks = args.ticks or 6
|
||||
local easing = args.easing or'outQuint'
|
||||
local pos = { x = args.x }
|
||||
local tween = Tween.new(ticks, pos, { x = args.ex }, easing)
|
||||
local lastScreen = args.canvas:copy()
|
||||
local ticks = args.ticks or 6
|
||||
local easing = args.easing or'outQuint'
|
||||
local pos = { x = args.x }
|
||||
local tween = Tween.new(ticks, pos, { x = args.ex }, easing)
|
||||
local lastScreen = args.canvas:copy()
|
||||
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local x = math.floor(pos.x)
|
||||
lastScreen:dirty()
|
||||
lastScreen:blit(device, {
|
||||
x = args.x,
|
||||
y = args.y,
|
||||
ex = args.ex - x + args.x,
|
||||
ey = args.ey },
|
||||
{ x = x, y = args.y })
|
||||
args.canvas:blit(device, {
|
||||
x = args.ex - x + args.x,
|
||||
y = args.y,
|
||||
ex = args.ex,
|
||||
ey = args.ey },
|
||||
{ x = args.x, y = args.y })
|
||||
return not finished
|
||||
end
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local x = math.floor(pos.x)
|
||||
lastScreen:dirty()
|
||||
lastScreen:blit(device, {
|
||||
x = args.x,
|
||||
y = args.y,
|
||||
ex = args.ex - x + args.x,
|
||||
ey = args.ey },
|
||||
{ x = x, y = args.y })
|
||||
args.canvas:blit(device, {
|
||||
x = args.ex - x + args.x,
|
||||
y = args.y,
|
||||
ex = args.ex,
|
||||
ey = args.ey },
|
||||
{ x = args.x, y = args.y })
|
||||
return not finished
|
||||
end
|
||||
end
|
||||
|
||||
function Transition.expandUp(args)
|
||||
local ticks = args.ticks or 3
|
||||
local easing = args.easing or 'linear'
|
||||
local pos = { y = args.ey + 1 }
|
||||
local tween = Tween.new(ticks, pos, { y = args.y }, easing)
|
||||
local ticks = args.ticks or 3
|
||||
local easing = args.easing or 'linear'
|
||||
local pos = { y = args.ey + 1 }
|
||||
local tween = Tween.new(ticks, pos, { y = args.y }, easing)
|
||||
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
args.canvas:blit(device, nil, { x = args.x, y = math.floor(pos.y) })
|
||||
return not finished
|
||||
end
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
args.canvas:blit(device, nil, { x = args.x, y = math.floor(pos.y) })
|
||||
return not finished
|
||||
end
|
||||
end
|
||||
|
||||
function Transition.grow(args)
|
||||
local ticks = args.ticks or 3
|
||||
local easing = args.easing or 'linear'
|
||||
local tween = Tween.new(ticks,
|
||||
{ x = args.width / 2 - 1, y = args.height / 2 - 1, w = 1, h = 1 },
|
||||
{ x = 1, y = 1, w = args.width, h = args.height }, easing)
|
||||
local ticks = args.ticks or 3
|
||||
local easing = args.easing or 'linear'
|
||||
local tween = Tween.new(ticks,
|
||||
{ x = args.width / 2 - 1, y = args.height / 2 - 1, w = 1, h = 1 },
|
||||
{ x = 1, y = 1, w = args.width, h = args.height }, easing)
|
||||
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local subj = tween.subject
|
||||
local rect = { x = math.floor(subj.x), y = math.floor(subj.y) }
|
||||
rect.ex = math.floor(rect.x + subj.w - 1)
|
||||
rect.ey = math.floor(rect.y + subj.h - 1)
|
||||
args.canvas:blit(device, rect, { x = args.x + rect.x - 1, y = args.y + rect.y - 1})
|
||||
return not finished
|
||||
end
|
||||
return function(device)
|
||||
local finished = tween:update(1)
|
||||
local subj = tween.subject
|
||||
local rect = { x = math.floor(subj.x), y = math.floor(subj.y) }
|
||||
rect.ex = math.floor(rect.x + subj.w - 1)
|
||||
rect.ey = math.floor(rect.y + subj.h - 1)
|
||||
args.canvas:blit(device, rect, { x = args.x + rect.x - 1, y = args.y + rect.y - 1})
|
||||
return not finished
|
||||
end
|
||||
end
|
||||
|
||||
return Transition
|
||||
|
@@ -1,31 +1,14 @@
|
||||
local tween = {
|
||||
_VERSION = 'tween 2.1.1',
|
||||
_DESCRIPTION = 'tweening for lua',
|
||||
_URL = 'https://github.com/kikito/tween.lua',
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
_VERSION = 'tween 2.1.1',
|
||||
_DESCRIPTION = 'tweening for lua',
|
||||
_URL = 'https://github.com/kikito/tween.lua',
|
||||
_LICENSE = [[
|
||||
MIT LICENSE
|
||||
|
||||
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
|
||||
Copyright (c) 2014 Enrique García Cota, Yuichi Tateno, Emmanuel Oga
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
]]
|
||||
Licence details: https://opensource.org/licenses/MIT
|
||||
]]
|
||||
}
|
||||
|
||||
-- easing
|
||||
@@ -45,57 +28,57 @@ local function linear(t, b, c, d) return c * t / d + b end
|
||||
-- quad
|
||||
local function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end
|
||||
local function outQuad(t, b, c, d)
|
||||
t = t / d
|
||||
return -c * t * (t - 2) + b
|
||||
t = t / d
|
||||
return -c * t * (t - 2) + b
|
||||
end
|
||||
local function inOutQuad(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 2) + b end
|
||||
return -c / 2 * ((t - 1) * (t - 3) - 1) + b
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 2) + b end
|
||||
return -c / 2 * ((t - 1) * (t - 3) - 1) + b
|
||||
end
|
||||
local function outInQuad(t, b, c, d)
|
||||
if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end
|
||||
return inQuad((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end
|
||||
return inQuad((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- cubic
|
||||
local function inCubic (t, b, c, d) return c * pow(t / d, 3) + b end
|
||||
local function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end
|
||||
local function inOutCubic(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * t * t * t + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * t + 2) + b
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * t * t * t + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * t + 2) + b
|
||||
end
|
||||
local function outInCubic(t, b, c, d)
|
||||
if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end
|
||||
return inCubic((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end
|
||||
return inCubic((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- quart
|
||||
local function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end
|
||||
local function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end
|
||||
local function inOutQuart(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 4) + b end
|
||||
return -c / 2 * (pow(t - 2, 4) - 2) + b
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 4) + b end
|
||||
return -c / 2 * (pow(t - 2, 4) - 2) + b
|
||||
end
|
||||
local function outInQuart(t, b, c, d)
|
||||
if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end
|
||||
return inQuart((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end
|
||||
return inQuart((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- quint
|
||||
local function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end
|
||||
local function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end
|
||||
local function inOutQuint(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 5) + b end
|
||||
return c / 2 * (pow(t - 2, 5) + 2) + b
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(t, 5) + b end
|
||||
return c / 2 * (pow(t - 2, 5) + 2) + b
|
||||
end
|
||||
local function outInQuint(t, b, c, d)
|
||||
if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end
|
||||
return inQuint((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end
|
||||
return inQuint((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- sine
|
||||
@@ -103,142 +86,142 @@ local function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end
|
||||
local function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end
|
||||
local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end
|
||||
local function outInSine(t, b, c, d)
|
||||
if t < d / 2 then return outSine(t * 2, b, c / 2, d) end
|
||||
return inSine((t * 2) -d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outSine(t * 2, b, c / 2, d) end
|
||||
return inSine((t * 2) -d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- expo
|
||||
local function inExpo(t, b, c, d)
|
||||
if t == 0 then return b end
|
||||
return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
|
||||
if t == 0 then return b end
|
||||
return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001
|
||||
end
|
||||
local function outExpo(t, b, c, d)
|
||||
if t == d then return b + c end
|
||||
return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
|
||||
if t == d then return b + c end
|
||||
return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b
|
||||
end
|
||||
local function inOutExpo(t, b, c, d)
|
||||
if t == 0 then return b end
|
||||
if t == d then return b + c end
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end
|
||||
return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
|
||||
if t == 0 then return b end
|
||||
if t == d then return b + c end
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end
|
||||
return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b
|
||||
end
|
||||
local function outInExpo(t, b, c, d)
|
||||
if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end
|
||||
return inExpo((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end
|
||||
return inExpo((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- circ
|
||||
local function inCirc(t, b, c, d) return(-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end
|
||||
local function outCirc(t, b, c, d) return(c * sqrt(1 - pow(t / d - 1, 2)) + b) end
|
||||
local function inOutCirc(t, b, c, d)
|
||||
t = t / d * 2
|
||||
if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (sqrt(1 - t * t) + 1) + b
|
||||
t = t / d * 2
|
||||
if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (sqrt(1 - t * t) + 1) + b
|
||||
end
|
||||
local function outInCirc(t, b, c, d)
|
||||
if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end
|
||||
return inCirc((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end
|
||||
return inCirc((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
-- elastic
|
||||
local function calculatePAS(p,a,c,d)
|
||||
p, a = p or d * 0.3, a or 0
|
||||
if a < abs(c) then return p, c, p / 4 end -- p, a, s
|
||||
return p, a, p / (2 * pi) * asin(c/a) -- p,a,s
|
||||
p, a = p or d * 0.3, a or 0
|
||||
if a < abs(c) then return p, c, p / 4 end -- p, a, s
|
||||
return p, a, p / (2 * pi) * asin(c/a) -- p,a,s
|
||||
end
|
||||
local function inElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
t = t - 1
|
||||
return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
t = t - 1
|
||||
return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b
|
||||
end
|
||||
local function outElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d
|
||||
if t == 1 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b
|
||||
end
|
||||
local function inOutElastic(t, b, c, d, a, p)
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d * 2
|
||||
if t == 2 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
t = t - 1
|
||||
if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b
|
||||
local s
|
||||
if t == 0 then return b end
|
||||
t = t / d * 2
|
||||
if t == 2 then return b + c end
|
||||
p,a,s = calculatePAS(p,a,c,d)
|
||||
t = t - 1
|
||||
if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end
|
||||
return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) * 0.5 + c + b
|
||||
end
|
||||
local function outInElastic(t, b, c, d, a, p)
|
||||
if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end
|
||||
return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
|
||||
if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end
|
||||
return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p)
|
||||
end
|
||||
|
||||
-- back
|
||||
local function inBack(t, b, c, d, s)
|
||||
s = s or 1.70158
|
||||
t = t / d
|
||||
return c * t * t * ((s + 1) * t - s) + b
|
||||
s = s or 1.70158
|
||||
t = t / d
|
||||
return c * t * t * ((s + 1) * t - s) + b
|
||||
end
|
||||
local function outBack(t, b, c, d, s)
|
||||
s = s or 1.70158
|
||||
t = t / d - 1
|
||||
return c * (t * t * ((s + 1) * t + s) + 1) + b
|
||||
s = s or 1.70158
|
||||
t = t / d - 1
|
||||
return c * (t * t * ((s + 1) * t + s) + 1) + b
|
||||
end
|
||||
local function inOutBack(t, b, c, d, s)
|
||||
s = (s or 1.70158) * 1.525
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
|
||||
s = (s or 1.70158) * 1.525
|
||||
t = t / d * 2
|
||||
if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end
|
||||
t = t - 2
|
||||
return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b
|
||||
end
|
||||
local function outInBack(t, b, c, d, s)
|
||||
if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end
|
||||
return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
|
||||
if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end
|
||||
return inBack((t * 2) - d, b + c / 2, c / 2, d, s)
|
||||
end
|
||||
|
||||
-- bounce
|
||||
local function outBounce(t, b, c, d)
|
||||
t = t / d
|
||||
if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end
|
||||
if t < 2 / 2.75 then
|
||||
t = t - (1.5 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.75) + b
|
||||
elseif t < 2.5 / 2.75 then
|
||||
t = t - (2.25 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.9375) + b
|
||||
end
|
||||
t = t - (2.625 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.984375) + b
|
||||
t = t / d
|
||||
if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end
|
||||
if t < 2 / 2.75 then
|
||||
t = t - (1.5 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.75) + b
|
||||
elseif t < 2.5 / 2.75 then
|
||||
t = t - (2.25 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.9375) + b
|
||||
end
|
||||
t = t - (2.625 / 2.75)
|
||||
return c * (7.5625 * t * t + 0.984375) + b
|
||||
end
|
||||
local function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end
|
||||
local function inOutBounce(t, b, c, d)
|
||||
if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end
|
||||
return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b
|
||||
if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end
|
||||
return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b
|
||||
end
|
||||
local function outInBounce(t, b, c, d)
|
||||
if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end
|
||||
return inBounce((t * 2) - d, b + c / 2, c / 2, d)
|
||||
if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end
|
||||
return inBounce((t * 2) - d, b + c / 2, c / 2, d)
|
||||
end
|
||||
|
||||
tween.easing = {
|
||||
linear = linear,
|
||||
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad,
|
||||
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic,
|
||||
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart,
|
||||
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint,
|
||||
inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine,
|
||||
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo,
|
||||
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc,
|
||||
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,
|
||||
inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack,
|
||||
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce
|
||||
linear = linear,
|
||||
inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad,
|
||||
inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic,
|
||||
inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart,
|
||||
inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint,
|
||||
inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine,
|
||||
inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo,
|
||||
inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc,
|
||||
inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic,
|
||||
inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack,
|
||||
inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce
|
||||
}
|
||||
|
||||
|
||||
@@ -246,68 +229,68 @@ tween.easing = {
|
||||
-- private stuff
|
||||
|
||||
local function copyTables(destination, keysTable, valuesTable)
|
||||
valuesTable = valuesTable or keysTable
|
||||
local mt = getmetatable(keysTable)
|
||||
if mt and getmetatable(destination) == nil then
|
||||
setmetatable(destination, mt)
|
||||
end
|
||||
for k,v in pairs(keysTable) do
|
||||
if type(v) == 'table' then
|
||||
destination[k] = copyTables({}, v, valuesTable[k])
|
||||
else
|
||||
destination[k] = valuesTable[k]
|
||||
end
|
||||
end
|
||||
return destination
|
||||
valuesTable = valuesTable or keysTable
|
||||
local mt = getmetatable(keysTable)
|
||||
if mt and getmetatable(destination) == nil then
|
||||
setmetatable(destination, mt)
|
||||
end
|
||||
for k,v in pairs(keysTable) do
|
||||
if type(v) == 'table' then
|
||||
destination[k] = copyTables({}, v, valuesTable[k])
|
||||
else
|
||||
destination[k] = valuesTable[k]
|
||||
end
|
||||
end
|
||||
return destination
|
||||
end
|
||||
|
||||
local function checkSubjectAndTargetRecursively(subject, target, path)
|
||||
path = path or {}
|
||||
local targetType, newPath
|
||||
for k,targetValue in pairs(target) do
|
||||
targetType, newPath = type(targetValue), copyTables({}, path)
|
||||
table.insert(newPath, tostring(k))
|
||||
if targetType == 'number' then
|
||||
assert(type(subject[k]) == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' is missing from subject or isn't a number")
|
||||
elseif targetType == 'table' then
|
||||
checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
|
||||
else
|
||||
assert(targetType == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' must be a number or table of numbers")
|
||||
end
|
||||
end
|
||||
path = path or {}
|
||||
local targetType, newPath
|
||||
for k,targetValue in pairs(target) do
|
||||
targetType, newPath = type(targetValue), copyTables({}, path)
|
||||
table.insert(newPath, tostring(k))
|
||||
if targetType == 'number' then
|
||||
assert(type(subject[k]) == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' is missing from subject or isn't a number")
|
||||
elseif targetType == 'table' then
|
||||
checkSubjectAndTargetRecursively(subject[k], targetValue, newPath)
|
||||
else
|
||||
assert(targetType == 'number', "Parameter '" .. table.concat(newPath,'/') .. "' must be a number or table of numbers")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function checkNewParams(duration, subject, target, easing)
|
||||
assert(type(duration) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
|
||||
local tsubject = type(subject)
|
||||
assert(tsubject == 'table' or tsubject == 'userdata', "subject must be a table or userdata. Was " .. tostring(subject))
|
||||
assert(type(target)== 'table', "target must be a table. Was " .. tostring(target))
|
||||
assert(type(easing)=='function', "easing must be a function. Was " .. tostring(easing))
|
||||
checkSubjectAndTargetRecursively(subject, target)
|
||||
assert(type(duration) == 'number' and duration > 0, "duration must be a positive number. Was " .. tostring(duration))
|
||||
local tsubject = type(subject)
|
||||
assert(tsubject == 'table' or tsubject == 'userdata', "subject must be a table or userdata. Was " .. tostring(subject))
|
||||
assert(type(target)== 'table', "target must be a table. Was " .. tostring(target))
|
||||
assert(type(easing)=='function', "easing must be a function. Was " .. tostring(easing))
|
||||
checkSubjectAndTargetRecursively(subject, target)
|
||||
end
|
||||
|
||||
local function getEasingFunction(easing)
|
||||
easing = easing or "linear"
|
||||
if type(easing) == 'string' then
|
||||
local name = easing
|
||||
easing = tween.easing[name]
|
||||
if type(easing) ~= 'function' then
|
||||
error("The easing function name '" .. name .. "' is invalid")
|
||||
end
|
||||
end
|
||||
return easing
|
||||
easing = easing or "linear"
|
||||
if type(easing) == 'string' then
|
||||
local name = easing
|
||||
easing = tween.easing[name]
|
||||
if type(easing) ~= 'function' then
|
||||
error("The easing function name '" .. name .. "' is invalid")
|
||||
end
|
||||
end
|
||||
return easing
|
||||
end
|
||||
|
||||
local function performEasingOnSubject(subject, target, initial, clock, duration, easing)
|
||||
local t,b,c,d
|
||||
for k,v in pairs(target) do
|
||||
if type(v) == 'table' then
|
||||
performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
|
||||
else
|
||||
t,b,c,d = clock, initial[k], v - initial[k], duration
|
||||
subject[k] = easing(t,b,c,d)
|
||||
end
|
||||
end
|
||||
local t,b,c,d
|
||||
for k,v in pairs(target) do
|
||||
if type(v) == 'table' then
|
||||
performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing)
|
||||
else
|
||||
t,b,c,d = clock, initial[k], v - initial[k], duration
|
||||
subject[k] = easing(t,b,c,d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Tween methods
|
||||
@@ -316,52 +299,52 @@ local Tween = {}
|
||||
local Tween_mt = {__index = Tween}
|
||||
|
||||
function Tween:set(clock)
|
||||
assert(type(clock) == 'number', "clock must be a positive number or 0")
|
||||
assert(type(clock) == 'number', "clock must be a positive number or 0")
|
||||
|
||||
self.initial = self.initial or copyTables({}, self.target, self.subject)
|
||||
self.clock = clock
|
||||
self.initial = self.initial or copyTables({}, self.target, self.subject)
|
||||
self.clock = clock
|
||||
|
||||
if self.clock <= 0 then
|
||||
if self.clock <= 0 then
|
||||
|
||||
self.clock = 0
|
||||
copyTables(self.subject, self.initial)
|
||||
self.clock = 0
|
||||
copyTables(self.subject, self.initial)
|
||||
|
||||
elseif self.clock >= self.duration then -- the tween has expired
|
||||
elseif self.clock >= self.duration then -- the tween has expired
|
||||
|
||||
self.clock = self.duration
|
||||
copyTables(self.subject, self.target)
|
||||
self.clock = self.duration
|
||||
copyTables(self.subject, self.target)
|
||||
|
||||
else
|
||||
else
|
||||
|
||||
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
|
||||
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
return self.clock >= self.duration
|
||||
return self.clock >= self.duration
|
||||
end
|
||||
|
||||
function Tween:reset()
|
||||
return self:set(0)
|
||||
return self:set(0)
|
||||
end
|
||||
|
||||
function Tween:update(dt)
|
||||
assert(type(dt) == 'number', "dt must be a number")
|
||||
return self:set(self.clock + dt)
|
||||
assert(type(dt) == 'number', "dt must be a number")
|
||||
return self:set(self.clock + dt)
|
||||
end
|
||||
|
||||
|
||||
-- Public interface
|
||||
|
||||
function tween.new(duration, subject, target, easing)
|
||||
easing = getEasingFunction(easing)
|
||||
checkNewParams(duration, subject, target, easing)
|
||||
return setmetatable({
|
||||
duration = duration,
|
||||
subject = subject,
|
||||
target = target,
|
||||
easing = easing,
|
||||
clock = 0
|
||||
}, Tween_mt)
|
||||
easing = getEasingFunction(easing)
|
||||
checkNewParams(duration, subject, target, easing)
|
||||
return setmetatable({
|
||||
duration = duration,
|
||||
subject = subject,
|
||||
target = target,
|
||||
easing = easing,
|
||||
clock = 0
|
||||
}, Tween_mt)
|
||||
end
|
||||
|
||||
return tween
|
Reference in New Issue
Block a user