mirror of
https://github.com/akaAgar/the-universal-mission-for-dcs-world.git
synced 2025-11-25 19:31:01 +00:00
392 lines
13 KiB
Lua
392 lines
13 KiB
Lua
-- ====================================================================================
|
|
-- DCSEX.MATH - MATH AND MATH-RELATED FUNCTIONS
|
|
-- ====================================================================================
|
|
-- (Constant) DCSEx.math.TWO_PI
|
|
-- DCSEx.math.addVec(vecA, vecB)
|
|
-- DCSEx.math.clamp(val, min, max)
|
|
-- DCSEx.math.getBearing(point, refPoint, returnAsNESWstring)
|
|
-- DCSEx.math.getDistance2D(vec2a, vec2b)
|
|
-- DCSEx.math.getDistance3D(vec3a, vec3b)
|
|
-- DCSEx.math.getLength3D(vec3)
|
|
-- DCSEx.math.getRelativeHeading(point, refObject, format)
|
|
-- 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 = {}
|
|
|
|
-------------------------------------
|
|
-- Two times Pi
|
|
-------------------------------------
|
|
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
|
|
-- @param returnAsNESWstring Should the value be returned as a N/S/E/W string instead of a numeric value
|
|
-- @return Bearing, as a number, in degrees
|
|
-------------------------------------
|
|
function DCSEx.math.getBearing(point, refPoint, returnAsNESWstring)
|
|
returnAsNESWstring = returnAsNESWstring or false
|
|
|
|
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
|
|
|
|
bearing = math.floor(bearing * (180 / math.pi))
|
|
|
|
if returnAsNESWstring then
|
|
bearing = math.floor((bearing / 45.0) + .5)
|
|
local namesArray = { "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest" }
|
|
return namesArray[(bearing % 8) + 1]
|
|
end
|
|
|
|
return bearing
|
|
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 length of a 3D vector
|
|
-------------------------------------
|
|
-- @param vec3 A 3D vector
|
|
-- @return Length of the vector
|
|
-------------------------------------
|
|
function DCSEx.math.getLength3D(vec3)
|
|
return math.sqrt(vec3.x ^ 2 + vec3.y ^ 2 + vec3.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 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
|