mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
VECTOR
**VECTOR** - New core class for vector algebra
This commit is contained in:
parent
b11df6b523
commit
9c7b5e8506
447
Moose Development/Moose/Core/Vector.lua
Normal file
447
Moose Development/Moose/Core/Vector.lua
Normal file
@ -0,0 +1,447 @@
|
||||
--- **CORE** - Vector algebra.
|
||||
--
|
||||
-- **Main Features:**
|
||||
--
|
||||
-- * Easy vector algebra function
|
||||
-- * Redefinition of +,-,* operators
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ## Example Missions:
|
||||
--
|
||||
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Core%20-%20Vector).
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **funkyfranky**
|
||||
--
|
||||
-- ===
|
||||
-- @module Core.Vector
|
||||
-- @image CORE_Vector.png
|
||||
|
||||
|
||||
--- VECTOR class.
|
||||
-- @type VECTOR
|
||||
-- @field #string ClassName Name of the class.
|
||||
-- @field #number verbose Verbosity of output.
|
||||
-- @field #number x Component.
|
||||
-- @field #number y Component.
|
||||
-- @field #number z Component.
|
||||
|
||||
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- # The VECTOR Concept
|
||||
--
|
||||
-- The VECTOR class has a great concept!
|
||||
--
|
||||
-- # The DCS Coordinate System
|
||||
--
|
||||
-- DCS has a rather unconventional way to define the coordinate system. The definition even depends whether you work with a 2D vector or a 3D vector.
|
||||
-- The good think is, that you usually do not need to worry about this unless you directly call the DCS API functions. Plus, this class tries to
|
||||
-- hide the differences between 2D and 3D conventions as good as possible with internal if-cases.
|
||||
--
|
||||
-- Still, it is important to unterstand the differences. So let us explain:
|
||||
--
|
||||
-- Usually, we draw a coordinate system in 2D and label the horizonal axis (pointing right) as the x-axis. The vertical axis (poining up on a piece of paper)
|
||||
--
|
||||
-- ## 3D
|
||||
--
|
||||
-- The x-axis points North. The z-axis points East. The y-axis points upwards and defines the altitue with respect to the mean sea level.
|
||||
--
|
||||
-- ## 2D
|
||||
--
|
||||
-- The x-axis points North (just like in the 3D) case. The y-axis points East.
|
||||
--
|
||||
-- # Operators
|
||||
--
|
||||
-- ## Addition [MATH] `+`
|
||||
--
|
||||
-- ## Subtraction [MATH] `-`
|
||||
--
|
||||
-- ## Multiplication [MATH] `*`
|
||||
--
|
||||
-- ## Devision [MATH] `/`
|
||||
--
|
||||
--
|
||||
-- # Examples
|
||||
--
|
||||
-- A new `VECTOR` object can be created with the @{#VECTOR.New} function.
|
||||
-- Here we create two vectors, a and b, and add them to create a new vector c.
|
||||
--
|
||||
-- local a=VECTOR:New(1, 0)
|
||||
-- local b=VECTOR:New(0, 1)
|
||||
-- local c=a+b
|
||||
--
|
||||
-- This is how it works.
|
||||
--
|
||||
-- @field #VECTOR
|
||||
VECTOR = {
|
||||
ClassName = "VECTOR",
|
||||
verbose = 0,
|
||||
}
|
||||
|
||||
--- VECTOR class version.
|
||||
-- @field #string version
|
||||
VECTOR.version="0.0.1"
|
||||
|
||||
--- VECTOR private index.
|
||||
-- @field #VECTOR __index
|
||||
VECTOR.__index = VECTOR
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- ToDo list
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- TODO: A lot...
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Constructor
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Create a new VECTOR class instance from given cartesian coordinates `x`, `y`, `z` (`z` is optional).
|
||||
-- If only `x` and `y` are given (`z`=nil), then a 2D vector is constructed.
|
||||
-- If `z` is passed, then a 3D vector is constucted.
|
||||
-- @param #VECTOR self
|
||||
-- @param #number x Component of vector along x-axis (pointing North).
|
||||
-- @param #number y Component of vector along y-axis (pointing East in 2D and Up in 3D).
|
||||
-- @param #number z (Optional) Component of the z-axis (pointing East in 3D).
|
||||
-- @return #VECTOR self
|
||||
function VECTOR:New(x, y, z)
|
||||
|
||||
self=setmetatable({x=x or 0, y=y or 0, z=z}, VECTOR)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new VECTOR class instance from polar coordinates (r, phi).
|
||||
-- @param #VECTOR self
|
||||
-- @param #number r Distance.
|
||||
-- @param #number phi Angle
|
||||
-- @return #VECTOR self
|
||||
function VECTOR:NewFromPolar(r, phi)
|
||||
|
||||
--TODO: convert.
|
||||
local x=1
|
||||
local y=1
|
||||
|
||||
local v=VECTOR:New(x, y)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new VECTOR class instance from spherical coordinates (r, theta, phi).
|
||||
-- @param #VECTOR self
|
||||
-- @param #number r Distance
|
||||
-- @param #number theta Angle
|
||||
-- @param #number phi Angle
|
||||
-- @return #VECTOR self
|
||||
function VECTOR:NewFromSpherical(r, theta, phi)
|
||||
|
||||
--TODO: convert.
|
||||
local x=1
|
||||
local y=1
|
||||
local z=1
|
||||
|
||||
local v=VECTOR:New(x, y, z)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the directional vector that points from a given vector `a` to another given vector `b`.
|
||||
-- The vector is `c=-a+b=b-a`.
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR a Vector a. This can also be given as any table with x,y and z components (z optional).
|
||||
-- @param #VECTOR b Vector b. This can also be given as any table with x,y and z components (z optional).
|
||||
-- @return #VECTOR Directional vector from a to b.
|
||||
function VECTOR:NewDirectionalVector(a, b)
|
||||
|
||||
local x=b.x-a.x
|
||||
local y=b.y-a.y
|
||||
|
||||
local c=VECTOR:New(x, y)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- User Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Get 2D vector as simple table.
|
||||
-- @param #VECTOR self
|
||||
-- @return DCS#Vec2 2D array {x=x, y=y}
|
||||
function VECTOR:GetVec2()
|
||||
|
||||
local vec2=nil
|
||||
|
||||
if self.z then
|
||||
vec2={x=self.x, y=self.z}
|
||||
else
|
||||
vec2={x=self.x, y=self.y}
|
||||
end
|
||||
|
||||
return vec2
|
||||
end
|
||||
|
||||
--- Get 3D vector as simple table.
|
||||
-- If the original vector was 2D, we return the land height at the 2D position.
|
||||
-- @param #VECTOR self
|
||||
-- @return DCS#Vec3 2D array {x=x, y=y, z=z}
|
||||
function VECTOR:GetVec3()
|
||||
|
||||
local vec=nil --DCS#Vec3
|
||||
|
||||
if self.z then
|
||||
vec={x=self.x, y=self.z}
|
||||
else
|
||||
-- Originally, we had a 2D vector, so we get the land height for y.
|
||||
vec={x=self.x, y=land.getHeight({x=self.x, y=self.y}), z=self.y}
|
||||
end
|
||||
|
||||
return vec
|
||||
end
|
||||
|
||||
--- Get the directional vector that points from this VECTOR to another VECTOR `a`.
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR a Vector a. This can also be given as any table with x,y and z components (z optional).
|
||||
-- @return #VECTOR Directional vector from self to a.
|
||||
function VECTOR:GetDirectionalVectorTo(a)
|
||||
|
||||
local x=a.x-self.x
|
||||
local y=a.y-self.y
|
||||
|
||||
local c=VECTOR:New(x, y)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
--- Get the directional vector that points from another VECTOR `a` to this VECTOR.
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR a Vector a. This can also be given as any table with x,y and z components (z optional).
|
||||
-- @return #VECTOR Directional vector from self to a.
|
||||
function VECTOR:GetDirectionalVectorFrom(a)
|
||||
|
||||
local x=self.x-a.x
|
||||
local y=self.y-a.y
|
||||
|
||||
local c=VECTOR:New(x, y)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
--- Get heading of vector.
|
||||
-- Note that a heading of
|
||||
--
|
||||
-- * 000° = North
|
||||
-- * 090° = East
|
||||
-- * 180° = South
|
||||
-- * 270° = West.
|
||||
--
|
||||
-- @param #VECTOR self
|
||||
-- @param #boolean To360 If `true` or `nil`, adjust heading to [0,360) range. If `false`, headings not in this range can occur.
|
||||
-- @return #number Heading in degrees.
|
||||
function VECTOR:GetHeading(To360)
|
||||
|
||||
local heading=0
|
||||
|
||||
if self.z then
|
||||
heading=math.atan2(self.z, self.x)
|
||||
else
|
||||
heading=math.atan2(self.y, self.x)
|
||||
end
|
||||
|
||||
-- Convert to degrees.
|
||||
heading=math.deg(heading)
|
||||
|
||||
if heading==360.0 then
|
||||
heading=0.0
|
||||
end
|
||||
|
||||
if To360==nil or To360==true then
|
||||
if heading>=360 then
|
||||
heading=heading-360
|
||||
elseif heading<360 then
|
||||
heading=heading+360
|
||||
end
|
||||
end
|
||||
|
||||
return heading
|
||||
end
|
||||
|
||||
--- Get the heading from this vector to another given vector.
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR Vector Vector to which the heading is requested.
|
||||
-- @return #number Heading from this vector to the other vector in degrees.
|
||||
function VECTOR:GetHeadingTo(Vector)
|
||||
|
||||
local x=Vector.x-self.x
|
||||
|
||||
local y=0
|
||||
if Vector.z and self.z then
|
||||
y=Vector.z-self.z
|
||||
elseif Vector.z then
|
||||
y=Vector.z-self.y
|
||||
elseif self.z then
|
||||
y=Vector.y-self.z
|
||||
else
|
||||
y=Vector.y-self.y
|
||||
end
|
||||
|
||||
local heading=math.deg(math.atan2(y, x))
|
||||
|
||||
if heading<0 then
|
||||
heading=heading+360
|
||||
elseif heading>=360 then
|
||||
heading=heading-360
|
||||
end
|
||||
|
||||
return heading
|
||||
end
|
||||
|
||||
--- Get the heading from a given vector to this vector.
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR Vector Vector from which the heading is requested.
|
||||
-- @return #number Heading from the other vector to this vector in degrees.
|
||||
function VECTOR:GetHeadingFrom(Vector)
|
||||
|
||||
local x=self.x-Vector.x
|
||||
|
||||
local y=0
|
||||
if Vector.z and self.z then
|
||||
y=self.z-Vector.z
|
||||
elseif Vector.z then
|
||||
y=self.y-Vector.z
|
||||
elseif self.z then
|
||||
y=self.z-Vector.y-self.z
|
||||
else
|
||||
y=self.y-Vector.y
|
||||
end
|
||||
|
||||
local heading=math.deg(math.atan2(y, x))
|
||||
|
||||
if heading<0 then
|
||||
heading=heading+360
|
||||
elseif heading>=360 then
|
||||
heading=heading-360
|
||||
end
|
||||
|
||||
return heading
|
||||
end
|
||||
|
||||
--- Get the difference of the heading of this vector w.
|
||||
--
|
||||
-- **Example 1:**
|
||||
-- This vector has a heading of 90° (pointing East) and the other vector has a heading of 225° (pointing South-West),
|
||||
-- we would optain a delta of 225°-90°=135°.
|
||||
--
|
||||
-- **Example 2:**
|
||||
-- This vector has a heading of 180 (pointing South) and the other vector has a heading of 90° (pointing East),
|
||||
-- we would optain a delta of 90°-180°=-90°.
|
||||
--
|
||||
-- @param #VECTOR self
|
||||
-- @param #VECTOR Vector Vector to which the heading is requested.
|
||||
-- @return #number Heading from this vector to the other vector in degrees.
|
||||
function VECTOR:GetHeadingDelta(Vector)
|
||||
|
||||
local h1=self:GetHeading(false)
|
||||
|
||||
local h2=Vector:GetHeading(false)
|
||||
|
||||
local delta=h2-h1
|
||||
|
||||
return delta
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- Private Functions
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
--- Check if passed object is a Vector.
|
||||
-- @param #table t The object to be tested.
|
||||
-- @return #boolean Returns `true` if `t` is a #VECTOR and `false` otherwise.
|
||||
function VECTOR._IsVector(t)
|
||||
return getmetatable(t) == VECTOR
|
||||
end
|
||||
|
||||
--- Meta function to add vectors together.
|
||||
-- ex: (vector(5,6) + vector(6,5)) is the same as vector(11,11)
|
||||
-- @param #VECTOR a Vector a.
|
||||
-- @param #VECTOR b Vector b.
|
||||
-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]+b[i] for i=x,y,z.
|
||||
function VECTOR.__add(a,b)
|
||||
assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__add: wrong argument types! (expected <vector> and <vector>)")
|
||||
local c=VECTOR:New(a.x+b.x, a.y+b.y)
|
||||
return c
|
||||
end
|
||||
|
||||
--- Meta function to substract vectors.
|
||||
-- @param #VECTOR a Vector a.
|
||||
-- @param #VECTOR b Vector b.
|
||||
-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]-b[i] for i=x,y,z.
|
||||
function VECTOR.__sub(a,b)
|
||||
assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "sub: wrong argument types: (expected <vector> and <vector>)")
|
||||
local c=nil
|
||||
if a.z and b.z then
|
||||
c=VECTOR:New(a.x-b.x, a.y-b.y)
|
||||
else
|
||||
c=VECTOR:New(a.x-b.x, a.y-b.y)
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
|
||||
--- Meta function to multiplicate vectors.
|
||||
-- @param #VECTOR a Vector a. Can also be a #number.
|
||||
-- @param #VECTOR b Vector b. Can also be a #number.
|
||||
-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]*b[i] for i=x,y,z.
|
||||
function VECTOR.__mul(a, b)
|
||||
|
||||
local c=nil --#VECTOR
|
||||
|
||||
if type(a)=='number' then
|
||||
if b.z then
|
||||
c=VECTOR:New(a*b.x, a*b.y, a*b.z)
|
||||
else
|
||||
c=VECTOR:New(a*b.x, a*b.y)
|
||||
end
|
||||
elseif type(b)=='number' then
|
||||
if a.z then
|
||||
c=VECTOR:New(b*a.x, b*a.y, b*a.z)
|
||||
else
|
||||
c=VECTOR:New(b*a.x, b*a.y)
|
||||
end
|
||||
else
|
||||
if a.z and b.z then
|
||||
c=VECTOR:New(a.x*b.x, a.y*b.y, a.z*b.z)
|
||||
elseif a.z then
|
||||
c=VECTOR:New(a.x*b.x, a.y*b.y, a.z)
|
||||
elseif b.z then
|
||||
c=VECTOR:New(a.x*b.x, a.y*b.y, b.z)
|
||||
else
|
||||
c=VECTOR:New(a.x*b.x, a.y*b.y)
|
||||
end
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
--- Meta function to change how vectors appear as string.
|
||||
-- @param #VECTOR self
|
||||
-- @return #string String representation of vector.
|
||||
function VECTOR:__tostring()
|
||||
local text=""
|
||||
if self.z then
|
||||
text=string.format("(x=%.3f, y=%.3f, z=%.3f) Heading=%3.3f°", self.x, self.y, self.z, self:GetHeading(false))
|
||||
else
|
||||
text=string.format("(x=%.3f, y=%.3f) Heading=%3.3f°", self.x, self.y, self:GetHeading(false))
|
||||
end
|
||||
return text
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@ -35,6 +35,7 @@ __Moose.Include( 'Scripts/Moose/Core/Velocity.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Zone_Detection.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Zone.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Pathline.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Core/Vector.lua' )
|
||||
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
||||
__Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
|
||||
|
||||
@ -480,7 +480,7 @@ NAVPOINT.version="0.0.1"
|
||||
--- Create a new NAVPOINT class instance from a given COORDINATE.
|
||||
-- @param #NAVPOINT self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param Core.Point#COORDINATE Coordinate of the fix.
|
||||
-- @param Core.Point#COORDINATE Coordinate Coordinate of the point.
|
||||
-- @return #NAVPOINT self
|
||||
function NAVPOINT:NewFromCoordinate(Name, Coordinate)
|
||||
|
||||
@ -498,18 +498,24 @@ function NAVPOINT:NewFromCoordinate(Name, Coordinate)
|
||||
end
|
||||
|
||||
--- Create a new NAVPOINT class instance from a given NAVPOINT.
|
||||
-- You have to specify the distance and bearing from the new point to the given point. *E.g.*, for a distance of 5 NM and a bearing of 090° (West), the
|
||||
-- new nav point is created 5 NM East of the given nav point. The reason is that this corresponts to convention used in most maps.
|
||||
-- You can, however, use the `Reciprocal` switch to create the new point in the direction you specify.
|
||||
-- @param #NAVFIX self
|
||||
-- @param #string Name Name of the fix. Should be unique!
|
||||
-- @param Navigation.Point#NAVPOINT NavPoint The navigation fix.
|
||||
-- @param #number Distance Distance in nautical miles.
|
||||
-- @param #number Bearing Bearing from the given NavFix to the newly created one.
|
||||
-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the new navfix to the given one.
|
||||
-- @param Navigation.Point#NAVPOINT NavPoint The given/existing navigation point.
|
||||
-- @param #number Distance Distance from the given to the new point in nautical miles.
|
||||
-- @param #number Bearing Bearing [Deg] from the new point to the given one.
|
||||
-- @param #boolean Reciprocal If `true` the reciprocal `Bearing` is taken so it specifies the direction from the given point to the new one.
|
||||
-- @return #NAVFIX self
|
||||
function NAVPOINT:NewFromNavPoint(Name, NavPoint, Distance, Bearing, Reciprocal)
|
||||
|
||||
local Angle=Bearing
|
||||
if Reciprocal then
|
||||
Bearing=Bearing-180
|
||||
end
|
||||
|
||||
local coord=NavPoint.coordinate:Translate(UTILS.NMToMeters(Distance), Angle)
|
||||
-- Translate.
|
||||
local coord=NavPoint.coordinate:Translate(UTILS.NMToMeters(Distance), Bearing)
|
||||
|
||||
local self=NavPoint:NewFromCoordinate(Name, coord)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user