mirror of
https://github.com/kepler155c/opus
synced 2025-10-16 00:07:39 +00:00
reorganization
This commit is contained in:
144
sys/apis/ui/fileui.lua
Normal file
144
sys/apis/ui/fileui.lua
Normal file
@@ -0,0 +1,144 @@
|
||||
local Util = require('util')
|
||||
local UI = require('ui')
|
||||
|
||||
return function(args)
|
||||
|
||||
local columns = {
|
||||
{ heading = 'Name', key = 'name', width = UI.term.width - 9 },
|
||||
}
|
||||
|
||||
if UI.term.width > 28 then
|
||||
columns[1].width = UI.term.width - 16
|
||||
table.insert(columns,
|
||||
{ heading = 'Size', key = 'size', width = 6 }
|
||||
)
|
||||
end
|
||||
|
||||
args = args or { }
|
||||
|
||||
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,
|
||||
backgroundColor = colors.brown,
|
||||
title = 'Select file',
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2,
|
||||
y = 2,
|
||||
rex = -2,
|
||||
rey = -4,
|
||||
path = '',
|
||||
sortColumn = 'name',
|
||||
columns = columns,
|
||||
},
|
||||
path = UI.TextEntry {
|
||||
x = 2,
|
||||
ry = -1,
|
||||
rex = -11,
|
||||
limit = 256,
|
||||
accelerators = {
|
||||
enter = 'path_enter',
|
||||
}
|
||||
},
|
||||
cancel = UI.Button {
|
||||
text = 'Cancel',
|
||||
rx = -8,
|
||||
ry = -1,
|
||||
event = 'cancel',
|
||||
},
|
||||
}
|
||||
|
||||
function selectFile:enable(path, fn)
|
||||
self:setPath(path)
|
||||
self.fn = fn
|
||||
UI.Page.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
|
||||
|
||||
self.path.value = self.grid.dir
|
||||
end
|
||||
|
||||
function selectFile.grid:draw()
|
||||
local files = fs.list(self.dir, true)
|
||||
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:getRowTextColor(file, selected)
|
||||
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: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
|
||||
|
||||
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.Page.eventHandler(self, event)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return selectFile
|
||||
end
|
196
sys/apis/ui/glasses.lua
Normal file
196
sys/apis/ui/glasses.lua
Normal file
@@ -0,0 +1,196 @@
|
||||
local class = require('class')
|
||||
local UI = require('ui')
|
||||
local Event = require('event')
|
||||
local Peripheral = require('peripheral')
|
||||
|
||||
--[[-- Glasses device --]]--
|
||||
local Glasses = class()
|
||||
function Glasses:init(args)
|
||||
|
||||
local defaults = {
|
||||
backgroundColor = colors.black,
|
||||
textColor = colors.white,
|
||||
textScale = .5,
|
||||
backgroundOpacity = .5,
|
||||
multiplier = 2.6665,
|
||||
-- multiplier = 2.333,
|
||||
}
|
||||
defaults.width, defaults.height = term.getSize()
|
||||
|
||||
UI.setProperties(defaults, args)
|
||||
UI.setProperties(self, defaults)
|
||||
|
||||
self.bridge = Peripheral.get({
|
||||
type = 'openperipheral_bridge',
|
||||
method = 'addBox',
|
||||
})
|
||||
self.bridge.clear()
|
||||
|
||||
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
|
||||
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 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
|
||||
|
||||
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
|
||||
|
||||
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 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
|
||||
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)
|
||||
-- self.t[i].text.setText('')
|
||||
end
|
||||
end
|
||||
|
||||
function Glasses:reset()
|
||||
self:clear()
|
||||
self.bridge.clear()
|
||||
self.bridge.sync()
|
||||
end
|
||||
|
||||
function Glasses:sync()
|
||||
self.bridge.sync()
|
||||
end
|
||||
|
||||
return Glasses
|
358
sys/apis/ui/region.lua
Normal file
358
sys/apis/ui/region.lua
Normal file
@@ -0,0 +1,358 @@
|
||||
-------------------------------------------------------------------------------
|
||||
--
|
||||
-- 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.
|
||||
--
|
||||
-- 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
|
||||
--
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local insert = table.insert
|
||||
local ipairs = ipairs
|
||||
local max = math.max
|
||||
local min = math.min
|
||||
local setmetatable = setmetatable
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
local Region = { }
|
||||
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]
|
||||
local a1, a2, a3, a4 = a[1], a[2], a[3], a[4]
|
||||
if a2 == s2 and a4 == s4 then
|
||||
if a3 + 1 == s1 then
|
||||
a[3] = s3
|
||||
return
|
||||
elseif a1 == s3 + 1 then
|
||||
a[1] = s1
|
||||
return
|
||||
end
|
||||
elseif a1 == s1 and a3 == s3 then
|
||||
if a4 + 1 == s2 then
|
||||
a[4] = s4
|
||||
return
|
||||
elseif a2 == s4 + 1 then
|
||||
a[2] = s2
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
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 } }
|
||||
end
|
||||
local r = { }
|
||||
if d1 < s1 then
|
||||
insertrect(r, d1, d2, s1 - 1, d4)
|
||||
d1 = s1
|
||||
end
|
||||
if d2 < s2 then
|
||||
insertrect(r, d1, d2, d3, s2 - 1)
|
||||
d2 = s2
|
||||
end
|
||||
if d3 > s3 then
|
||||
insertrect(r, s3 + 1, d2, d3, d4)
|
||||
d3 = s3
|
||||
end
|
||||
if d4 > s4 then
|
||||
insertrect(r, d1, s4 + 1, d3, d4)
|
||||
end
|
||||
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
|
||||
local d1, d2, d3, d4 = dr[1], dr[2], dr[3], dr[4]
|
||||
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
|
||||
insertrect(r, t[1], t[2], t[3], t[4])
|
||||
end
|
||||
end
|
||||
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)
|
||||
end
|
||||
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
|
||||
local t1, t2, t3, t4 =
|
||||
Region.intersect(d[1], d[2], d[3], d[4], s1, s2, s3, s4)
|
||||
if t1 then
|
||||
insertrect(r, t1, t2, t3, t4)
|
||||
end
|
||||
end
|
||||
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 } }
|
||||
for _, d in ipairs(self.region) do
|
||||
local d1, d2, d3, d4 = d[1], d[2], d[3], d[4]
|
||||
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
|
||||
insertrect(r1, t[1], t[2], t[3], t[4])
|
||||
end
|
||||
r2 = cutregion(r2, d1, d2, d3, d4)
|
||||
end
|
||||
self.region = r1
|
||||
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
|
||||
local d1, d2, d3, d4 = d[1], d[2], d[3], d[4]
|
||||
for _, t in ipairs(cutrect(d1, d2, d3, d4, s1, s2, s3, s4)) do
|
||||
insertrect(r1, t[1], t[2], t[3], t[4])
|
||||
end
|
||||
end
|
||||
self.region = r1
|
||||
return self
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- region:getRect - gets an iterator on the rectangles in a region [internal]
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
function Region:getRects()
|
||||
local index = 0
|
||||
return function(object)
|
||||
index = index + 1
|
||||
if object[index] then
|
||||
return unpack(object[index])
|
||||
end
|
||||
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
|
||||
return true
|
||||
end
|
||||
end
|
||||
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
|
||||
self:subRect(r1, r2, r3, r4)
|
||||
end
|
||||
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
|
||||
for _, d in ipairs(self.region) do
|
||||
local t1, t2, t3, t4 =
|
||||
Region.intersect(d[1], d[2], d[3], d[4],
|
||||
s[1], s[2], s[3], s[4])
|
||||
if t1 then
|
||||
insertrect(r, t1, t2, t3, t4)
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
r[2] = r[2] + dy
|
||||
r[3] = r[3] + dx
|
||||
r[4] = r[4] + 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
|
||||
local miny = 1000000
|
||||
local maxx = 0
|
||||
local maxy = 0
|
||||
for _, r in ipairs(self.region) do
|
||||
minx = min(minx, r[1])
|
||||
miny = min(miny, r[2])
|
||||
maxx = max(maxx, r[3])
|
||||
maxy = max(maxy, r[4])
|
||||
end
|
||||
return minx, miny, maxx, maxy
|
||||
end
|
||||
end
|
||||
|
||||
return Region
|
367
sys/apis/ui/tween.lua
Normal file
367
sys/apis/ui/tween.lua
Normal file
@@ -0,0 +1,367 @@
|
||||
local tween = {
|
||||
_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
|
||||
|
||||
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.
|
||||
]]
|
||||
}
|
||||
|
||||
-- easing
|
||||
|
||||
-- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits.
|
||||
-- For all easing functions:
|
||||
-- t = time == how much time has to pass for the tweening to complete
|
||||
-- b = begin == starting property value
|
||||
-- c = change == ending - beginning
|
||||
-- d = duration == running time. How much time has passed *right now*
|
||||
|
||||
local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin
|
||||
|
||||
-- linear
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
end
|
||||
|
||||
-- sine
|
||||
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)
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- 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
|
||||
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
|
||||
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)
|
||||
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
|
||||
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
|
||||
end
|
||||
|
||||
-- Tween methods
|
||||
|
||||
local Tween = {}
|
||||
local Tween_mt = {__index = Tween}
|
||||
|
||||
function Tween:set(clock)
|
||||
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
|
||||
|
||||
if self.clock <= 0 then
|
||||
|
||||
self.clock = 0
|
||||
copyTables(self.subject, self.initial)
|
||||
|
||||
elseif self.clock >= self.duration then -- the tween has expired
|
||||
|
||||
self.clock = self.duration
|
||||
copyTables(self.subject, self.target)
|
||||
|
||||
else
|
||||
|
||||
performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing)
|
||||
|
||||
end
|
||||
|
||||
return self.clock >= self.duration
|
||||
end
|
||||
|
||||
function Tween:reset()
|
||||
return self:set(0)
|
||||
end
|
||||
|
||||
function Tween:update(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)
|
||||
end
|
||||
|
||||
return tween
|
Reference in New Issue
Block a user