--!native --!optimize 2 -- Compiled with roblox-ts v3.0.0 local MathTools = {} do local _container = MathTools local max = math.max local min = math.min local sign = math.sign local abs = math.abs local ceil = math.ceil local pow = math.pow local tau = 2 * math.pi --[[ * * @param n * @returns n! ]] local function Factor(n) n = math.floor(n) if n == 0 or n == 1 then return 1 end local result = n while n > 1 do n -= 1 result *= n end return result end _container.Factor = Factor local function Lerp(start, target, alpha) return start + (target - start) * alpha end _container.Lerp = Lerp --[[ * * goes in the direction of the target with step, will never overshoot the target * * ```e.g start = 5, target = 6, step = 2 direction = 6 - 5 = 1 returns 5 + math.min(1, 2) = 6``` * * ```e.g start = 6, target = 4, step = 1 direction = 4 - 6 = -2 returns 6 - math.min(math.abs(-2), 1) = 5``` * * sugar for linear spring * ```ts * current = LerpWith(current, target, speed * dt) * ``` * * @param start * @param target * @param step * @returns ]] local function LerpWith(start, target, step) local difference = target - start --calculating min difference towards target; step = sign(difference) * min(step, abs(difference)) return start + step end _container.LerpWith = LerpWith --*compares 2 numbers with epsilon precision local function FuzzyEq(a, b, epsilon) if epsilon == nil then epsilon = 1e-5 end return a == b or math.abs(a - b) <= (math.abs(a) + 1) * epsilon end _container.FuzzyEq = FuzzyEq --[[ *wraps angle to range [-pi, pi) * @see https://stackoverflow.com/questions/2320986/easy-way-to-keeping-angles-between-179-and-180-degrees ]] local function NormalizeAngle(alpha) return alpha - math.floor((alpha + math.pi) / tau) * tau end _container.NormalizeAngle = NormalizeAngle --*@see https://stackoverflow.com/questions/1878907/how-can-i-find-the-smallest-difference-between-two-angles-around-a-point local function GetShortestAngle(start, target) if start == target then return 0 end local difference = target - start return ((difference + math.pi) % tau) - math.pi end _container.GetShortestAngle = GetShortestAngle --[[ *lerpes the angle with fixed step * angle [-pi, pi] ]] local function LerpAngleWith(start, target, step) if start == target then return NormalizeAngle(target) end local angle = GetShortestAngle(start, target) --calculates minimal step to not overwalk the angle step = min(step, abs(angle)) --step * sign(angle) -- steps in direction of the target angle local new_angle = start + step * sign(angle) --returns normalized angle return NormalizeAngle(new_angle) end _container.LerpAngleWith = LerpAngleWith --*maps the value from 1 range to other local function Map(value, input_min, input_max, output_min, output_max, clamp) local difference_input = input_max - input_min local difference_output = output_max - output_min local multiplier = difference_output / difference_input local current_difference = value - input_min local output = output_min + current_difference * multiplier if clamp then local min_output = min(output_min, output_max) local max_output = max(output_min, output_max) --can be error if use clamp output = max(output, min_output) output = min(output, max_output) end return output end _container.Map = Map --[[ *wraps number in range * 2, 2, 0, 2 => 2 * 0, 4, 0, 3 => 1 * 0 + 4 = 4 - (3 - 0) => 1 ]] local function WrapAdd(value, step, min_value, max_value) local offset_to_min_value = value + step - min_value local range = max_value - min_value --prevent cases with min and max values 0; if range == 0 then return min_value end --turns -1 to 0 without abs local range_ratio = ceil(abs(offset_to_min_value / range)) if offset_to_min_value > range then --it should be in range of a range, or will subtract 2 ranges offset_to_min_value -= range * (range_ratio - 1) elseif offset_to_min_value < 0 then offset_to_min_value += range * range_ratio end return min_value + offset_to_min_value end _container.WrapAdd = WrapAdd -- export function WrapPage(page_number: number, min_page: number, max_page: number) { -- } --[[ * * * unit number [0, 1] * bias [-inf, 1]; * bias -1 -larger numbers are very frequent * bias 1 -smaller numbers are very frequent * bias power of the curve * gets bigger numbers that are less frequent *@see https://www.youtube.com/watch?v=lctXaT9pxA0&t=454s&ab_channel=SebastianLague ]] local function BiasFunction(unit_number, bias) --pow works better than ^ local k = pow(1 - bias, 3) return (unit_number * k) / (unit_number * k - unit_number + 1) end _container.BiasFunction = BiasFunction local function GetValueFromNumberSequence(number_sequence, alpha) local keypoints = number_sequence.Keypoints --return first if alpha is 0 if alpha == 0 then return keypoints[1].Value end --return last if alpha is 1 if alpha == 1 then return keypoints[#keypoints].Value end local current_keypoint local next_keypoint local value = 0 for _ = 0, #keypoints - 2 do current_keypoint = keypoints[1] next_keypoint = keypoints[2] if not (alpha >= current_keypoint.Time and alpha <= next_keypoint.Time) then continue end --*time from the current keypoint local offset = alpha - current_keypoint.Time --*time range between the keypoints local time_range = next_keypoint.Time - current_keypoint.Time --*value range between the keypoints local value_range = next_keypoint.Value - current_keypoint.Value --*calculates the value, remaps offset from keypoint from time range to value range value = (offset / time_range) * value_range break end return value end _container.GetValueFromNumberSequence = GetValueFromNumberSequence --[[ * * * @param value * @returns whether the value is nan ]] local function IsNan(value) return value == value end _container.IsNan = IsNan --[[ * * * @param value * @returns 0 if value is nan ]] local function FixNumber(value) return if value == value then value else 0 end _container.FixNumber = FixNumber end return { MathTools = MathTools, }