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 tablex = require(path .. "tablex")
|
||||
local mathx = require(path .. "mathx")
|
||||
|
||||
local functional = setmetatable({}, {
|
||||
__index = tablex,
|
||||
@ -172,6 +173,54 @@ function functional.zip(t1, t2, f)
|
||||
return ret
|
||||
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
|
||||
-----------------------------------------------------------
|
||||
|
@ -55,7 +55,7 @@ function intersect.circle_circle_collide(a_pos, a_rad, b_pos, b_rad, into)
|
||||
into = vec2:zero()
|
||||
end
|
||||
--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
|
||||
end
|
||||
return false
|
||||
@ -97,7 +97,7 @@ function intersect._line_displacement_to_sep(a_start, a_end, separation, total_r
|
||||
if sep <= 0 then
|
||||
if distance <= COLLIDE_EPS then
|
||||
--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
|
||||
separation:smuli(-sep)
|
||||
end
|
||||
@ -418,4 +418,60 @@ function intersect.point_in_poly(point, poly)
|
||||
return wn ~= 0
|
||||
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
|
||||
|
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
|
||||
|
||||
--import copying tablex
|
||||
function sequence:keys()
|
||||
return sequence(table.keys(self))
|
||||
--(common case where something returns another sequence for chaining)
|
||||
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
|
||||
|
||||
function sequence:values()
|
||||
return sequence(table.values(self))
|
||||
--aliases
|
||||
for _, v in ipairs({
|
||||
{"flatten", "collapse"},
|
||||
}) do
|
||||
sequence[v[1]] = sequence[v[2]]
|
||||
end
|
||||
|
||||
function sequence:dedupe()
|
||||
return sequence(table.dedupe(self))
|
||||
--import functional interface in method form
|
||||
|
||||
--(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
|
||||
|
||||
function sequence:collapse()
|
||||
return sequence(table.collapse(self))
|
||||
end
|
||||
sequence.flatten = sequence.collapse
|
||||
|
||||
function sequence:append(...)
|
||||
return sequence(table.append(self, ...))
|
||||
--(cases where we don't want to construct a new sequence)
|
||||
for _, v in ipairs({
|
||||
"foreach",
|
||||
"reduce",
|
||||
"any",
|
||||
"none",
|
||||
"all",
|
||||
"count",
|
||||
"contains",
|
||||
"sum",
|
||||
"mean",
|
||||
"minmax",
|
||||
"max",
|
||||
"min",
|
||||
"find_min",
|
||||
"find_max",
|
||||
"find_nearest",
|
||||
"find_match",
|
||||
}) do
|
||||
sequence[v] = functional[v]
|
||||
end
|
||||
|
||||
function sequence:overlay(...)
|
||||
return sequence(table.overlay(self, ...))
|
||||
end
|
||||
|
||||
function sequence:copy(...)
|
||||
return sequence(table.copy(self, ...))
|
||||
end
|
||||
|
||||
--import functional interface
|
||||
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))
|
||||
|
||||
--aliases
|
||||
for _, v in ipairs({
|
||||
{"remap", "map_inplace"},
|
||||
{"map_stitch", "stitch"},
|
||||
{"map_cycle", "cycle"},
|
||||
{"find_best", "find_max"},
|
||||
}) do
|
||||
sequence[v[1]] = sequence[v[2]]
|
||||
end
|
||||
|
||||
--(anything that needs bespoke wrapping)
|
||||
function sequence:partition(f)
|
||||
local a, b = functional.partition(self, f)
|
||||
return sequence(a), sequence(b)
|
||||
end
|
||||
|
||||
function sequence:zip(other, f)
|
||||
return sequence(functional.zip(self, other, f))
|
||||
end
|
||||
|
||||
return sequence
|
||||
|
30
stringx.lua
30
stringx.lua
@ -215,11 +215,8 @@ function stringx.trim(s)
|
||||
return s:sub(head, tail)
|
||||
end
|
||||
|
||||
--trim the start of a string
|
||||
function stringx.ltrim(s)
|
||||
if s == "" or s == string.rep(" ", s:len()) then
|
||||
return ""
|
||||
end
|
||||
|
||||
local head = 1
|
||||
for i = 1, #s do
|
||||
if not _whitespace_bytes[s:byte(i)] then
|
||||
@ -227,13 +224,14 @@ function stringx.ltrim(s)
|
||||
break
|
||||
end
|
||||
end
|
||||
if head == 1 then
|
||||
return s
|
||||
end
|
||||
return s:sub(head)
|
||||
end
|
||||
|
||||
--trim the end of a string
|
||||
function stringx.rtrim(s)
|
||||
if s == "" or s == string.rep(" ", s:len()) then
|
||||
return ""
|
||||
end
|
||||
local tail = #s
|
||||
|
||||
for i = #s, 1, -1 do
|
||||
@ -243,6 +241,10 @@ function stringx.rtrim(s)
|
||||
end
|
||||
end
|
||||
|
||||
if tail == #s then
|
||||
return s
|
||||
end
|
||||
|
||||
return s:sub(1, tail)
|
||||
end
|
||||
|
||||
@ -322,13 +324,13 @@ function stringx.starts_with(s, prefix)
|
||||
return true
|
||||
end
|
||||
|
||||
function stringx.ends_with(s, posfix)
|
||||
if posfix == "" then return true end
|
||||
|
||||
if #posfix > #s then return false end
|
||||
|
||||
for i = 0, #posfix-1 do
|
||||
if s:byte(#s-i) ~= posfix:byte(#posfix-i) then
|
||||
--check if a given string ends with another
|
||||
--(without garbage)
|
||||
function stringx.ends_with(s, suffix)
|
||||
local len = #s
|
||||
local suffix_len = #suffix
|
||||
for i = 0, suffix_len - 1 do
|
||||
if s:byte(len - i) ~= suffix:byte(suffix_len - i) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user