mirror of
https://github.com/1bardesign/batteries.git
synced 2024-11-25 23:24:35 +00:00
BREAKING class interface refactor - all classes will need a minor update, see class.lua
tl;dr is that new no longer needs to call init, calling :new() directly in user code is not allowed, properties are copied, metamethods work, and a config table is needed rather than a class to extend from, so use {extends = superclass} if you want a minimal fix
This commit is contained in:
parent
e1bb76d419
commit
3cc177a0c0
12
async.lua
12
async.lua
@ -18,13 +18,13 @@
|
|||||||
local path = (...):gsub("async", "")
|
local path = (...):gsub("async", "")
|
||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
|
|
||||||
local async = class()
|
local async = class({
|
||||||
|
name = "async",
|
||||||
|
})
|
||||||
|
|
||||||
function async:new()
|
function async:new()
|
||||||
return self:init({
|
self.tasks = {}
|
||||||
tasks = {},
|
self.tasks_stalled = {}
|
||||||
tasks_stalled = {},
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--add a task to the kernel
|
--add a task to the kernel
|
||||||
@ -138,4 +138,4 @@ function async:add_interval(f, delay)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return async
|
return async
|
||||||
|
176
class.lua
176
class.lua
@ -1,83 +1,131 @@
|
|||||||
--[[
|
--[[
|
||||||
barebones oop basics
|
barebones oop basics
|
||||||
supports basic inheritance and means you don't have to build/set your own metatable each time
|
|
||||||
|
|
||||||
todo: collect some stats on classes/optional global class registry
|
call the class object to construct a new instance
|
||||||
|
|
||||||
|
classes are used as metatables directly so that
|
||||||
|
metamethods "just work" - except for index, which is
|
||||||
|
used to hook up instance methods
|
||||||
|
|
||||||
|
classes do use a prototype chain for inheritance, but
|
||||||
|
also copy their interfaces (including superclass)
|
||||||
|
|
||||||
|
we copy interfaces in classes rather than relying on
|
||||||
|
a prototype chain, so that pairs on the class gets
|
||||||
|
all the methods when implemented as an interface
|
||||||
|
|
||||||
|
class properties are not copied and should likely
|
||||||
|
be accessed through the concrete class object so
|
||||||
|
that everything refers to the same object
|
||||||
|
|
||||||
|
arguments (all optional):
|
||||||
|
name (string):
|
||||||
|
the name to use for type()
|
||||||
|
extends (class):
|
||||||
|
superclass for basic inheritance
|
||||||
|
implements (ordered table of classes):
|
||||||
|
mixins/interfaces
|
||||||
|
default_tostring (boolean):
|
||||||
|
whether or not to provide a default tostring function
|
||||||
|
|
||||||
]]
|
]]
|
||||||
|
|
||||||
local function class(inherits)
|
--generate unique increasing class ids
|
||||||
local c = {}
|
local class_id_gen = 0
|
||||||
--class metatable
|
local function next_class_id()
|
||||||
setmetatable(c, {
|
class_id_gen = class_id_gen + 1
|
||||||
--wire up call as ctor
|
return class_id_gen
|
||||||
__call = function(self, ...)
|
end
|
||||||
return self:new(...)
|
|
||||||
end,
|
|
||||||
--handle single inheritence chain
|
|
||||||
__index = inherits,
|
|
||||||
})
|
|
||||||
--instance metatable
|
|
||||||
c.__mt = {
|
|
||||||
__index = c,
|
|
||||||
}
|
|
||||||
--common class functions
|
|
||||||
|
|
||||||
--internal initialisation
|
--implement an interface into c
|
||||||
--sets up an initialised object with a default value table
|
local function implement(c, interface)
|
||||||
--performing a super construction if necessary, and (re-)assigning the right metatable
|
c.__is[interface] = true
|
||||||
function c:init(t, ...)
|
for k, v in pairs(interface) do
|
||||||
if inherits and inherits.new then
|
if c[k] == nil and type(v) == "function" then
|
||||||
--construct superclass instance, then overlay args table
|
c[k] = v
|
||||||
local ct = inherits:new(...)
|
|
||||||
for k,v in pairs(t) do
|
|
||||||
ct[k] = v
|
|
||||||
end
|
|
||||||
t = ct
|
|
||||||
end
|
end
|
||||||
--upgrade to this class and return
|
end
|
||||||
return setmetatable(t, self.__mt)
|
end
|
||||||
|
|
||||||
|
--build a new class
|
||||||
|
local function class(config)
|
||||||
|
local class_id = next_class_id()
|
||||||
|
|
||||||
|
config = config or {}
|
||||||
|
local extends = config.extends
|
||||||
|
local implements = config.implements
|
||||||
|
local name = config.name or ("unnamed class %d)"):format(class_id)
|
||||||
|
|
||||||
|
local c = {}
|
||||||
|
|
||||||
|
--unique generated id per-class
|
||||||
|
c.__id = class_id
|
||||||
|
|
||||||
|
--the class name
|
||||||
|
c.__name = name
|
||||||
|
|
||||||
|
--prototype
|
||||||
|
c.__index = c
|
||||||
|
|
||||||
|
--return the name of the class
|
||||||
|
function c:type()
|
||||||
|
return name
|
||||||
end
|
end
|
||||||
|
|
||||||
--constructor
|
if config.default_tostring then
|
||||||
--generally to be overridden
|
function c:__tostring()
|
||||||
function c:new()
|
return name
|
||||||
return self:init({})
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--class metatable to set up constructor call
|
||||||
|
setmetatable(c, {
|
||||||
|
__call = function(self, ...)
|
||||||
|
local instance = setmetatable({}, self)
|
||||||
|
instance:new(...)
|
||||||
|
return instance
|
||||||
|
end,
|
||||||
|
__index = extends,
|
||||||
|
})
|
||||||
|
|
||||||
|
--checking class membership for probably-too-dynamic code
|
||||||
|
--returns true for both extended classes and implemented interfaces
|
||||||
|
--(implemented with a hashset for fast lookups)
|
||||||
|
c.__is = {}
|
||||||
|
c.__is[c] = true
|
||||||
|
function c:is(t)
|
||||||
|
return self.__is[t] == true
|
||||||
end
|
end
|
||||||
|
|
||||||
--get the inherited class for super calls if/as needed
|
--get the inherited class for super calls if/as needed
|
||||||
--allows overrides that still refer to superclass behaviour
|
--allows overrides that still refer to superclass behaviour
|
||||||
function c:super()
|
c.__super = extends
|
||||||
return inherits
|
--nop by default
|
||||||
|
function c:super() end
|
||||||
|
|
||||||
|
if c.__super then
|
||||||
|
--perform a super construction for an instance
|
||||||
|
function c:super(...)
|
||||||
|
c.__super.new(self, ...)
|
||||||
|
end
|
||||||
|
--implement superclass interface
|
||||||
|
implement(c, c.__super)
|
||||||
end
|
end
|
||||||
|
|
||||||
--delegate a call to the superclass, by name
|
|
||||||
--still a bit clumsy but much cleaner than the inline equivalent,
|
--implement all the passed interfaces/mixins
|
||||||
--plus handles heirarchical complications, and detects various mistakes
|
--in order provided
|
||||||
function c:super_call(func_name, ...)
|
if implements then
|
||||||
--
|
for _, interface in ipairs(implements) do
|
||||||
if type(func_name) ~= "string" then
|
implement(c, interface)
|
||||||
error("super_call requires a string function name to look up, got "..tostring(func_name))
|
|
||||||
end
|
end
|
||||||
--todo: memoize the below :)
|
end
|
||||||
local previous_impl = c:super()
|
|
||||||
--find the first superclass that actually has the method
|
--default constructor, just proxy to the super constructor
|
||||||
while previous_impl and not rawget(previous_impl, func_name) do
|
--override it and use to set up the properties of the instance
|
||||||
previous_impl = previous_impl:super()
|
--but don't forget to call the super constructor!
|
||||||
end
|
function c:new(...)
|
||||||
if not previous_impl then
|
self:super(...)
|
||||||
error("failed super call - no superclass in the chain has an implementation of "..func_name)
|
|
||||||
end
|
|
||||||
-- get the function
|
|
||||||
local f = previous_impl[func_name]
|
|
||||||
if not f then -- this should never happen because we bail out earlier
|
|
||||||
error("failed super call - missing function "..func_name.." in superclass")
|
|
||||||
end
|
|
||||||
-- check if someone reuses that reference
|
|
||||||
if f == self[func_name] then
|
|
||||||
error("failed super call - function "..func_name.." is same in superclass as in derived; this will be a infinite recursion!")
|
|
||||||
end
|
|
||||||
-- call that function
|
|
||||||
return f(self, ...)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--done
|
--done
|
||||||
|
1
init.lua
1
init.lua
@ -39,6 +39,7 @@ local _batteries = {
|
|||||||
manual_gc = require_relative("manual_gc"),
|
manual_gc = require_relative("manual_gc"),
|
||||||
colour = require_relative("colour"),
|
colour = require_relative("colour"),
|
||||||
pretty = require_relative("pretty"),
|
pretty = require_relative("pretty"),
|
||||||
|
make_pooled = require_relative("make_pooled"),
|
||||||
}
|
}
|
||||||
|
|
||||||
--assign aliases
|
--assign aliases
|
||||||
|
43
make_pooled.lua
Normal file
43
make_pooled.lua
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
--[[
|
||||||
|
add pooling functionality to a class
|
||||||
|
|
||||||
|
adds a handful of class and instance methods
|
||||||
|
]]
|
||||||
|
|
||||||
|
return function(class, limit)
|
||||||
|
--shared pooled storage
|
||||||
|
local _pool = {}
|
||||||
|
--size limit for tuning memory upper bound
|
||||||
|
local _pool_limit = limit or 128
|
||||||
|
|
||||||
|
--flush the entire pool
|
||||||
|
function class:flush_pool()
|
||||||
|
if #_pool > 0 then
|
||||||
|
_pool = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--drain one element from the pool, if it exists
|
||||||
|
function class:drain_pool()
|
||||||
|
if #_pool > 0 then
|
||||||
|
return table.remove(_pool)
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--get a pooled object
|
||||||
|
--(re-initialised with new, or freshly constructed if the pool was empty)
|
||||||
|
function class:pooled(...)
|
||||||
|
if #_pool == 0 then
|
||||||
|
return c(...)
|
||||||
|
end
|
||||||
|
return c.drain_pool():new(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
--release a vector to the pool
|
||||||
|
function class:release()
|
||||||
|
if #_pool < _pool_limit then
|
||||||
|
table.insert(_pool, self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
local path = (...):gsub("pubsub", "")
|
local path = (...):gsub("pubsub", "")
|
||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
local pubsub = class()
|
local pubsub = class({
|
||||||
|
name = "pubsub",
|
||||||
|
})
|
||||||
|
|
||||||
--create a new pubsub bus
|
--create a new pubsub bus
|
||||||
function pubsub:new()
|
function pubsub:new()
|
||||||
return self:init({
|
self.subscriptions = {}
|
||||||
subscriptions = {},
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--(internal; notify a callback set of an event)
|
--(internal; notify a callback set of an event)
|
||||||
|
@ -66,7 +66,7 @@ Extensions to existing lua core modules to provide missing features.
|
|||||||
|
|
||||||
General utility data structures and algorithms to speed you along your way.
|
General utility data structures and algorithms to speed you along your way.
|
||||||
|
|
||||||
- [`class`](./class.lua) - Single-inheritance oo in a single function.
|
- [`class`](./class.lua) - OOP with inheritance and interfaces in a single function.
|
||||||
- [`functional`](./functional.lua) - Functional programming facilities. `map`, `reduce`, `any`, `match`, `minmax`, `mean`...
|
- [`functional`](./functional.lua) - Functional programming facilities. `map`, `reduce`, `any`, `match`, `minmax`, `mean`...
|
||||||
- [`sequence`](./sequence.lua) - An oo wrapper on sequential tables, so you can do `t:insert(i, v)` instead of `table.insert(t, i, v)`. Also supports method chaining for the `functional` interface above, which can save a lot of needless typing!
|
- [`sequence`](./sequence.lua) - An oo wrapper on sequential tables, so you can do `t:insert(i, v)` instead of `table.insert(t, i, v)`. Also supports method chaining for the `functional` interface above, which can save a lot of needless typing!
|
||||||
- [`set`](./set.lua) - A set type supporting a full suite of set operations with fast membership testing and `ipairs`-style iteration.
|
- [`set`](./set.lua) - A set type supporting a full suite of set operations with fast membership testing and `ipairs`-style iteration.
|
||||||
|
13
sequence.lua
13
sequence.lua
@ -6,12 +6,19 @@
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local path = (...):gsub("sequence", "")
|
local path = (...):gsub("sequence", "")
|
||||||
local class = require(path .. "class")
|
|
||||||
local table = require(path .. "tablex") --shadow global table module
|
local table = require(path .. "tablex") --shadow global table module
|
||||||
local functional = require(path .. "functional")
|
local functional = require(path .. "functional")
|
||||||
local stable_sort = require(path .. "sort").stable_sort
|
local stable_sort = require(path .. "sort").stable_sort
|
||||||
|
|
||||||
local sequence = class(table) --proxy missing table fns to tablex api
|
--(not a class, because we want to be able to upgrade tables that are passed in without a copy)
|
||||||
|
local sequence = {}
|
||||||
|
sequence.__index = sequence
|
||||||
|
setmetatable(sequence, {
|
||||||
|
__index = table,
|
||||||
|
__call = function(self, ...)
|
||||||
|
return sequence:new(...)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
--iterators as method calls
|
--iterators as method calls
|
||||||
--(no pairs, sequences are ordered)
|
--(no pairs, sequences are ordered)
|
||||||
@ -21,7 +28,7 @@ sequence.iterate = ipairs
|
|||||||
|
|
||||||
--upgrade a table into a sequence, or create a new sequence
|
--upgrade a table into a sequence, or create a new sequence
|
||||||
function sequence:new(t)
|
function sequence:new(t)
|
||||||
return self:init(t or {})
|
return setmetatable(t or {}, sequence)
|
||||||
end
|
end
|
||||||
|
|
||||||
--sorting default to stable
|
--sorting default to stable
|
||||||
|
17
set.lua
17
set.lua
@ -6,21 +6,20 @@ local path = (...):gsub("set", "")
|
|||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
local table = require(path .. "tablex") --shadow global table module
|
local table = require(path .. "tablex") --shadow global table module
|
||||||
|
|
||||||
local set = class()
|
local set = class({
|
||||||
|
name = "set",
|
||||||
|
})
|
||||||
|
|
||||||
--construct a new set
|
--construct a new set
|
||||||
--elements is an optional ordered table of elements to be added to the set
|
--elements is an optional ordered table of elements to be added to the set
|
||||||
function set:new(elements)
|
function set:new(elements)
|
||||||
self = self:init({
|
self._keyed = {}
|
||||||
_keyed = {},
|
self._ordered = {}
|
||||||
_ordered = {},
|
|
||||||
})
|
|
||||||
if elements then
|
if elements then
|
||||||
for _, v in ipairs(elements) do
|
for _, v in ipairs(elements) do
|
||||||
self:add(v)
|
self:add(v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--check if an element is present in the set
|
--check if an element is present in the set
|
||||||
@ -120,7 +119,7 @@ end
|
|||||||
|
|
||||||
--copy a set
|
--copy a set
|
||||||
function set:copy()
|
function set:copy()
|
||||||
return set:new():add_set(self)
|
return set():add_set(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
--create a new set containing the complement of the other set contained in this one
|
--create a new set containing the complement of the other set contained in this one
|
||||||
@ -141,7 +140,7 @@ end
|
|||||||
--create a new set containing the intersection of this set with another
|
--create a new set containing the intersection of this set with another
|
||||||
--only the elements present in both sets will remain in the result
|
--only the elements present in both sets will remain in the result
|
||||||
function set:intersection(other)
|
function set:intersection(other)
|
||||||
local r = set:new()
|
local r = set()
|
||||||
for i, v in self:ipairs() do
|
for i, v in self:ipairs() do
|
||||||
if other:has(v) then
|
if other:has(v) then
|
||||||
r:add(v)
|
r:add(v)
|
||||||
@ -157,7 +156,7 @@ end
|
|||||||
--equal to self:union(other):subtract_set(self:intersection(other))
|
--equal to self:union(other):subtract_set(self:intersection(other))
|
||||||
-- but with much less wasted effort
|
-- but with much less wasted effort
|
||||||
function set:symmetric_difference(other)
|
function set:symmetric_difference(other)
|
||||||
local r = set:new()
|
local r = set()
|
||||||
for i, v in self:ipairs() do
|
for i, v in self:ipairs() do
|
||||||
if not other:has(v) then
|
if not other:has(v) then
|
||||||
r:add(v)
|
r:add(v)
|
||||||
|
@ -26,17 +26,15 @@
|
|||||||
local path = (...):gsub("state_machine", "")
|
local path = (...):gsub("state_machine", "")
|
||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
|
|
||||||
local state_machine = class()
|
local state_machine = class({
|
||||||
|
name = "state_machine",
|
||||||
|
})
|
||||||
|
|
||||||
function state_machine:new(states, start_in_state)
|
function state_machine:new(states, start_in_state)
|
||||||
self = self:init({
|
self.states = states or {}
|
||||||
states = states or {},
|
self.current_state_name = ""
|
||||||
current_state_name = "",
|
self.reset_state_name = start_in_state or ""
|
||||||
reset_state_name = start_in_state or "",
|
|
||||||
})
|
|
||||||
|
|
||||||
self:reset()
|
self:reset()
|
||||||
|
|
||||||
return self
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--get the current state table (or nil if it doesn't exist)
|
--get the current state table (or nil if it doesn't exist)
|
||||||
|
15
timer.lua
15
timer.lua
@ -9,19 +9,20 @@
|
|||||||
|
|
||||||
local path = (...):gsub("timer", "")
|
local path = (...):gsub("timer", "")
|
||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
local timer = class()
|
local timer = class({
|
||||||
|
name = "timer",
|
||||||
|
})
|
||||||
|
|
||||||
--create a timer, with optional callbacks
|
--create a timer, with optional callbacks
|
||||||
--callbacks recieve as arguments:
|
--callbacks recieve as arguments:
|
||||||
-- the current progress as a number from 0 to 1, so can be used for lerps
|
-- the current progress as a number from 0 to 1, so can be used for lerps
|
||||||
-- the timer object, so can be reset if needed
|
-- the timer object, so can be reset if needed
|
||||||
function timer:new(time, on_progress, on_finish)
|
function timer:new(time, on_progress, on_finish)
|
||||||
return self:init({
|
self.time = 0
|
||||||
time = 0, --set in the reset below
|
self.timer = 0
|
||||||
timer = 0,
|
self.on_progress = on_progress
|
||||||
on_progress = on_progress,
|
self.on_finish = on_finish
|
||||||
on_finish = on_finish,
|
self:reset(time)
|
||||||
}):reset(time)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--update this timer, calling the relevant callback if it exists
|
--update this timer, calling the relevant callback if it exists
|
||||||
|
100
vec2.lua
100
vec2.lua
@ -5,115 +5,67 @@
|
|||||||
local path = (...):gsub("vec2", "")
|
local path = (...):gsub("vec2", "")
|
||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
local math = require(path .. "mathx") --shadow global math module
|
local math = require(path .. "mathx") --shadow global math module
|
||||||
|
local make_pooled = require(path .. "make_pooled")
|
||||||
|
|
||||||
local vec2 = class()
|
local vec2 = class({
|
||||||
vec2.type = "vec2"
|
name = "vec2",
|
||||||
|
})
|
||||||
|
|
||||||
--stringification
|
--stringification
|
||||||
vec2.__mt.__tostring = function(self)
|
function vec2:__tostring()
|
||||||
return ("(%.2f, %.2f)"):format(self.x, self.y)
|
return ("(%.2f, %.2f)"):format(self.x, self.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
--probably-too-flexible ctor
|
--probably-too-flexible ctor
|
||||||
function vec2:new(x, y)
|
function vec2:new(x, y)
|
||||||
if type(x) == "number" and type(y) == "number" then
|
if type(x) == "number" or type(x) == "nil" then
|
||||||
return vec2:xy(x,y)
|
self:sset(x or 0, y)
|
||||||
elseif x then
|
elseif type(x) == "table" then
|
||||||
if type(x) == "number" then
|
if x.type and x:type() == "vec2" then
|
||||||
return vec2:filled(x)
|
self:vset(x)
|
||||||
elseif type(x) == "table" then
|
elseif x[1] then
|
||||||
if x.type == "vec2" then
|
self:sset(x[1], x[2])
|
||||||
return x:copy()
|
else
|
||||||
elseif x[1] and x[2] then
|
self:sset(x.x, x.y)
|
||||||
return vec2:xy(x[1], x[2])
|
|
||||||
elseif x.x and x.y then
|
|
||||||
return vec2:xy(x.x, x.y)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return vec2:zero()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--explicit ctors
|
--explicit ctors
|
||||||
function vec2:copy()
|
function vec2:copy()
|
||||||
return self:init({
|
return vec2(self.x, self.y)
|
||||||
x = self.x, y = self.y
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec2:xy(x, y)
|
function vec2:xy(x, y)
|
||||||
return self:init({
|
return vec2(x, y)
|
||||||
x = x, y = y
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec2:filled(v)
|
function vec2:filled(v)
|
||||||
return self:init({
|
return vec2(v)
|
||||||
x = v, y = v
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec2:zero()
|
function vec2:zero()
|
||||||
return vec2:filled(0)
|
return vec2(0)
|
||||||
end
|
|
||||||
|
|
||||||
--shared pooled storage
|
|
||||||
local _vec2_pool = {}
|
|
||||||
--size limit for tuning memory upper bound
|
|
||||||
local _vec2_pool_limit = 128
|
|
||||||
|
|
||||||
function vec2.pool_size()
|
|
||||||
return #_vec2_pool
|
|
||||||
end
|
|
||||||
|
|
||||||
--flush the entire pool
|
|
||||||
function vec2.flush_pool()
|
|
||||||
if vec2.pool_size() > 0 then
|
|
||||||
_vec2_pool = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--drain one element from the pool, if it exists
|
|
||||||
function vec2.drain_pool()
|
|
||||||
if #_vec2_pool > 0 then
|
|
||||||
return table.remove(_vec2_pool)
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--get a pooled vector (initialise it yourself)
|
|
||||||
function vec2:pooled()
|
|
||||||
return vec2.drain_pool() or vec2:zero()
|
|
||||||
end
|
|
||||||
|
|
||||||
--get a pooled copy of an existing vector
|
|
||||||
function vec2:pooled_copy()
|
|
||||||
return vec2:pooled():vset(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
--release a vector to the pool
|
|
||||||
function vec2:release(...)
|
|
||||||
if vec2.pool_size() < _vec2_pool_limit then
|
|
||||||
table.insert(_vec2_pool, self)
|
|
||||||
end
|
|
||||||
if ... then
|
|
||||||
vec2.release(...)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--unpack for multi-args
|
--unpack for multi-args
|
||||||
|
|
||||||
function vec2:unpack()
|
function vec2:unpack()
|
||||||
return self.x, self.y
|
return self.x, self.y
|
||||||
end
|
end
|
||||||
|
|
||||||
--pack when a sequence is needed
|
--pack when a sequence is needed
|
||||||
--(not particularly useful)
|
|
||||||
|
|
||||||
function vec2:pack()
|
function vec2:pack()
|
||||||
return {self:unpack()}
|
return {self:unpack()}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--shared pooled storage
|
||||||
|
make_pooled(vec2, 128)
|
||||||
|
|
||||||
|
--get a pooled copy of an existing vector
|
||||||
|
function vec2:pooled_copy()
|
||||||
|
return vec2:pooled():vset(self)
|
||||||
|
end
|
||||||
|
|
||||||
--modify
|
--modify
|
||||||
|
|
||||||
function vec2:sset(x, y)
|
function vec2:sset(x, y)
|
||||||
|
103
vec3.lua
103
vec3.lua
@ -7,118 +7,73 @@ local path = (...):gsub("vec3", "")
|
|||||||
local class = require(path .. "class")
|
local class = require(path .. "class")
|
||||||
local vec2 = require(path .. "vec2")
|
local vec2 = require(path .. "vec2")
|
||||||
local math = require(path .. "mathx") --shadow global math module
|
local math = require(path .. "mathx") --shadow global math module
|
||||||
|
local make_pooled = require(path .. "make_pooled")
|
||||||
|
|
||||||
local vec3 = class()
|
local vec3 = class({
|
||||||
vec3.type = "vec3"
|
name = "vec3",
|
||||||
|
})
|
||||||
|
|
||||||
--stringification
|
--stringification
|
||||||
vec3.__mt.__tostring = function(self)
|
function vec3:__tostring()
|
||||||
return ("(%.2f, %.2f, %.2f)"):format(self.x, self.y, self.z)
|
return ("(%.2f, %.2f, %.2f)"):format(self.x, self.y, self.z)
|
||||||
end
|
end
|
||||||
|
|
||||||
--probably-too-flexible ctor
|
--probably-too-flexible ctor
|
||||||
function vec3:new(x, y, z)
|
function vec3:new(x, y, z)
|
||||||
if x and y and z then
|
if type(x) == "number" or type(x) == "nil" then
|
||||||
return vec3:xyz(x, y, z)
|
self:sset(x or 0, y, z)
|
||||||
elseif x then
|
elseif type(x) == "table" then
|
||||||
if type(x) == "number" then
|
if x.type and x:type() == "vec3" then
|
||||||
return vec3:filled(x)
|
self:vset(x)
|
||||||
elseif type(x) == "table" then
|
elseif x[1] then
|
||||||
if x.type == "vec3" then
|
self:sset(x[1], x[2], x[3])
|
||||||
return x:copy()
|
else
|
||||||
elseif x[1] and x[2] and x[3] then
|
self:sset(x.x, x.y, x.z)
|
||||||
return vec3:xyz(x[1], x[2], x[3])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return vec3:zero()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--explicit ctors
|
--explicit ctors
|
||||||
function vec3:copy()
|
function vec3:copy()
|
||||||
return self:init({
|
return vec3(self.x, self.y, self.z)
|
||||||
x = self.x, y = self.y, z = self.z
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec3:xyz(x, y, z)
|
function vec3:xyz(x, y, z)
|
||||||
return self:init({
|
return vec3(x, y, z)
|
||||||
x = x, y = y, z = z
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec3:filled(v)
|
function vec3:filled(x, y, z)
|
||||||
return self:init({
|
return vec3(x, y, z)
|
||||||
x = v, y = v, z = v
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function vec3:zero()
|
function vec3:zero()
|
||||||
return vec3:filled(0)
|
return vec3(0, 0, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
--shared pooled storage
|
--unpack for multi-args
|
||||||
local _vec3_pool = {}
|
function vec3:unpack()
|
||||||
--size limit for tuning memory upper bound
|
return self.x, self.y, self.z
|
||||||
local _vec3_pool_limit = 128
|
|
||||||
|
|
||||||
function vec3.pool_size()
|
|
||||||
return #_vec3_pool
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--flush the entire pool
|
--pack when a sequence is needed
|
||||||
function vec3.flush_pool()
|
function vec3:pack()
|
||||||
if vec3.pool_size() > 0 then
|
return {self:unpack()}
|
||||||
_vec3_pool = {}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--drain one element from the pool, if it exists
|
--handle pooling
|
||||||
function vec3.drain_pool()
|
make_pooled(vec3, 128)
|
||||||
if #_vec3_pool > 0 then
|
|
||||||
return table.remove(_vec3_pool)
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
--get a pooled vector (initialise it yourself)
|
|
||||||
function vec3:pooled()
|
|
||||||
return vec3.drain_pool() or vec3:zero()
|
|
||||||
end
|
|
||||||
|
|
||||||
--get a pooled copy of an existing vector
|
--get a pooled copy of an existing vector
|
||||||
function vec3:pooled_copy()
|
function vec3:pooled_copy()
|
||||||
return vec3:pooled():vset(self)
|
return vec3:pooled():vset(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
--release a vector to the pool
|
|
||||||
function vec3:release()
|
|
||||||
if vec3.pool_size() < _vec3_pool_limit then
|
|
||||||
table.insert(_vec3_pool, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--unpack for multi-args
|
|
||||||
|
|
||||||
function vec3:unpack()
|
|
||||||
return self.x, self.y, self.z
|
|
||||||
end
|
|
||||||
|
|
||||||
--pack when a sequence is needed
|
|
||||||
--(not particularly useful)
|
|
||||||
|
|
||||||
function vec3:pack()
|
|
||||||
return {self:unpack()}
|
|
||||||
end
|
|
||||||
|
|
||||||
--modify
|
--modify
|
||||||
|
|
||||||
function vec3:sset(x, y, z)
|
function vec3:sset(x, y, z)
|
||||||
if not y then y = x end
|
|
||||||
if not z then z = y end
|
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y or x
|
||||||
self.z = z
|
self.z = z or y or z
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user