| if Platform.cmdline then | |
| return | |
| end | |
| ------------------ Math functions ------------------------ | |
| --- Caclulate the difference between 2 given angles in minutes; the result is from -180*60 to 180*60 minutes. | |
| -- @cstyle int AngleDiff(int a1, int a2). | |
| -- @param a1 int; angle 1 in minutes. | |
| -- @param a2 int; angle 2 in minutes. | |
| -- @return int; difference in minutes. | |
| -- reimplemented in C in luaExports.cpp | |
| --- Caclulates the closest angle from given list to the angle 'a' given. | |
| -- @cstyle int ClosestAngle(int a, ...) | |
| -- @param a int; angle in minutes. | |
| -- @return int, int; the closest angle and the min difference. | |
| function ClosestAngle(a, ...) | |
| local best_diff, angle = 1000000, false | |
| for _, v in pairs{...} do | |
| local diff = abs(AngleDiff(v, a)) | |
| if best_diff > diff then | |
| best_diff = diff | |
| angle = v | |
| end | |
| end | |
| return angle, angle and best_diff or false | |
| end | |
| --- Clamps an angle value between a minimum and maximum angle. | |
| -- @param a int The angle value to clamp. | |
| -- @param min int The minimum angle value. | |
| -- @param max int The maximum angle value. | |
| -- @return int The clamped angle value. | |
| function ClampAngle(a, min, max) | |
| local diff1, diff2 = AngleDiff(a, min), AngleDiff(a, max) | |
| if diff1 < 0 and diff2 > 0 then | |
| return -diff1 > diff2 and max or min | |
| end | |
| return a | |
| end | |
| --- Rotates given point around arbitrary center. | |
| -- @cstyle point RotateAroundCenter(point center, point pt, int angle). | |
| -- @param center point, the rotation center. | |
| -- @param pt point, the point to rotate. | |
| -- @param angle int, angle to rotate in minutes. | |
| -- @return point; the rotated point. | |
| function RotateAroundCenter(center, pt, angle, new_len) | |
| local len = new_len or (pt-center):Len() | |
| return center + SetLen(Rotate(pt-center, angle), len) | |
| end | |
| --- Performs a double multiplication and division with truncation. | |
| -- This function first performs a multiplication and division with truncation, and then performs another multiplication and division with truncation on the result. | |
| -- @param a number The first number to multiply. | |
| -- @param b number The second number to multiply. | |
| -- @param c number The number to divide by. | |
| -- @return number The result of the double multiplication and division with truncation. | |
| function MulDivTrunc2(a, b, c) | |
| return MulDivTrunc(MulDivTrunc(a, b, c), b, c) | |
| end | |
| --- Calculates the trajectory of an object given the starting and ending positions, the time of travel, and the acceleration due to gravity. | |
| -- @param from point The starting position of the object. | |
| -- @param to point The ending position of the object. | |
| -- @param time number The time of travel in seconds. | |
| -- @param g number The acceleration due to gravity in meters per second squared. | |
| -- @return function, number The trajectory function and the angle of the trajectory in radians. | |
| function TrajectoryTime(from, to, time, g) | |
| local delta = (to - from):SetInvalidZ() | |
| local d = delta:Len() | |
| local angle = atan(MulDivTrunc(time, time * g, 1000 * 1000), 2 * d) | |
| local v = sqrt(d * g) * 4096 / (sin(2 * angle)) | |
| local z_error = 0 | |
| local function f(t) | |
| local error_compensation = z_error * t / time -- compensate error | |
| local x = d * t / time | |
| local h = x * sin(angle) / cos(angle) - MulDivTrunc2(MulDivTrunc2(g / 2, x, v), 4096, cos(angle)) | |
| return from + (delta * Clamp(t, 0, time) / time):SetZ(h + from:z() + error_compensation) | |
| end | |
| z_error = to:z() - f(time):z() | |
| return f, angle / 60 | |
| end | |
| -- angle is in minutes | |
| --- Calculates the trajectory of an object given the starting and ending positions, the angle of the trajectory, and the acceleration due to gravity. | |
| -- @param from point The starting position of the object. | |
| -- @param to point The ending position of the object. | |
| -- @param angle number The angle of the trajectory in radians. | |
| -- @param g number The acceleration due to gravity in meters per second squared. | |
| -- @return function, number The trajectory function and the time of travel in seconds. | |
| function TrajectoryAngle(from, to, angle, g) | |
| local delta = (to - from):SetInvalidZ() | |
| local d = delta:Len() | |
| local v = sqrt(d * g) * 4096 / (sin(2 * angle)) | |
| local time = MulDivTrunc(d, 4098000, v * cos(angle)) | |
| local z_error = 0 | |
| local function f(t) | |
| local error_compensation = z_error * t / time -- compensate error | |
| local x = d * t / time -- mult * 100 | |
| local h = x * sin(angle) / cos(angle) - MulDivTrunc2(MulDivTrunc2(g / 2, x, v), 4096, cos(angle)) | |
| return from + (delta * Clamp(t, 0, time) / time):SetZ(h + error_compensation) | |
| end | |
| z_error = to:z() - f(time):z() | |
| return f, time | |
| end | |
| --- Perfrom quadratic interpolation over 3 values(to, (from-to)*med, from). | |
| -- @cstyle lerp_function Qerp(int from, int to, int med, int total_time, capped). | |
| -- @param from int; starting interpolation value. | |
| -- @param to int; ending interpolation value. | |
| -- @param med int; percentage from 1 to 99 which defines the return value when time parameter is total_time/2(3rd key value). | |
| -- @param total_time int. | |
| -- @param capped bool; if capped is true then the returned values is always in the range [to..from]. | |
| -- @return function; a function that given a time from 0 to total_time will return the interpolated value. | |
| function Qerp(from, to, med, total_time, capped) | |
| if total_time == 0 then | |
| return | |
| function() | |
| return to | |
| end | |
| end | |
| local a, b = 200 - 4 * med, 4 * med - 100 | |
| local delta = to - from | |
| return | |
| function(time) | |
| if capped then | |
| if time < 0 then | |
| return from | |
| end | |
| if time >= total_time then | |
| return to | |
| end | |
| end | |
| local t = ((delta*time/total_time)*time/total_time)*a/100 + (delta*time/total_time)*b/100 | |
| return from + t | |
| end | |
| end | |
| --- Given 2 points it return those points with Zs modified so they can be interpolated i.e. both have valid Zs or both have invalid Zs. | |
| -- effectively if one of the point is with invalid Z and the other is with valid z then the function returns the first point with z = terrain haight and the second point unmodified. | |
| -- @cstyle point, point CalcZForInterpolation(p1, p2). | |
| -- @param p1 int; first point. | |
| -- @param p2 int; second point. | |
| -- @return point, point; points good for interpolation. | |
| function CalcZForInterpolation(p1, p2) | |
| local p1_isvalid_z, p2_isvalid_z = p1:IsValidZ(), p2:IsValidZ() | |
| if p1_isvalid_z ~= p2_isvalid_z then | |
| return p1_isvalid_z and p1 or p1:SetZ(terrain.GetHeight(p1)), p2_isvalid_z and p2 or p2:SetZ(terrain.GetHeight(p2)) | |
| end | |
| return p1, p2 | |
| end | |
| --- Perfrom linear interpolation over 2 values. | |
| -- @cstyle lerp_function ValueLerp(int from, int to, int total_time). | |
| -- @param from int; starting interpolation value. | |
| -- @param to int; ending interpolation value. | |
| -- @param total_time int. | |
| -- @param capped bool; if capped is true then the returned values is always in the range [to..from]. | |
| -- @return function; a function that given a time from 0 to total_time will return the interpolated value. | |
| function ValueLerp(from, to, total_time, capped) | |
| if IsPoint(from) then | |
| from, to = CalcZForInterpolation(from, to) | |
| end | |
| local delta = to - from | |
| if total_time == 0 then | |
| return | |
| function() | |
| return to | |
| end | |
| end | |
| local useMulDiv = not capped | |
| if not useMulDiv then | |
| local o = MulDivTrunc(delta, total_time, 2147483647) | |
| if type(o) == "number" then | |
| useMulDiv = o ~= 0 | |
| else | |
| useMulDiv = o:Len() > 0 | |
| end | |
| end | |
| if useMulDiv then | |
| if capped then | |
| return | |
| function(time) | |
| return from + MulDivTrunc(delta, Clamp(time, 0, total_time), total_time) | |
| end | |
| else | |
| return | |
| function(time) | |
| return from + MulDivTrunc(delta, time, total_time) | |
| end | |
| end | |
| else | |
| assert(capped == true) | |
| return | |
| function(time) | |
| return from + delta * Clamp(time, 0, total_time) / total_time | |
| end | |
| end | |
| end | |
| --- Perfrom linear interpolation over 2 values over game time. | |
| -- @cstyle lerp_function GameTimeLerp(int from, int to, int total_time). | |
| -- @param from int; starting interpolation value. | |
| -- @param to int; ending interpolation value. | |
| -- @param total_time int. | |
| -- @param capped bool; if capped is true then the returned values is always in the range [to..from]. | |
| -- @return function; a function that given a time from game time "now" to game time "now" + total_time will return the interpolated value. | |
| function GameTimeLerp(from, to, total_time, capped) | |
| local start_time = GameTime() | |
| if IsPoint(from) then | |
| from, to = CalcZForInterpolation(from, to) | |
| end | |
| local delta = to - from | |
| if total_time == 0 then | |
| return | |
| function() | |
| return to | |
| end | |
| end | |
| local useMulDiv = not capped | |
| if not useMulDiv then | |
| local o = MulDivTrunc(delta, total_time, 2147483647) | |
| if type(o) == "number" then | |
| useMulDiv = o ~= 0 | |
| else | |
| useMulDiv = o:Len() > 0 | |
| end | |
| end | |
| if useMulDiv then | |
| if capped then | |
| return | |
| function(time) | |
| return from + MulDivTrunc(delta, Clamp(time - start_time, 0, total_time), total_time) | |
| end | |
| else | |
| return | |
| function(time) | |
| return from + MulDivTrunc(delta, time - start_time, total_time) | |
| end | |
| end | |
| else | |
| assert(capped == true) | |
| return | |
| function(time) | |
| return from + delta * Clamp(time - start_time, 0, total_time) / total_time | |
| end | |
| end | |
| end | |
| --- Perfrom linear interpolation over angles. | |
| -- @cstyle lerp_function AngleLerp(int from, int to, int total_time). | |
| -- @param from int; starting interpolation value. | |
| -- @param to int; ending interpolation value. | |
| -- @param total_time int. | |
| -- @param capped bool; if capped is true then the returned values is always in the range [to..from]. | |
| -- @return function; a function that given a time from 0 to total_time will return the interpolated value. | |
| function AngleLerp(from, to, total_time, capped) | |
| local delta = AngleDiff(to, from) | |
| if total_time == 0 then | |
| return | |
| function() | |
| return to | |
| end | |
| end | |
| return | |
| function(time) | |
| if capped then | |
| if time <= 0 then | |
| return from | |
| end | |
| if time >= total_time then | |
| return to | |
| end | |
| end | |
| return AngleNormalize(from + delta * time / total_time) | |
| end | |
| end | |
| --- Returns a point moved a given distance from the source point towards the dest point. | |
| -- @cstyle point MovePoint(point src, point dest, int dist). | |
| -- @param src point; the source point to move. | |
| -- @param dest point; the destination point. | |
| -- @param dist int; distance to move. | |
| -- @return point. | |
| function MovePoint(src, dest, dist) | |
| dest, src = CalcZForInterpolation(dest, src) | |
| local v = dest - src | |
| if v:Len() > dist then v = SetLen(v, dist) end | |
| return src + v | |
| end | |
| --- ATTENTION!!! This function works only in 2D, and returns only points in the same Z. | |
| --- Returns the nearest passable point to a point moved a given distance from the source point away from the dest point. | |
| -- @cstyle point MovePointAway(point src, point dest, int dist). | |
| -- @param src point; the source point to move. | |
| -- @param dest point; the destination point. | |
| -- @param dist int; distance to move. | |
| -- @return point. | |
| function MovePointAwayPass(src, dest, dist) | |
| local v = dest - src | |
| v = SetLen(v, dist) | |
| local pt = src - v | |
| local pass = GetPassablePointNearby(pt) | |
| return pass and terrain.IsPointInBounds(pass) and pass or pt | |
| end | |
| --- Returns a point moved a given distance from the source point away from the dest point. | |
| -- @cstyle point MovePointAway(point src, point dest, int dist). | |
| -- @param src point; the source point to move. | |
| -- @param dest point; the destination point. | |
| -- @param dist int; distance to move. | |
| -- @return point. | |
| function MovePointAway(src, dest, dist) | |
| dest, src = CalcZForInterpolation(dest, src) | |
| local v = dest - src | |
| v = SetLen(v, dist) | |
| return src - v | |
| end | |
| --- Calculates the angle between two 3D vectors. | |
| -- @param v1 point; the first vector | |
| -- @param v2 point; the second vector | |
| -- @return number; the angle between the two vectors in radians | |
| function Angle3dVectors(v1, v2) | |
| return acos(MulDivTrunc(Dot(v1, v2), 4096, v1:Len() * v2:Len())) | |
| end | |
| ------------------------------------------------ | |
| --- Returns a list of points in a radial pattern around a given position. | |
| -- @param n int; the number of points to generate | |
| -- @param pos point; the center position | |
| -- @param direction point; the direction to orient the radial pattern | |
| -- @param radius number; the radius of the radial pattern | |
| -- @return table; a list of points in the radial pattern | |
| function GetRadialOffsets(n, pos, direction, radius) | |
| -- Implementation details | |
| end | |
| function GetRadialOffsets(n, pos, direction, radius) | |
| local off1 = point(-direction:y(), direction:x(), 0) | |
| if off1 == point30 then | |
| off1 = point(1, 0, 0) | |
| end | |
| off1 = SetLen(off1, radius) | |
| local offs = {off1} | |
| for i = 1, n - 1 do | |
| table.insert(offs, RotateAxis(off1, direction, (360 * 60 * i) / n)) | |
| end | |
| return offs | |
| end | |
| --- Returns a list of points in a radial pattern around a given position. | |
| -- @param n int; the number of points to generate | |
| -- @param pos point; the center position | |
| -- @param direction point; the direction to orient the radial pattern | |
| -- @param radius number; the radius of the radial pattern | |
| -- @return table; a list of points in the radial pattern | |
| function GetRadialPoints(n, pos, direction, radius) | |
| local ps = GetRadialOffsets(n, pos, direction, radius) | |
| for i = 1, n do | |
| ps[i] = pos + ps[i] | |
| end | |
| return ps | |
| end | |
| function GetRadialPoints(n, pos, direction, radius) | |
| local ps = GetRadialOffsets(n, pos, direction, radius) | |
| for i = 1, n do | |
| ps[i] = pos + ps[i] | |
| end | |
| return ps | |
| end | |
| --- Returns a value scaled between a minimum and maximum value based on a percentage. | |
| -- @param min number The minimum value. | |
| -- @param max number The maximum value. | |
| -- @param perc number The percentage value between 0 and 1. | |
| -- @param div number (optional) The divisor to use for the percentage. Defaults to 100. | |
| -- @return number The scaled value between min and max. | |
| function GetScaledValue(min, max, perc, div) | |
| div = div or 100 | |
| return min + MulDivRound(max - min, perc, div) | |
| end | |
| --- Divides a value `v` by a divisor `d` and rounds up the result to the nearest integer. | |
| -- @param v number The value to divide. | |
| -- @param d number The divisor. | |
| -- @return number The result of the division, rounded up to the nearest integer. | |
| function DivCeil(v, d) | |
| v = v + d - 1 | |
| return v / d | |
| end | |
| -- Re-map value from one range to another range. | |
| function MapRange(value, new_range_max, new_range_min, old_range_max, old_range_min) | |
| if old_range_max == old_range_min then | |
| return new_range_max | |
| end | |
| return MulDivRound(new_range_max - new_range_min , value - old_range_max, old_range_max - old_range_min) + new_range_max | |
| end |