mirror of
https://github.com/1bardesign/batteries.git
synced 2024-11-29 08:14:35 +00:00
Merge remote-tracking branch 'origin/master' into feature/insert-sorted-binary-search
This commit is contained in:
commit
5601d9415b
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
local path = (...):gsub("functional", "")
|
local path = (...):gsub("functional", "")
|
||||||
local tablex = require(path .. "tablex")
|
local tablex = require(path .. "tablex")
|
||||||
|
local mathx = require(path .. "mathx")
|
||||||
|
|
||||||
local functional = setmetatable({}, {
|
local functional = setmetatable({}, {
|
||||||
__index = tablex,
|
__index = tablex,
|
||||||
@ -172,6 +173,54 @@ function functional.zip(t1, t2, f)
|
|||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
--specialised maps
|
||||||
|
-- (experimental: let me know if you have better names for these!)
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
--maps a sequence {a, b, c} -> collapse { f(a), f(b), f(c) }
|
||||||
|
-- (ie results from functions should generally be sequences,
|
||||||
|
-- which are appended onto each other, resulting in one big sequence)
|
||||||
|
-- (automatically drops any nils, same as map)
|
||||||
|
function functional.stitch(t, f)
|
||||||
|
local result = {}
|
||||||
|
for i, v in ipairs(t) do
|
||||||
|
local v = f(v, i)
|
||||||
|
if v ~= nil then
|
||||||
|
if type(v) == "table" then
|
||||||
|
for _, e in ipairs(v) do
|
||||||
|
table.insert(result, e)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(result, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
--alias
|
||||||
|
functional.map_stitch = functional.stitch
|
||||||
|
|
||||||
|
--maps a sequence {a, b, c} -> { f(a, b), f(b, c), f(c, a) }
|
||||||
|
-- useful for inter-dependent data
|
||||||
|
-- (automatically drops any nils, same as map)
|
||||||
|
|
||||||
|
function functional.cycle(t, f)
|
||||||
|
local result = {}
|
||||||
|
for i, a in ipairs(t) do
|
||||||
|
local b = t[mathx.wrap(i + 1, 1, #t + 1)]
|
||||||
|
local v = f(a, b, i)
|
||||||
|
if v ~= nil then
|
||||||
|
table.insert(result, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
functional.map_cycle = functional.cycle
|
||||||
|
|
||||||
|
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
--generating data
|
--generating data
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
@ -55,7 +55,7 @@ function intersect.circle_circle_collide(a_pos, a_rad, b_pos, b_rad, into)
|
|||||||
into = vec2:zero()
|
into = vec2:zero()
|
||||||
end
|
end
|
||||||
--normalise, scale to separating distance
|
--normalise, scale to separating distance
|
||||||
into:vset(_ccc_delta):sdiv(dist):smuli(rad - dist)
|
into:vset(_ccc_delta):sdivi(dist):smuli(rad - dist)
|
||||||
return into
|
return into
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
@ -97,7 +97,7 @@ function intersect._line_displacement_to_sep(a_start, a_end, separation, total_r
|
|||||||
if sep <= 0 then
|
if sep <= 0 then
|
||||||
if distance <= COLLIDE_EPS then
|
if distance <= COLLIDE_EPS then
|
||||||
--point intersecting the line; push out along normal
|
--point intersecting the line; push out along normal
|
||||||
separation:vset(a_end):vsub(a_start):normalisei():rot90li()
|
separation:vset(a_end):vsubi(a_start):normalisei():rot90li()
|
||||||
else
|
else
|
||||||
separation:smuli(-sep)
|
separation:smuli(-sep)
|
||||||
end
|
end
|
||||||
@ -418,4 +418,60 @@ function intersect.point_in_poly(point, poly)
|
|||||||
return wn ~= 0
|
return wn ~= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--resolution helpers
|
||||||
|
|
||||||
|
--resolve a collision between two bodies, given a (minimum) separating vector
|
||||||
|
-- from a's frame of reference, like the result of any of the _collide functions
|
||||||
|
--requires the two positions of the bodies, the msv, and a balance factor
|
||||||
|
--balance should be between 1 and 0;
|
||||||
|
-- 1 is only a_pos moving to resolve
|
||||||
|
-- 0 is only b_pos moving to resolve
|
||||||
|
-- 0.5 is balanced between both (default)
|
||||||
|
--note: this wont work as-is for line segments, which have two separate position coordinates
|
||||||
|
-- you will need to understand what is going on and move the second coordinate yourself
|
||||||
|
function intersect.resolve_msv(a_pos, b_pos, msv, balance)
|
||||||
|
balance = balance or 0.5
|
||||||
|
a_pos:fmai(msv, balance)
|
||||||
|
b_pos:fmai(msv, -(1 - balance))
|
||||||
|
end
|
||||||
|
|
||||||
|
--bounce a velocity off of a normal (modifying velocity)
|
||||||
|
--essentially flips the part of the velocity in the direction of the normal
|
||||||
|
function intersect.bounce_off(velocity, normal, conservation)
|
||||||
|
--(default)
|
||||||
|
conservation = conservation or 1
|
||||||
|
--take a copy, we need it
|
||||||
|
local old_vel = vec2.pooled_copy(velocity)
|
||||||
|
--reject on the normal (keep velocity tangential to the normal)
|
||||||
|
velocity:vreji(normal)
|
||||||
|
--add back the complement of the difference;
|
||||||
|
--basically "flip" the velocity in line with the normal.
|
||||||
|
velocity:fmai(old_vel:vsubi(velocity), -conservation)
|
||||||
|
--clean up
|
||||||
|
old_vel:release()
|
||||||
|
return velocity
|
||||||
|
end
|
||||||
|
|
||||||
|
--mutual bounce; two similar bodies bounce off each other, transferring energy
|
||||||
|
function intersect.mutual_bounce(velocity_a, velocity_b, normal, conservation)
|
||||||
|
--(default)
|
||||||
|
conservation = conservation or 1
|
||||||
|
--take copies, we need them
|
||||||
|
local old_a_vel = vec2.pooled_copy(velocity_a)
|
||||||
|
local old_b_vel = vec2.pooled_copy(velocity_b)
|
||||||
|
--reject on the normal
|
||||||
|
velocity_a:vreji(normal)
|
||||||
|
velocity_b:vreji(normal)
|
||||||
|
--calculate the amount remaining from the old velocity
|
||||||
|
--(transfer ownership)
|
||||||
|
local a_remaining = old_a_vel:vsubi(velocity_a)
|
||||||
|
local b_remaining = old_b_vel:vsubi(velocity_b)
|
||||||
|
--transfer it to the other body
|
||||||
|
velocity_a:fmai(b_remaining, conservation)
|
||||||
|
velocity_b:fmai(a_remaining, conservation)
|
||||||
|
--clean up
|
||||||
|
a_remaining:release()
|
||||||
|
b_remaining:release()
|
||||||
|
end
|
||||||
|
|
||||||
return intersect
|
return intersect
|
||||||
|
120
sequence.lua
120
sequence.lua
@ -24,73 +24,85 @@ sequence.sort = stable_sort
|
|||||||
--patch various interfaces in a type-preserving way, for method chaining
|
--patch various interfaces in a type-preserving way, for method chaining
|
||||||
|
|
||||||
--import copying tablex
|
--import copying tablex
|
||||||
function sequence:keys()
|
--(common case where something returns another sequence for chaining)
|
||||||
return sequence(table.keys(self))
|
for _, v in ipairs({
|
||||||
|
"keys",
|
||||||
|
"values",
|
||||||
|
"dedupe",
|
||||||
|
"collapse",
|
||||||
|
"append",
|
||||||
|
"overlay",
|
||||||
|
"copy",
|
||||||
|
}) do
|
||||||
|
local table_f = table[v]
|
||||||
|
sequence[v] = function(self, ...)
|
||||||
|
return sequence(table_f(self, ...))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sequence:values()
|
--aliases
|
||||||
return sequence(table.values(self))
|
for _, v in ipairs({
|
||||||
|
{"flatten", "collapse"},
|
||||||
|
}) do
|
||||||
|
sequence[v[1]] = sequence[v[2]]
|
||||||
end
|
end
|
||||||
|
|
||||||
function sequence:dedupe()
|
--import functional interface in method form
|
||||||
return sequence(table.dedupe(self))
|
|
||||||
|
--(common case where something returns another sequence for chaining)
|
||||||
|
for _, v in ipairs({
|
||||||
|
"map",
|
||||||
|
"map_inplace",
|
||||||
|
"filter",
|
||||||
|
"filter_inplace",
|
||||||
|
"remove_if",
|
||||||
|
"zip",
|
||||||
|
"stitch",
|
||||||
|
"cycle",
|
||||||
|
}) do
|
||||||
|
local functional_f = functional[v]
|
||||||
|
sequence[v] = function(self, ...)
|
||||||
|
return sequence(functional_f(self, ...))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function sequence:collapse()
|
--(cases where we don't want to construct a new sequence)
|
||||||
return sequence(table.collapse(self))
|
for _, v in ipairs({
|
||||||
end
|
"foreach",
|
||||||
sequence.flatten = sequence.collapse
|
"reduce",
|
||||||
|
"any",
|
||||||
function sequence:append(...)
|
"none",
|
||||||
return sequence(table.append(self, ...))
|
"all",
|
||||||
|
"count",
|
||||||
|
"contains",
|
||||||
|
"sum",
|
||||||
|
"mean",
|
||||||
|
"minmax",
|
||||||
|
"max",
|
||||||
|
"min",
|
||||||
|
"find_min",
|
||||||
|
"find_max",
|
||||||
|
"find_nearest",
|
||||||
|
"find_match",
|
||||||
|
}) do
|
||||||
|
sequence[v] = functional[v]
|
||||||
end
|
end
|
||||||
|
|
||||||
function sequence:overlay(...)
|
|
||||||
return sequence(table.overlay(self, ...))
|
--aliases
|
||||||
end
|
for _, v in ipairs({
|
||||||
|
{"remap", "map_inplace"},
|
||||||
function sequence:copy(...)
|
{"map_stitch", "stitch"},
|
||||||
return sequence(table.copy(self, ...))
|
{"map_cycle", "cycle"},
|
||||||
end
|
{"find_best", "find_max"},
|
||||||
|
}) do
|
||||||
--import functional interface
|
sequence[v[1]] = sequence[v[2]]
|
||||||
function sequence:foreach(f)
|
|
||||||
return functional.foreach(self, f)
|
|
||||||
end
|
|
||||||
|
|
||||||
function sequence:reduce(seed, f)
|
|
||||||
return functional.reduce(self, seed, f)
|
|
||||||
end
|
|
||||||
|
|
||||||
function sequence:map(f)
|
|
||||||
return sequence(functional.map(self, f))
|
|
||||||
end
|
|
||||||
|
|
||||||
function sequence:map_inplace(f)
|
|
||||||
return sequence(functional.map_inplace(self, f))
|
|
||||||
end
|
|
||||||
|
|
||||||
sequence.remap = sequence.map_inplace
|
|
||||||
|
|
||||||
function sequence:filter(f)
|
|
||||||
return sequence(functional.filter(self, f))
|
|
||||||
end
|
|
||||||
|
|
||||||
function sequence:filter_inplace(f)
|
|
||||||
return sequence(functional.filter_inplace(self, f))
|
|
||||||
end
|
|
||||||
|
|
||||||
function sequence:remove_if(f)
|
|
||||||
return sequence(functional.remove_if(self, f))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--(anything that needs bespoke wrapping)
|
||||||
function sequence:partition(f)
|
function sequence:partition(f)
|
||||||
local a, b = functional.partition(self, f)
|
local a, b = functional.partition(self, f)
|
||||||
return sequence(a), sequence(b)
|
return sequence(a), sequence(b)
|
||||||
end
|
end
|
||||||
|
|
||||||
function sequence:zip(other, f)
|
|
||||||
return sequence(functional.zip(self, other, f))
|
|
||||||
end
|
|
||||||
|
|
||||||
return sequence
|
return sequence
|
||||||
|
30
stringx.lua
30
stringx.lua
@ -215,11 +215,8 @@ function stringx.trim(s)
|
|||||||
return s:sub(head, tail)
|
return s:sub(head, tail)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--trim the start of a string
|
||||||
function stringx.ltrim(s)
|
function stringx.ltrim(s)
|
||||||
if s == "" or s == string.rep(" ", s:len()) then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local head = 1
|
local head = 1
|
||||||
for i = 1, #s do
|
for i = 1, #s do
|
||||||
if not _whitespace_bytes[s:byte(i)] then
|
if not _whitespace_bytes[s:byte(i)] then
|
||||||
@ -227,13 +224,14 @@ function stringx.ltrim(s)
|
|||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if head == 1 then
|
||||||
|
return s
|
||||||
|
end
|
||||||
return s:sub(head)
|
return s:sub(head)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--trim the end of a string
|
||||||
function stringx.rtrim(s)
|
function stringx.rtrim(s)
|
||||||
if s == "" or s == string.rep(" ", s:len()) then
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
local tail = #s
|
local tail = #s
|
||||||
|
|
||||||
for i = #s, 1, -1 do
|
for i = #s, 1, -1 do
|
||||||
@ -243,6 +241,10 @@ function stringx.rtrim(s)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if tail == #s then
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
return s:sub(1, tail)
|
return s:sub(1, tail)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -322,13 +324,13 @@ function stringx.starts_with(s, prefix)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function stringx.ends_with(s, posfix)
|
--check if a given string ends with another
|
||||||
if posfix == "" then return true end
|
--(without garbage)
|
||||||
|
function stringx.ends_with(s, suffix)
|
||||||
if #posfix > #s then return false end
|
local len = #s
|
||||||
|
local suffix_len = #suffix
|
||||||
for i = 0, #posfix-1 do
|
for i = 0, suffix_len - 1 do
|
||||||
if s:byte(#s-i) ~= posfix:byte(#posfix-i) then
|
if s:byte(len - i) ~= suffix:byte(suffix_len - i) then
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
2
vec2.lua
2
vec2.lua
@ -16,7 +16,7 @@ end
|
|||||||
|
|
||||||
--probably-too-flexible ctor
|
--probably-too-flexible ctor
|
||||||
function vec2:new(x, y)
|
function vec2:new(x, y)
|
||||||
if x and y then
|
if type(x) == "number" and type(y) == "number" then
|
||||||
return vec2:xy(x,y)
|
return vec2:xy(x,y)
|
||||||
elseif x then
|
elseif x then
|
||||||
if type(x) == "number" then
|
if type(x) == "number" then
|
||||||
|
Loading…
Reference in New Issue
Block a user