mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
1201 lines
34 KiB
Lua
1201 lines
34 KiB
Lua
--- **CORE** - Vector algebra.
|
|
--
|
|
-- **Main Features:**
|
|
--
|
|
-- * Easy vector algebra function
|
|
-- * Redefinition of `+`, `-`, `*`, `/`, `%` operators to be compatible with vectors
|
|
-- * Interface to DCS API functions
|
|
-- * Reduced confusion of DCS coordinate system
|
|
-- * Better performance than related classes (COORDINATE, POINT_VEC)
|
|
--
|
|
-- ===
|
|
--
|
|
-- ## 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 pointing North if > 0 and South if < 0.
|
|
-- @field #number y Component pointing up if >0 and down if < 0. This describes the altitude above main sea level.
|
|
-- @field #number z Component pointing East if > 0 and West if < 0.
|
|
|
|
--- *Mathematics knows no races or geographic boundaries; for mathematics, the cultural world is one country.* --David Hilbert
|
|
--
|
|
-- ===
|
|
--
|
|
-- # The VECTOR Concept
|
|
--
|
|
-- The VECTOR class has a great concept!
|
|
--
|
|
-- https://github.com/automattf/vector.lua/blob/master/vector.lua
|
|
--
|
|
-- # 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.
|
|
--
|
|
-- # Constructors
|
|
--
|
|
-- There are different ways to create a new instance of a VECTOR. All methods start with `New`.
|
|
--
|
|
-- ## From Components
|
|
--
|
|
-- ## From 2D or 3D Vectors
|
|
--
|
|
-- ## From Polar Coordinates
|
|
--
|
|
-- ## From Spherical Coordinates
|
|
--
|
|
--
|
|
-- # Operators
|
|
--
|
|
-- ## Addition [MATH] `+`
|
|
--
|
|
-- ## Subtraction [MATH] `-`
|
|
--
|
|
-- ## Multiplication [MATH] `*`
|
|
--
|
|
-- ## Devision [MATH] `/`
|
|
--
|
|
-- ## Modulo [MATH] `%`
|
|
--
|
|
--
|
|
-- # DCS API Interface
|
|
--
|
|
-- This class offers easy and convenient ways to access all vector related DCS API functions.
|
|
--
|
|
-- ## Land and Surface
|
|
--
|
|
-- ## Atmosphere
|
|
--
|
|
-- ## Effects and Actions
|
|
--
|
|
-- ## Map Markings
|
|
--
|
|
-- ## Coordinates
|
|
--
|
|
-- # Inferface to other MOOSE Classes
|
|
--
|
|
-- Of course, this class is interfaced with other MOOSE classes in the sense that you can obtain and work with VECTOR instances from MOOSE objects, from which a position or direction
|
|
-- vector can be derived.
|
|
--
|
|
-- ## GROUP
|
|
--
|
|
-- ## UNIT
|
|
--
|
|
-- ## STATIC
|
|
--
|
|
-- ## SCENERY
|
|
--
|
|
-- ## ZONE
|
|
--
|
|
-- # 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.2"
|
|
|
|
--- VECTOR private index.
|
|
-- @field #VECTOR __index
|
|
VECTOR.__index = VECTOR
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- ToDo list
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
-- TODO: 3D rotation
|
|
-- TODO: Markers
|
|
-- TODO: Documentation
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- Constructor
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Create a new VECTOR class instance from given cartesian coordinates `x`, `y` and `z`, where `z` is optional.
|
|
-- **Note** that whether the `z` component is passed or nil has great impact on the interpretation of the `y` component.
|
|
-- If `z` is not given (`nil`), then `y` is used as `z` (the coordinate pointing East) because we always constuct a 3D vector.
|
|
-- If `z` is given (not `nil`), then `y` is used as coordinate pointing up (out of plane) as for all 3D DCS vectors.
|
|
-- @param #VECTOR self
|
|
-- @param #number x Component of vector along x-axis (pointing North in both 2D and 3D).
|
|
-- @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)
|
|
|
|
if z==nil then
|
|
-- z-component is not given ==> 2D ==> we take y as z and set y=0.
|
|
self=setmetatable({x=x or 0, y=0, z=y or 0}, VECTOR)
|
|
else
|
|
self=setmetatable({x=x or 0, y=y or 0, z=z or 0}, VECTOR)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Create a new VECTOR class instance from given 2D or 3D vector object.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 Vec Vector object with `x`, `y` and optionally `z` components. Can be a DCS#Vec2, DCS#Vec3, `COORDINATE`, `VECTOR` object.
|
|
-- @return #VECTOR self
|
|
function VECTOR:NewFromVec(Vec)
|
|
|
|
-- The :New function takes care whether this is a 2D or 3D vector.
|
|
local vector=VECTOR:New(Vec.x, Vec.y, Vec.z)
|
|
|
|
return vector
|
|
end
|
|
|
|
--- Create a new VECTOR class instance from polar coordinates (r, phi).
|
|
-- @param #VECTOR self
|
|
-- @param #number r Distance.
|
|
-- @param #number phi Angle in Degrees. Note that 0° corresponds to North, 90° East etc.
|
|
-- @return #VECTOR self
|
|
function VECTOR:NewFromPolar(r, phi)
|
|
|
|
-- Convert deg to rad.
|
|
local Phi=math.rad(phi)
|
|
|
|
-- Polar coordinates. What we want in DCS is:
|
|
-- North: Phi=0 ==> x= 1, y= 0
|
|
-- East : Phi=90 ==> x= 0, y= 1
|
|
-- South: Phi=180 ==> x=-1, y= 0
|
|
-- West : Phi=270 ==> x= 0, y=-1
|
|
|
|
-- sin(0)=0 sin(90)=1, sin(180)=0, sin(270)=-1 ==> y
|
|
-- cos(0)=1 cos(90)=0, cos(180)=-1, cos(270)=0 ==> x
|
|
|
|
local x=r*math.cos(phi)
|
|
local y=r*math.sin(phi)
|
|
|
|
-- Create new vector. As z is nil, the 2D character is taken care of.
|
|
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 in meters with r>=0.
|
|
-- @param #number theta Polar angle in Degrees measured from a fixed polar axis or zenith direction. This angle is in [0°, 180°].
|
|
-- @param #number phi Azimuthal angle in Degrees. This angle is in [0°, 360°).
|
|
-- @return #VECTOR self
|
|
function VECTOR:NewFromSpherical(r, theta, phi)
|
|
|
|
local sinPhi=math.sin(math.rad(phi))
|
|
local cosPhi=math.cos(math.rad(phi))
|
|
local sinTheta=math.sin(math.rad(theta))
|
|
local cosTheta=math.cos(math.rad(theta))
|
|
|
|
--TODO: Check x,y,z convention for DCS.
|
|
local x=r*sinTheta*cosPhi
|
|
local y=r*sinTheta*sinPhi
|
|
local z=r*cosTheta
|
|
|
|
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
|
|
local z
|
|
|
|
if a.z and b.z then
|
|
-- Both given vectors are 3D
|
|
y=b.y-a.y
|
|
z=b.z-a.z
|
|
elseif b.z then
|
|
-- a is 2D and b is 3D
|
|
y=b.y-0
|
|
z=b.z-a.y
|
|
elseif a.z then
|
|
-- a is 3D and b is 2D
|
|
y=0-a.y
|
|
z=b.y-a.z
|
|
else
|
|
-- a is 2D and b is 2D
|
|
y=b.y-a.y
|
|
z=nil --We leave z=nil, so the New function takes care of the 2D character.
|
|
end
|
|
|
|
local c=VECTOR:New(x, y, z)
|
|
|
|
return c
|
|
end
|
|
|
|
|
|
--- Creates a new VECTOR instance from given the latitude and longitude in decimal degrees (DD).
|
|
-- @param #VECTOR self
|
|
-- @param #number Latitude Latitude in decimal degrees.
|
|
-- @param #number Longitude Longitude in decimal degrees.
|
|
-- @param #number altitude (Optional) Altitude in meters. Default is the land height at the 2D position.
|
|
-- @return #VECTOR self
|
|
function VECTOR:NewFromLLDD(Latitude, Longitude, Altitude)
|
|
|
|
-- Returns a point from latitude and longitude in the vec3 format.
|
|
local vec3=coord.LLtoLO(Latitude, Longitude)
|
|
|
|
-- Convert vec3 to coordinate object.
|
|
self=VECTOR:NewFromVec(vec3)
|
|
|
|
-- -- Adjust height
|
|
-- if Altitude==nil then
|
|
-- self.y=self:GetSurfaceHeight()
|
|
-- else
|
|
-- self.y=Altitude
|
|
-- end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Creates a new VECTOR instance from given latitude and longitude in degrees, minutes and seconds (DMS).
|
|
-- **Note** that latitude and longitude are passed as strings and the characters `°`, `'` and `"` are important.
|
|
-- @param #VECTOR self
|
|
-- @param #string Latitude Latitude in DMS as string, e.g. "`42° 24' 14.3"`".
|
|
-- @param #string Longitude Longitude in DMS as string, e.g. "`42° 24' 14.3"`".
|
|
-- @param #number Altitude (Optional) Altitude in meters. Default is the land height at the coordinate.
|
|
-- @return #VECTOR
|
|
function VECTOR:NewFromLLDMS(Latitude, Longitude, Altitude)
|
|
|
|
local lat=UTILS.LLDMSstringToDD(Latitude)
|
|
local lon=UTILS.LLDMSstringToDD(Longitude)
|
|
|
|
self=VECTOR:NewFromLLDD(lat, lon, Altitude)
|
|
|
|
return self
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- User Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Get 2D vector as simple table.
|
|
-- @param #VECTOR self
|
|
-- @return DCS#Vec2 2D array {x, y}.
|
|
function VECTOR:GetVec2()
|
|
|
|
local vec={x=self.x, y=self.z}
|
|
|
|
return vec
|
|
end
|
|
|
|
--- Get 3D vector as simple table.
|
|
-- @param #VECTOR self
|
|
-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position.
|
|
-- @return DCS#Vec3 3D array {x=x, y=y, z=z}.
|
|
function VECTOR:GetVec3(OnSurface)
|
|
|
|
local x=self.x
|
|
local y=OnSurface and land.getHeight({x=self.x, y=self.z}) or self.y
|
|
local z=self.z
|
|
|
|
local vec={x=x, y=y, z=z} --DCS#Vec3
|
|
|
|
return vec
|
|
end
|
|
|
|
--- Get a COORDINATE object.
|
|
-- @param #VECTOR self
|
|
-- @param #boolean OnSurface If `true`, the `y` component is set the land height [m] of the 2D position.
|
|
-- @return Core.Point#COORDINATE The COORDINATE object.
|
|
function VECTOR:GetCoordinate(OnSurface)
|
|
|
|
local vec3=self:GetVec3(OnSurface)
|
|
|
|
local coordinate=COORDINATE:NewFromVec3(vec3)
|
|
|
|
return coordinate
|
|
end
|
|
|
|
--- Get the distance from this vector to another vector.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vector Vector to which the distance is requested.
|
|
-- @param #boolean Only2D If `true`, calculate only the projected 2D distance.
|
|
-- @return #number Distance in meters.
|
|
function VECTOR:GetDistance(Vector, Only2D)
|
|
|
|
local dx=self.x-Vector.x
|
|
local dy=0
|
|
local dz=0
|
|
|
|
if Vector.z then
|
|
if not Only2D then
|
|
dy=self.y-Vector.y
|
|
end
|
|
dz=self.z-Vector.z
|
|
else
|
|
-- Given vector is 2D.
|
|
dy=0
|
|
dz=self.z-Vector.y
|
|
end
|
|
|
|
-- Calculate the distance.
|
|
local dist=math.sqrt( dx*dx + dy*dy + dz*dz )
|
|
|
|
return dist
|
|
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, where `z` is optional.
|
|
-- @return #VECTOR Directional vector from this vector to `a`.
|
|
function VECTOR:GetDirectionalVectorTo(a)
|
|
|
|
local x=a.x-self.x
|
|
local y=0
|
|
local z=nil
|
|
|
|
if a.z then
|
|
-- a is 3D
|
|
y=a.y-self.y
|
|
z=a.z-self.z
|
|
else
|
|
-- a is 2D
|
|
y=a.y-self.z
|
|
z=nil --We leave z=nil, so the New function takes care of the 2D character.
|
|
end
|
|
|
|
local c=VECTOR:New(x, y, z)
|
|
|
|
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
|
|
local z
|
|
|
|
if a.z then
|
|
-- a is 3D
|
|
y=self.y-a.y
|
|
z=self.z-a.z
|
|
else
|
|
-- a is 2D ==> we work in 2D and take z component of self
|
|
y=self.z-a.y
|
|
z=nil --We leave z=nil, so the New function takes care of the 2D character.
|
|
end
|
|
|
|
local c=VECTOR:New(x, y, z)
|
|
|
|
return c
|
|
end
|
|
|
|
--- Get length/norm/magnitude of this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #number Length of vector.
|
|
function VECTOR:GetLength()
|
|
|
|
local l=math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z)
|
|
|
|
return l
|
|
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)
|
|
|
|
-- Get heading.
|
|
local heading=math.atan2(self.z, self.x)
|
|
|
|
-- Convert to degrees.
|
|
heading=math.deg(heading)
|
|
|
|
|
|
if To360==nil or To360==true then
|
|
-- Adjust heading so it is in [0,360).
|
|
heading=UTILS.AdjustHeading360(heading)
|
|
else
|
|
-- Just make sure 360 is returned a 0.
|
|
if heading==360.0 then
|
|
heading=0.0
|
|
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)
|
|
|
|
-- Get directional vector from given vector to this vector.
|
|
local a=self:GetDirectionalVectorTo(Vector)
|
|
|
|
-- Get heading of directional vector.
|
|
local heading=math.deg(math.atan2(a.z, a.x))
|
|
|
|
-- Adjust heading so it is in [0,360).
|
|
heading=UTILS.AdjustHeading360(heading)
|
|
|
|
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)
|
|
|
|
-- Get directional vector from given vector to this vector.
|
|
local a=self:GetDirectionalVectorFrom(Vector)
|
|
|
|
-- Get heading of directional vector.
|
|
local heading=math.deg(math.atan2(a.z, a.x))
|
|
|
|
-- Adjust heading so it is in [0,360).
|
|
heading=UTILS.AdjustHeading360(heading)
|
|
|
|
return heading
|
|
end
|
|
|
|
|
|
--- Get latitude and longitude of this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #number Latitude in decimal degrees (DD).
|
|
-- @return #number Longitude in decimal degrees (DD).
|
|
function VECTOR:GetLatitudeLongitude()
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
local latitude, longitude, altitude=coord.LOtoLL(vec3)
|
|
|
|
return latitude, longitude
|
|
end
|
|
|
|
|
|
--- Get MGRS coordinates of this vector.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vector Vector from which the heading is requested.
|
|
-- @return #number Latitude
|
|
-- @return #number Longitude
|
|
function VECTOR:GetMGRS()
|
|
|
|
local lat, long=self:GetLatitudeLongitude()
|
|
|
|
local mrgs=coord.LLtoMGRS(lat, long)
|
|
|
|
return mrgs.Easing, mrgs.Northing
|
|
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
|
|
|
|
--- Return an intermediate VECTOR between this and another given vector.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vector The destination vector.
|
|
-- @param #number Fraction The fraction (0,1) where the new vector is created. Default 0.5, *i.e.* in the middle.
|
|
-- @return #VECTOR Vector between this and the other vector.
|
|
function VECTOR:GetIntermediateCoordinate(Vector, Fraction)
|
|
|
|
local f=Fraction or 0.5
|
|
|
|
-- Get the directional vector to the given Vector.
|
|
local vec=self:GetDirectionalVectorTo(Vector)
|
|
|
|
-- Get the length of the vector.
|
|
local length=vec:GetLength()
|
|
|
|
-- Set/scale the length.
|
|
vec:SetLength(f*length)
|
|
|
|
--TODO: Not sure what this was supposed to do?!
|
|
-- if f>1 then
|
|
-- local norm=UTILS.VecNorm(vec)
|
|
-- f=Fraction/norm
|
|
-- end
|
|
|
|
-- Get to the desired position.
|
|
vec=self+vec
|
|
|
|
return vec
|
|
end
|
|
|
|
|
|
|
|
--- Set length/norm/magnitude of this vector.
|
|
-- @param #VECTOR self
|
|
-- @param #number Length Desired length of vector.
|
|
function VECTOR:SetLength(Length)
|
|
|
|
-- Normalize this vector to a length of 1.
|
|
self:Normalize()
|
|
|
|
-- Scale the vector to the desired length.
|
|
local v=self*Length
|
|
|
|
-- Replace with scaled version.
|
|
self:Replace(v)
|
|
|
|
return self
|
|
end
|
|
|
|
--- Set x-component of vector. The x-axis points to the North.
|
|
-- @param #VECTOR self
|
|
-- @param #number x Value of x. Default 0.
|
|
-- @return #VECTOR self
|
|
function VECTOR:SetX(x)
|
|
self.x=x or 0
|
|
return self
|
|
end
|
|
|
|
--- Set y-component of vector. The y-axis points to the upwards and describes the altitude above mean sea level.
|
|
-- @param #VECTOR self
|
|
-- @param #number y Value of y. Default land/surface height at this point.
|
|
-- @return #VECTOR self
|
|
function VECTOR:SetY(y)
|
|
|
|
if y==nil then
|
|
y=self:GetSurfaceHeight()
|
|
end
|
|
self.y=y
|
|
return self
|
|
end
|
|
|
|
--- Set z-component of vector. The z-axis points to the East.
|
|
-- @param #VECTOR self
|
|
-- @param #number z Value of z. Default 0.
|
|
-- @param #VECTOR self
|
|
function VECTOR:SetZ(z)
|
|
self.z=z or 0
|
|
return self
|
|
end
|
|
|
|
|
|
|
|
--- Add a vector to this. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects.
|
|
-- Note that if you want to add a VECTOR, you can also simply use the `+` operator.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 Vec Vector to add. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object.
|
|
-- @return #VECTOR self
|
|
function VECTOR:AddVec(Vec)
|
|
|
|
self.x=self.x+Vec.x
|
|
|
|
if Vec.z then
|
|
self.y=self.y+Vec.y
|
|
self.z=self.z+Vec.z
|
|
else
|
|
-- Vec is 2D ==> we take its y-component.
|
|
self.z=self.z+Vec.y
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Substract a vector from this one. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects.
|
|
-- Note that if you want to add a VECTOR, you can also simply use the `-` operator.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 Vec Vector to substract. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object.
|
|
-- @return #VECTOR self
|
|
function VECTOR:SubVec(Vec)
|
|
|
|
self.x=self.x-Vec.x
|
|
|
|
if Vec.z then
|
|
self.y=self.y-Vec.y
|
|
self.z=self.z-Vec.z
|
|
else
|
|
-- Vec is 2D ==> we take its y-component.
|
|
self.z=self.z-Vec.y
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Calculate the dot product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object.
|
|
-- @return #number Dot product Sum_i(a[i]*b[i]). Note that this is a **scalar** and not a vector any more!
|
|
function VECTOR:Dot(Vec)
|
|
|
|
local dot=self.x*Vec.x
|
|
if Vec.z then
|
|
dot=dot+self.y*Vec.y+self.z*Vec.z
|
|
else
|
|
-- Vec is 2D ==> we take its y-component for z.
|
|
dot=dot+self.z*Vec.y
|
|
end
|
|
|
|
return dot
|
|
end
|
|
|
|
--- Calculate the rotation or cross product of this VECTOR with another vector. This function works for DCS#Vec2, DCS#Vec3, VECTOR, COORDINATE objects.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 Vec The other vector. Can also be a DCS#Vec2, DCS#Vec3, COORDINATE or VECTOR object.
|
|
-- @return #VECTOR The cross product vector.
|
|
function VECTOR:Rot(Vec)
|
|
|
|
-- TODO:
|
|
local dot=self.x*Vec.x
|
|
if Vec.z then
|
|
dot=dot+self.y*Vec.y+self.z*Vec.z
|
|
else
|
|
-- Vec is 2D ==> we take its y-component for z.
|
|
dot=dot+self.z*Vec.y
|
|
end
|
|
|
|
return dot
|
|
end
|
|
|
|
|
|
--- Get a clone (deep copy) of this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #VECTOR Copy of the vector.
|
|
function VECTOR:Copy()
|
|
|
|
local c=VECTOR:New(self.x, self.y, self.z)
|
|
|
|
return c
|
|
end
|
|
|
|
--- Replace this vector with another one.
|
|
-- If the given vector is 2D, we
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vector The vector that is used to replace this vector.
|
|
-- @param #boolean Project2D If `true` and the given vector is 2D, we project the updated vector to 2D (`y=0`). Otherwise, we leave `y` untouched.
|
|
-- @return #VECTOR self updated
|
|
function VECTOR:Replace(Vector, Project2D)
|
|
|
|
self.x=Vector.x
|
|
|
|
if Vector.z then
|
|
-- Given vector is 3D
|
|
self.y=Vector.y
|
|
self.z=Vector.z
|
|
else
|
|
-- Given vector is 2D
|
|
if Project2D then
|
|
self.y=0
|
|
end
|
|
self.z=Vector.y
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Normalize this vector, so that has a length of 1.
|
|
-- @param #VECTOR self
|
|
-- @return #VECTOR self
|
|
function VECTOR:Normalize()
|
|
|
|
local l=self:GetLength()
|
|
|
|
if l~=0 then
|
|
self:Replace(self/l)
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Translate the vector by a given distance and angle.
|
|
-- @param #VECTOR self
|
|
-- @param #number Distance Distance in meters. Default 1000 meters.
|
|
-- @param #number Heading Heading angle in degrees. Default 0° = North.
|
|
-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged.
|
|
-- @return #VECTOR The translated vector or a copy of it.
|
|
function VECTOR:Translate(Distance, Heading, Copy)
|
|
|
|
-- Set default distance if not passed.
|
|
Distance=Distance or 1000
|
|
|
|
-- Angle in rad.
|
|
local alpha = math.rad(Heading or 0)
|
|
|
|
-- Create a copy if requested.
|
|
local vector=Copy and self:Copy() or self
|
|
|
|
-- Set new coordinates.
|
|
vector.x = Distance * math.cos(alpha) + vector.x -- New x
|
|
vector.z = Distance * math.sin(alpha) + vector.z -- New z
|
|
|
|
return vector
|
|
end
|
|
|
|
--- Rotate the VECTOR clockwise in the 2D (x,z) plane.
|
|
-- @param #VECTOR self
|
|
-- @param #number Angle Rotation angle in degrees). Default 0.
|
|
-- @param #boolean Copy Create a copy of the VECTOR so the original stays unchanged.
|
|
-- @return #VECTOR The translated vector or a copy of it.
|
|
function VECTOR:Rotate2D(Angle, Copy)
|
|
|
|
-- Angle in rad.
|
|
local phi = -math.rad(Angle or 0)
|
|
|
|
-- Sin/Cos of angle.
|
|
local sinPhi = math.sin(phi)
|
|
local cosPhi = math.cos(phi)
|
|
|
|
-- Get more convenient notation.
|
|
local X=self.z
|
|
local Y=self.x
|
|
|
|
-- Apply rotation matrix.
|
|
local z = X*cosPhi - Y*sinPhi
|
|
local x = X*sinPhi + Y*cosPhi
|
|
|
|
-- Create new vector.
|
|
-- TODO: Copy argument
|
|
local vector=VECTOR:New(x, self.y, z)
|
|
|
|
return vector
|
|
end
|
|
|
|
|
|
|
|
--- Provides an MGRS string.
|
|
-- @param #VECTOR self
|
|
-- @param Core.Settings#SETTINGS Settings (Optional) The settings. Can be nil, and in this case the default settings are used. If you want to specify your own settings, use the _SETTINGS object.
|
|
-- @return #string The MGRS text.
|
|
function VECTOR:ToStringMGRS(Settings)
|
|
|
|
-- Get Accuracy.
|
|
local MGRS_Accuracy = Settings and Settings.MGRS_Accuracy or _SETTINGS.MGRS_Accuracy
|
|
|
|
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
|
|
|
local MGRS = coord.LLtoMGRS( lat, lon )
|
|
|
|
local text="MGRS " .. UTILS.tostringMGRS( MGRS, MGRS_Accuracy )
|
|
|
|
return text
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-- DCS API Wrapper Functions
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
|
|
--- Get the surface type at the vector.
|
|
--
|
|
-- * LAND = 1
|
|
-- * SHALLOW_WATER = 2
|
|
-- * WATER = 3
|
|
-- * ROAD = 4
|
|
-- * RUNWAY = 5
|
|
--
|
|
-- @param #VECTOR self
|
|
-- @return #number Surface Type
|
|
function VECTOR:GetSurfaceType()
|
|
|
|
local vec2=self:GetVec2()
|
|
|
|
local s=land.getSurfaceType(vec2)
|
|
|
|
return s
|
|
end
|
|
|
|
--- Check if a given vector has line of sight with this vector.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vec The other vector.
|
|
-- @return #number Surface Type
|
|
function VECTOR:IsVisible(Vec)
|
|
|
|
local vec1=self:GetVec3()
|
|
|
|
local vec2={x=Vec.x, Vec.y, Vec.z}
|
|
|
|
local los=land.isVisible(vec1, vec2)
|
|
|
|
return los
|
|
end
|
|
|
|
--- Get a vector on the closest road.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vec The other vector.
|
|
-- @return #VECTOR Closest vector on a road.
|
|
function VECTOR:GetClosestRoad()
|
|
|
|
local vec2=self:GetVec2()
|
|
|
|
local x,y=land.getClosestPointOnRoads('roads', vec2.x, vec2.y)
|
|
|
|
local road=nil
|
|
if x and y then
|
|
road=VECTOR:New(x, y)
|
|
end
|
|
|
|
return road
|
|
end
|
|
|
|
|
|
--- Get the path on road from this vector to a given other vector.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vec The destination vector.
|
|
-- @return Core.Path#PATHLINE Pathline with points on road.
|
|
function VECTOR:GetPathOnRoad(Vec)
|
|
|
|
local vec2=self:GetVec2()
|
|
|
|
local path=nil
|
|
|
|
local vec2points=land.findPathOnRoads("roads", vec2.x , vec2.y, vec2.x, vec2.y)
|
|
|
|
if vec2points then
|
|
path=PATHLINE:NewFromVec2Array("Road", vec2points)
|
|
end
|
|
|
|
return path
|
|
end
|
|
|
|
|
|
--- Get profile of the land between the two passed points.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vec3 The 3D destination vector. If a 2D vector is passed, `y` is set to the land height.
|
|
-- @return Core.Path#PATHLINE Pathline with points of the profile.
|
|
function VECTOR:GetProfile(Vec3)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
-- Get profile
|
|
local vec3s=land.profile(vec3, Vec3)
|
|
|
|
local profile=nil
|
|
if vec3s then
|
|
|
|
profile=PATHLINE:NewFromVec3Array("Profile", vec3s)
|
|
|
|
end
|
|
|
|
return profile
|
|
end
|
|
|
|
--- Returns an intercept point at which a ray drawn from the this vector in the passed normalized direction for a specified distance.
|
|
-- @param #VECTOR self
|
|
-- @param DCS#Vec3 DirectionVector Directional vector.
|
|
-- @param #number Distance Distance in meters. Default 1000 m.
|
|
-- @return #VECTOR Intercept vector. Can be `nil` if no intercept point is found.
|
|
function VECTOR:GetInterceptPoint(DirectionVector, Distance)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
local ip3=land.getIP(vec3, DirectionVector, Distance or 1000) --DCS#Vec3
|
|
|
|
local ipvector=nil
|
|
|
|
if ip3 then
|
|
ipvector=VECTOR:New(ip3.x, ip3.y , ip3.z)
|
|
end
|
|
|
|
return ipvector
|
|
end
|
|
|
|
--- Returns the distance from sea level at this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #number Distance above sea leavel in meters.
|
|
function VECTOR:GetSurfaceHeight()
|
|
|
|
local vec2=self:GetVec2()
|
|
|
|
local h=land.getHeight(vec2)
|
|
|
|
return h
|
|
end
|
|
|
|
|
|
--- Returns the distance from sea level at this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #number Heigh above sea leavel in meters.
|
|
-- @return #number Depth (positive) at this point in meters.
|
|
function VECTOR:GetSurfaceHeightAndDepth()
|
|
|
|
local vec2=self:GetVec2()
|
|
|
|
local h,d=land.getSurfaceHeightWithSeabed(vec2)
|
|
|
|
return h,d
|
|
end
|
|
|
|
--- Returns a velocity vector of the wind at this vector. Turbolences can be optionally be included.
|
|
-- @param #VECTOR self
|
|
-- @param #boolean WithTurbulence If `true`, return wind including turbulence.
|
|
-- @return #VECTOR Velocity 3D vector [m/s] the wind is blowing to.
|
|
function VECTOR:GetWindVector(WithTurbulence)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
local wind=nil
|
|
if WithTurbulence then
|
|
wind=atmosphere.getWindWithTurbulence(vec3)
|
|
|
|
else
|
|
wind=atmosphere.getWind(vec3)
|
|
end
|
|
|
|
local vector=VECTOR:New(wind)
|
|
|
|
return vector
|
|
end
|
|
|
|
--- Returns a temperature and pressure at this vector.
|
|
-- @param #VECTOR self
|
|
-- @return #number Temperatur in Kelvin.
|
|
-- @return #number Pressure in Pascals.
|
|
function VECTOR:GetTemperaturAndPressure()
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
local t,p=atmosphere.getTemperatureAndPressure(vec3)
|
|
|
|
return t,p
|
|
end
|
|
|
|
|
|
--- Creates a large smoke and fire effect of a specified type and density at this vector.
|
|
-- @param #VECTOR self
|
|
-- @param #number Preset Preset of smoke. Default `BIGSMOKEPRESET.LargeSmokeAndFire`.
|
|
-- @param #number Density Density between [0,1]. Default 0.5.
|
|
-- @return #string Name of the smoke. Can be used to stop it.
|
|
function VECTOR:SmokeAndFire(Preset, Density)
|
|
|
|
Preset=Preset or BIGSMOKEPRESET.LargeSmokeAndFire
|
|
Density=Density or 0.5
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
--TODO: Get a unique name or pass name as parameter?
|
|
|
|
trigger.action.effectSmokeBig(vec3, Preset, Density, Name)
|
|
|
|
return Name
|
|
end
|
|
|
|
--- Creates an illumination bomb at the specified point.
|
|
-- @param #VECTOR self
|
|
-- @param #number Power The power in Candela (cd). Should be between 1 and 1000000. Default 1000 cd.
|
|
-- @return #VECTOR self
|
|
function VECTOR:IlluminationBomb(Power)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
trigger.action.illuminationBomb(vec3, Power or 1000)
|
|
|
|
end
|
|
|
|
--- Creates an explosion at a given point at the specified power.
|
|
-- @param #VECTOR self
|
|
-- @param #number Power The power in kg TNT. Default 100 kg.
|
|
-- @return #VECTOR self
|
|
function VECTOR:Explosion(Power)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
trigger.action.explosion(vec3, Power or 100)
|
|
|
|
return self
|
|
end
|
|
|
|
--- Creates a signal flare at the given point in the specified color. The flare will be launched in the direction of the azimuth angle.
|
|
-- @param #VECTOR self
|
|
-- @param #number Color Color of flare. Default Green.
|
|
-- @param #number Azimuth Azimuth angle in degrees. Default 0.
|
|
-- @return #VECTOR self
|
|
function VECTOR:Flare(Color, Azimuth)
|
|
|
|
local vec3=self:GetVec3()
|
|
|
|
trigger.action.signalFlare(vec3, Color or 0, math.rad(Azimuth or 0))
|
|
|
|
return self
|
|
end
|
|
|
|
|
|
--- Creates a arrow from this VECTOR to another vector on the F10 map.
|
|
-- @param #VECTOR self
|
|
-- @param #VECTOR Vector The vector defining the endpoint.
|
|
-- @return #VECTOR self
|
|
function VECTOR:ArrowToAll(Vector)
|
|
|
|
local vec3Start=self:GetVec3()
|
|
|
|
trigger.action.arrowToAll(coalition , id, vec3Start, vec3End, color, fillColor , lineType, readOnly, "")
|
|
|
|
return self
|
|
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.
|
|
-- @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, a.z+b.z)
|
|
|
|
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), "ERROR in VECTOR.__sub: wrong argument types: (expected <vector> and <vector>)")
|
|
|
|
local c=VECTOR:New(a.x-b.x, a.y-b.y, a.z-b.z)
|
|
|
|
return c
|
|
end
|
|
|
|
|
|
--- Meta function to multiplicate vector by another vector or a scalar.
|
|
-- @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
|
|
c=VECTOR:New(a*b.x, a*b.y, a*b.z)
|
|
elseif type(b)=='number' then
|
|
c=VECTOR:New(b*a.x, b*a.y, b*a.z)
|
|
else
|
|
c=VECTOR:New(a.x*b.x, a.y*b.y, a.z*b.z)
|
|
end
|
|
|
|
return c
|
|
end
|
|
|
|
--- Meta function for dividing vectors by scalars.
|
|
-- @param #VECTOR a Vector a.
|
|
-- @param #number b Number by which the components of the vector are divided.
|
|
-- @return #VECTOR Returns a new VECTOR c with c[i]=a[i]/b for i=x,y,z.
|
|
function VECTOR.__div(a, b)
|
|
|
|
assert(VECTOR._IsVector(a) and type(b) == "number", "div: wrong argument types (expected <vector> and <number>)")
|
|
|
|
env.info("FF __div")
|
|
|
|
local c=VECTOR:New(a.x/b, a.y/b, a.z/b)
|
|
|
|
return c
|
|
end
|
|
|
|
--- Meta function to make vectors negative.
|
|
-- @param #VECTOR v Vector v.
|
|
function VECTOR.__unm(v)
|
|
local c=VECTOR:New(-v.x, -v.y, -v.z)
|
|
return c
|
|
end
|
|
|
|
--- Meta function to check if two vectors are equal.
|
|
-- @param #VECTOR a Vector a.
|
|
-- @param #VECTOR b Vector b.
|
|
-- @return #boolean If `true`, both vectors are equal
|
|
function VECTOR.__eq(a, b)
|
|
--assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__eq: wrong argument types: (expected <vector> and <vector>)")
|
|
env.info("FF __eq",showMessageBox)
|
|
BASE:I(a)
|
|
BASE:I(b)
|
|
return a.x==b.x and a.y==b.y and a.z==b.z
|
|
end
|
|
|
|
|
|
--- Meta function to change how vectors appear as string.
|
|
-- @param #VECTOR self
|
|
-- @return #string String representation of vector.
|
|
function VECTOR:__tostring()
|
|
local text=string.format("(x=%.1f, y=%.1f, z=%.1f) |v|=%.1f Phi=%4.1f°", self.x, self.y, self.z, self:GetLength(), self:GetHeading(false))
|
|
return text
|
|
end
|
|
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|