sirnii's picture
Upload 1816 files
b6a38d7 verified
raw
history blame
14.5 kB
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