mirror of
https://github.com/akaAgar/the-universal-mission-for-dcs-world.git
synced 2025-11-25 19:31:01 +00:00
Initial commit
This commit is contained in:
351
Script/DCS extensions/Math.lua
Normal file
351
Script/DCS extensions/Math.lua
Normal file
@@ -0,0 +1,351 @@
|
||||
-- ====================================================================================
|
||||
-- (DCS LUA ADD-ON) MATH - EXTENSION TO THE "MATH" TABLE
|
||||
--
|
||||
-- (Constant) DCSEx.math.TWO_PI
|
||||
-- DCSEx.math.addVec(vecA, vecB)
|
||||
-- DCSEx.math.clamp(val, min, max)
|
||||
-- DCSEx.math.getBearing(point, refPoint)
|
||||
-- DCSEx.math.getDistance2D(vec2a, vec2b)
|
||||
-- DCSEx.math.getDistance3D(vec3a, vec3b)
|
||||
-- DCSEx.math.getRelativeHeading(point, refObject)
|
||||
-- DCSEx.math.getVec2FromAngle(angle)
|
||||
-- DCSEx.math.isPointInsideCircle(center, radius, vec2)
|
||||
-- DCSEx.math.isPointInsidePolygon(polygon, vec2)
|
||||
-- DCSEx.math.isSamePoint(pointA, pointB)
|
||||
-- DCSEx.math.lerp(val0, val1, t)
|
||||
-- DCSEx.math.multVec2(vec2, mult)
|
||||
-- DCSEx.math.normalizeVec2(vec2)
|
||||
-- DCSEx.math.randomBoolean()
|
||||
-- DCSEx.math.randomFloat(min, max)
|
||||
-- DCSEx.math.randomPointAtDistance(point, distance)
|
||||
-- DCSEx.math.randomPointInCircle(center, radius, minRadius, surfaceType)
|
||||
-- DCSEx.math.randomSign()
|
||||
-- DCSEx.math.toBoolean(val)
|
||||
-- DCSEx.math.vec2ToVec3(vec2, y)
|
||||
-- DCSEx.math.vec3ToVec2(vec3)
|
||||
-- ====================================================================================
|
||||
|
||||
DCSEx.math = {}
|
||||
|
||||
-------------------------------------
|
||||
-- Constants
|
||||
-------------------------------------
|
||||
DCSEx.math.TWO_PI = math.pi * 2
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the sum of two vec2 or vec3
|
||||
-- @param vecA A vector
|
||||
-- @param vecB Another vector
|
||||
-- @return The sum of both vectors
|
||||
-------------------------------------
|
||||
function DCSEx.math.addVec(vecA, vecB)
|
||||
if vecA.z then
|
||||
return { x = vecA.x + vecB.x, y = vecA.y + vecB.y, z = vecA.z + vecB.z }
|
||||
end
|
||||
|
||||
return { x = vecA.x + vecB.x, y = vecA.y + vecB.y }
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Clamp a number value between min and max
|
||||
-- @param value The value to clamp
|
||||
-- @param min Minimum allowed value
|
||||
-- @param max Maximum allowed value
|
||||
-- @return Clamped value
|
||||
-------------------------------------
|
||||
function DCSEx.math.clamp(val, min, max)
|
||||
return math.max(min, math.min(max, val))
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Gets the bearing between two vectors, in degrees
|
||||
-- @param point A vec2/vec3
|
||||
-- @param refPoint Vec2/vec3 to use as a reference point
|
||||
-- @return Bearing, as a number, in degrees
|
||||
-------------------------------------
|
||||
function DCSEx.math.getBearing(point, refPoint)
|
||||
refPoint = refPoint or {x = 0, y = 0}
|
||||
if point.z then point = DCSEx.math.vec3ToVec2(point) end -- Point is a vec3, convert it
|
||||
if refPoint.z then refPoint = DCSEx.math.vec3ToVec2(refPoint) end -- Point is a vec3, convert it
|
||||
|
||||
local bearing = math.atan2(point.y - refPoint.y, point.x - refPoint.x)
|
||||
|
||||
if bearing < 0 then
|
||||
bearing = bearing + DCSEx.math.TWO_PI
|
||||
end
|
||||
|
||||
return math.floor(bearing * (180 / math.pi))
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the pythagorean distance between two 2D points or the length of a single vector
|
||||
-- @param vec2a A 2D point
|
||||
-- @param vec2b (optional) Another 2D point
|
||||
-- @return Distance between the points
|
||||
-------------------------------------
|
||||
function DCSEx.math.getDistance2D(vec2a, vec2b)
|
||||
vec2b = vec2b or { x = 0, y = 0 }
|
||||
if vec2a.z then vec2a = DCSEx.math.vec3ToVec2(vec2a) end
|
||||
if vec2b.z then vec2b = DCSEx.math.vec3ToVec2(vec2b) end
|
||||
|
||||
return math.sqrt((vec2a.x - vec2b.x) ^ 2 + (vec2a.y - vec2b.y) ^ 2)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the pythagorean distance between two 3D points or the length of a single vector
|
||||
-- @param vec3a A 3D point
|
||||
-- @param vec3b (optional) Another 3D point
|
||||
-- @return Distance between the points
|
||||
-------------------------------------
|
||||
function DCSEx.math.getDistance3D(vec3a, vec3b)
|
||||
vec3b = vec3b or { x = 0, y = 0, z = 0 }
|
||||
|
||||
return math.sqrt((vec3a.x - vec3b.x) ^ 2 + (vec3a.y - vec3b.y) ^ 2 + (vec3a.z - vec3b.z) ^ 2)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns the relative heading difference between refObject and a given point
|
||||
-- @param point The point for which to check the relative heading
|
||||
-- @param refObject The reference object against which relative heading should be measured
|
||||
-- @param format (optional) Return format. Possible formats are "clock" (1 o'clock...) or "cardinal" (NNW...)
|
||||
-- @return A number in degrees, or a string if a special format was provided
|
||||
-------------------------------------
|
||||
function DCSEx.math.getRelativeHeading(point, refObject, format)
|
||||
if not point then return nil end
|
||||
if not refObject then return nil end
|
||||
|
||||
local unitpos = refObject:getPosition()
|
||||
local bearing = DCSEx.math.getBearing(point, unitpos:getPoint())
|
||||
|
||||
local unitBearing = math.atan2(unitpos.x.z, unitpos.x.x)
|
||||
if unitBearing < 0 then unitBearing = unitBearing + DCSEx.math.TWO_PI end
|
||||
unitBearing = math.floor(unitBearing * (180 / math.pi))
|
||||
|
||||
local relBearing = bearing - unitBearing
|
||||
if relBearing < 0 then relBearing = relBearing + 360 end
|
||||
|
||||
if format == "oclock" then
|
||||
local oClock = math.floor(relBearing / 30)
|
||||
if oClock <= 0 then oClock = 12 end
|
||||
|
||||
return tostring(oClock).." o'clock"
|
||||
elseif format == "cardinal" then
|
||||
return "TODO" -- TODO: cardinal
|
||||
end
|
||||
|
||||
return math.floor(relBearing)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns an normalized vec2 from an angle/bearing in radians
|
||||
-- @param unit Angle/bearing in radians
|
||||
-- @return A normalized vec2
|
||||
-------------------------------------
|
||||
function DCSEx.math.getVec2FromAngle(angle)
|
||||
return {x = math.cos(angle), y = math.sin(angle)}
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Is a point inside a circle?
|
||||
-- @param center The center of the circle, as a vec2
|
||||
-- @param radius The radius of the circle
|
||||
-- @param vec2 A vec2
|
||||
-- @return True if vec2 is inside the circle, false otherwise
|
||||
-------------------------------------
|
||||
function DCSEx.math.isPointInsideCircle(center, radius, vec2)
|
||||
return (vec2.x - center.x) ^ 2 + (vec2.y - center.y) ^ 2 < radius ^ 2
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Is a point inside a polygon?
|
||||
-- @param vec2[] A polygon, as a table of vec2
|
||||
-- @param vec2 A vec2
|
||||
-- @return True if vec2 is inside the polygon, false otherwise
|
||||
-------------------------------------
|
||||
function DCSEx.math.isPointInsidePolygon(polygon, vec2)
|
||||
if not polygon or not vec2 then return false end
|
||||
|
||||
local oddNodes = false
|
||||
local j = #polygon
|
||||
for i = 1, #polygon do
|
||||
if (polygon[i].y < vec2.y and polygon[j].y >= vec2.y or polygon[j].y < vec2.y and polygon[i].y >= vec2.y) then
|
||||
if
|
||||
(polygon[i].x + (vec2.y - polygon[i].y) / (polygon[j].y - polygon[i].y) * (polygon[j].x - polygon[i].x) <
|
||||
vec2.x)
|
||||
then
|
||||
oddNodes = not oddNodes
|
||||
end
|
||||
end
|
||||
j = i
|
||||
end
|
||||
|
||||
return oddNodes
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Compares two 2D or 3D points
|
||||
-- @param pointA a Point2 or Point3
|
||||
-- @param pointB another Point2 or Point3
|
||||
-- @return True if points are the same, false otherwise
|
||||
-------------------------------------
|
||||
function DCSEx.math.isSamePoint(pointA, pointB)
|
||||
if pointA.x ~= pointB.x then return false end
|
||||
if pointA.y ~= pointB.y then return false end
|
||||
if pointA.z or pointB.z then
|
||||
if pointA.z ~= pointB.z then return false end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Linearly interpolates between two numbers
|
||||
-- @param val0 Value vers l=0
|
||||
-- @param val1 Value vers l=1
|
||||
-- @param t Interpolation between 0 and 1
|
||||
-- @return A number value
|
||||
-------------------------------------
|
||||
function DCSEx.math.lerp(val0, val1, t)
|
||||
return val0 + t * (val1 - val0);
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Multiplies both the x and y components of a vec2 by a floating-point value
|
||||
-- @param vec2 A vec2
|
||||
-- @param mult A floating-point value
|
||||
-- @return A vec2
|
||||
-------------------------------------
|
||||
function DCSEx.math.multVec2(vec2, mult)
|
||||
return {x = vec2.x * mult, y = vec2.y * mult}
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns an normalized vec2
|
||||
-- @param unit A vec2
|
||||
-- @return A normalized vec2
|
||||
-------------------------------------
|
||||
function DCSEx.math.normalizeVec2(vec2)
|
||||
local length = DCSEx.math.getDistance2D(vec2)
|
||||
return {x = vec2.x / length, y = vec2.y / length}
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns a random boolean
|
||||
-- @return A boolean
|
||||
-------------------------------------
|
||||
function DCSEx.math.randomBoolean()
|
||||
return DCSEx.math.random(1, 2) == 1
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns a random floating-point number between min and max
|
||||
-- @param min Minimum floating-point value
|
||||
-- @param max Maximum floating-point value
|
||||
-- @return A number
|
||||
-------------------------------------
|
||||
function DCSEx.math.randomFloat(min, max)
|
||||
if min >= max then
|
||||
return min
|
||||
end
|
||||
|
||||
return min + math.random() * (max - min)
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns a random vec2 at a given distance of another vec2
|
||||
-- @param point Reference point
|
||||
-- @param distance Distance from the reference point
|
||||
-- @return A vec2
|
||||
-------------------------------------
|
||||
function DCSEx.math.randomPointAtDistance(point, distance)
|
||||
local angle = math.random() * math.pi * 2.0
|
||||
|
||||
local x = point.x + math.cos(angle) * distance
|
||||
local y = point.y + math.sin(angle) * distance
|
||||
return { x = x, y = y }
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns a random vec2 in circle of a given center and radius
|
||||
-- @param center Center of the circle as a vec2
|
||||
-- @param radius Radius of the circle
|
||||
-- @param minRadius (optional) Minimum inner radius circle in which points should not be spawned
|
||||
-- @param surfaceType (optional) If not nil, point must be of given surface type
|
||||
-- @return A vec2 or nil if no point was found
|
||||
-------------------------------------
|
||||
function DCSEx.math.randomPointInCircle(center, radius, minRadius, surfaceType)
|
||||
local minRadius = minRadius or 0
|
||||
local dist = minRadius + math.random() * (radius - minRadius)
|
||||
local angle = math.random() * math.pi * 2.0
|
||||
|
||||
local point = nil
|
||||
for i=1,36 do
|
||||
local x = center.x + math.cos(angle) * dist
|
||||
local y = center.y + math.sin(angle) * dist
|
||||
|
||||
point = { x = x, y = y }
|
||||
|
||||
if not surfaceType then return point end
|
||||
if land.getSurfaceType(point) == surfaceType then return point end
|
||||
angle = angle + 0.174533 -- Increment angle by 10° (in radians)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Returns a random sign as a number, -1 or 1
|
||||
-- @return -1 50% of the time, 1 50% of the time
|
||||
-------------------------------------
|
||||
function DCSEx.math.randomSign()
|
||||
if math.random() < .5 then
|
||||
return -1
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Converts a value to a boolean
|
||||
-- @param val Value to convert
|
||||
-- @return A boolean, or nil if val was nil
|
||||
-------------------------------------
|
||||
function DCSEx.math.toBoolean(val)
|
||||
if val == nil then return nil end
|
||||
if not val then return false end
|
||||
if val == 0 then return false end
|
||||
if val:lower() == "false" or val:lower() == "no" or val:lower() == "off" then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Converts a vec2 to a vec3
|
||||
-- @param vec2 A vec2
|
||||
-- @param y (Optional) A value for the vec3's y component or "land" to use land height
|
||||
-- @return A vec3 where v3.x=v2.x, v3.y=y and v3.z=v2.y
|
||||
-------------------------------------
|
||||
function DCSEx.math.vec2ToVec3(vec2, y)
|
||||
-- Value was already a vec3
|
||||
if vec2.z then
|
||||
return {x = vec2.x, y = vec2.y, z = vec2.z}
|
||||
end
|
||||
|
||||
y = y or 0
|
||||
if y == "land" then y = land.getHeight(vec2) end
|
||||
|
||||
return {x = vec2.x, y = y, z = vec2.y}
|
||||
end
|
||||
|
||||
-------------------------------------
|
||||
-- Converts a vec3 to a vec2
|
||||
-- @param vec3 A vec3
|
||||
-- @return A vec2 where v2.x=v3.x and v2.y=v3.z
|
||||
-------------------------------------
|
||||
function DCSEx.math.vec3ToVec2(vec3)
|
||||
-- Value was already a vec2
|
||||
if not vec3.z then
|
||||
return {x = vec3.x, y = vec3.y}
|
||||
end
|
||||
|
||||
return {x = vec3.x, y = vec3.z}
|
||||
end
|
||||
Reference in New Issue
Block a user