From c46061466cb9a58e1c8cd05b1b741fc4a658a8a3 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 24 Sep 2023 22:09:49 +0200 Subject: [PATCH] VECTOR - Added functions --- Moose Development/Moose/Core/Pathline.lua | 10 +- Moose Development/Moose/Core/Vector.lua | 950 ++++++++++++++++--- Moose Development/Moose/Core/Zone.lua | 14 + Moose Development/Moose/Navigation/Point.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 14 + Moose Development/Moose/Wrapper/Group.lua | 18 + 6 files changed, 853 insertions(+), 155 deletions(-) diff --git a/Moose Development/Moose/Core/Pathline.lua b/Moose Development/Moose/Core/Pathline.lua index bde36d467..1bab11e14 100644 --- a/Moose Development/Moose/Core/Pathline.lua +++ b/Moose Development/Moose/Core/Pathline.lua @@ -243,14 +243,14 @@ end --- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc. -- @param #PATHLINE self --- @return List of points. +-- @return #list List of points. function PATHLINE:GetPoints() return self.points end --- Get segments of pathline. -- @param #PATHLINE self --- @return List of points. +-- @return #list List of points. function PATHLINE:GetSetments() local segments={} @@ -267,7 +267,7 @@ end --- Get 3D points of pathline. -- @param #PATHLINE self --- @return List of DCS#Vec3 points. +-- @return #list List of DCS#Vec3 points. function PATHLINE:GetPoints3D() local vecs={} @@ -282,7 +282,7 @@ end --- Get 2D points of pathline. -- @param #PATHLINE self --- @return List of DCS#Vec2 points. +-- @return #list List of DCS#Vec2 points. function PATHLINE:GetPoints2D() local vecs={} @@ -297,7 +297,7 @@ end --- Get COORDINATES of pathline. Note that COORDINATE objects are created when calling this function. That does involve deep copy calls and can have an impact on performance if done too often. -- @param #PATHLINE self --- @return List of COORDINATES points. +-- @return #list List of COORDINATES points. function PATHLINE:GetCoordinats() local vecs={} diff --git a/Moose Development/Moose/Core/Vector.lua b/Moose Development/Moose/Core/Vector.lua index feec82179..d85d76448 100644 --- a/Moose Development/Moose/Core/Vector.lua +++ b/Moose Development/Moose/Core/Vector.lua @@ -3,7 +3,10 @@ -- **Main Features:** -- -- * Easy vector algebra function --- * Redefinition of +,-,* operators +-- * 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) -- -- === -- @@ -24,11 +27,11 @@ -- @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. +-- @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. ---- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson +--- *Mathematics knows no races or geographic boundaries; for mathematics, the cultural world is one country.* --David Hilbert -- -- === -- @@ -54,6 +57,19 @@ -- -- 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] `+` @@ -64,6 +80,37 @@ -- -- ## 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 -- @@ -84,7 +131,7 @@ VECTOR = { --- VECTOR class version. -- @field #string version -VECTOR.version="0.0.1" +VECTOR.version="0.0.2" --- VECTOR private index. -- @field #VECTOR __index @@ -100,32 +147,62 @@ VECTOR.__index = VECTOR -- 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. +--- 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). +-- @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) - self=setmetatable({x=x or 0, y=y or 0, z=z}, VECTOR) - + 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 +-- @param #number phi Angle in Degrees. Note that 0° corresponds to North, 90° East etc. -- @return #VECTOR self function VECTOR:NewFromPolar(r, phi) - --TODO: convert. - local x=1 - local y=1 + -- 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 @@ -133,16 +210,21 @@ 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 +-- @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) - --TODO: convert. - local x=1 - local y=1 - local z=1 + 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) @@ -152,15 +234,34 @@ 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). +-- @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 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) + local c=VECTOR:New(x, y, z) return c end @@ -171,48 +272,91 @@ end --- Get 2D vector as simple table. -- @param #VECTOR self --- @return DCS#Vec2 2D array {x=x, y=y} +-- @return DCS#Vec2 2D array {x, 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 + 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 (z optional). --- @return #VECTOR Directional vector from self to a. +-- @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=a.y-self.y + 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) + local c=VECTOR:New(x, y, z) return c end @@ -224,13 +368,33 @@ end function VECTOR:GetDirectionalVectorFrom(a) local x=self.x-a.x - local y=self.y-a.y + 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) + 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 @@ -245,27 +409,21 @@ end -- @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 + -- Get heading. + local heading=math.atan2(self.z, self.x) -- 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 + -- 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 @@ -277,26 +435,14 @@ end -- @return #number Heading from this vector to the other vector in degrees. function VECTOR:GetHeadingTo(Vector) - local x=Vector.x-self.x + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorTo(Vector) - 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 + -- Get heading of directional vector. + local heading=math.deg(math.atan2(a.z, a.x)) - local heading=math.deg(math.atan2(y, x)) - - if heading<0 then - heading=heading+360 - elseif heading>=360 then - heading=heading-360 - end + -- Adjust heading so it is in [0,360). + heading=UTILS.AdjustHeading360(heading) return heading end @@ -307,30 +453,47 @@ end -- @return #number Heading from the other vector to this vector in degrees. function VECTOR:GetHeadingFrom(Vector) - local x=self.x-Vector.x + -- Get directional vector from given vector to this vector. + local a=self:GetDirectionalVectorFrom(Vector) - 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 + -- 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:** @@ -355,6 +518,487 @@ function VECTOR:GetHeadingDelta(Vector) 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. +-- @param #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. +-- @param #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 + + +--- 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.getWind(vec3) + else + wind=atmosphere.getWindWithTurbulence(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 + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Private Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -367,13 +1011,15 @@ function VECTOR._IsVector(t) 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 and )") - local c=VECTOR:New(a.x+b.x, a.y+b.y) + + local c=VECTOR:New(a.x+b.x, a.y+b.y, a.z+b.z) + return c end @@ -382,18 +1028,16 @@ end -- @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 and )") - 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 + + assert(VECTOR._IsVector(a) and VECTOR._IsVector(b), "ERROR in VECTOR.__sub: wrong argument types: (expected and )") + + local c=VECTOR:New(a.x-b.x, a.y-b.y, a.z-b.z) + return c end ---- Meta function to multiplicate vectors. +--- 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. @@ -402,46 +1046,54 @@ 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 + c=VECTOR:New(a*b.x, a*b.y, a*b.z) 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 + c=VECTOR:New(b*a.x, b*a.y, b*a.z) 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 + 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 and )") + + 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 and )") + 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="" - 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 + 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 - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 12391027c..3bead7f37 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -296,6 +296,20 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1 return self.Coordinate end +--- Returns the @{Core.Vector#VECTOR} of the zone. +-- @param #ZONE_BASE self +-- @param DCS#Distance Height The height in meters to add to the land height where the center of the zone is located. +-- @return Core.Vector#VECTOR The vector of the zone. +function ZONE_BASE:GetVector( Height ) + self:F2(self.ZoneName) + + local Vec3 = self:GetVec3( Height ) + + local vector=VECTOR:NewFromVec(Vec3) + + return vector +end + --- Get 2D distance to a coordinate. -- @param #ZONE_BASE self -- @param Core.Point#COORDINATE Coordinate Reference coordinate. Can also be a DCS#Vec2 or DCS#Vec3 object. diff --git a/Moose Development/Moose/Navigation/Point.lua b/Moose Development/Moose/Navigation/Point.lua index aafde14a0..3aa86f901 100644 --- a/Moose Development/Moose/Navigation/Point.lua +++ b/Moose Development/Moose/Navigation/Point.lua @@ -484,7 +484,7 @@ NAVPOINT.version="0.0.1" -- @return #NAVPOINT self function NAVPOINT:NewFromCoordinate(Name, Coordinate) - -- Inherit everything from SCENERY class. + -- Inherit everything from BASE class. self=BASE:Inherit(self, BASE:New()) -- #NAVFIX self.coordinate=Coordinate diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 786e48c26..225b56b47 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3038,6 +3038,20 @@ function UTILS.BearingToCardinal(Heading) end end +--- Adjust given heading so that is is in [0, 360). +-- @param #number Heading The heading in degrees. +-- @return #number Adjust heading in [0,360). +function UTILS.AdjustHeading360(Heading) + + if Heading>=360 then + Heading=Heading-360 + elseif Heading<0 then + Heading=Heading+360 + end + + return Heading +end + --- Create a BRAA NATO call string BRAA between two GROUP objects -- @param Wrapper.Group#GROUP FromGrp GROUP object -- @param Wrapper.Group#GROUP ToGrp GROUP object diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index faeb810a5..07a32f676 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1074,6 +1074,24 @@ function GROUP:GetAverageVec3() return nil end +--- Returns the current VECTOR of the GROUP. +-- @param #GROUP self +-- @return Core.Vector#VECTOR Current VECTOR of the first Unit of the GROUP. Can return `nil` if no unit can be found. +function GROUP:GetVector() + + -- Get first unit. + local unit=self:GetUnit(1) + + if unit then + local vec3=unit:GetVec3() + local vector=VECTOR:New(vec3.x, vec3.y, vec3.z) + return vector + end + + self:E("ERROR: Cannot get VECTOR of group "..tostring(self.GroupName)) + return nil +end + --- Returns a POINT_VEC2 object indicating the point in 2D of the first UNIT of the GROUP within the mission. -- @param #GROUP self -- @return Core.Point#POINT_VEC2 The 2D point vector of the first DCS Unit of the GROUP.