From 28411d20937ffa8914e4d83e317f4ab3eef26fa4 Mon Sep 17 00:00:00 2001 From: Thomas <72444570+Applevangelist@users.noreply.github.com> Date: Sun, 21 Apr 2024 10:12:51 +0200 Subject: [PATCH] Revert "Adding SHAPES (#2110)" (#2112) This reverts commit 26deaca16632a2e16a854339f32170f0594f717d. --- Moose Development/Moose/Modules.lua | 9 - Moose Development/Moose/Shapes/Circle.lua | 259 ----------- Moose Development/Moose/Shapes/Cube.lua | 66 --- Moose Development/Moose/Shapes/Line.lua | 331 -------------- Moose Development/Moose/Shapes/Oval.lua | 213 --------- Moose Development/Moose/Shapes/Polygon.lua | 458 ------------------- Moose Development/Moose/Shapes/ShapeBase.lua | 216 --------- Moose Development/Moose/Shapes/Triangle.lua | 86 ---- Moose Development/Moose/Utilities/Utils.lua | 56 +-- 9 files changed, 9 insertions(+), 1685 deletions(-) delete mode 100644 Moose Development/Moose/Shapes/Circle.lua delete mode 100644 Moose Development/Moose/Shapes/Cube.lua delete mode 100644 Moose Development/Moose/Shapes/Line.lua delete mode 100644 Moose Development/Moose/Shapes/Oval.lua delete mode 100644 Moose Development/Moose/Shapes/Polygon.lua delete mode 100644 Moose Development/Moose/Shapes/ShapeBase.lua delete mode 100644 Moose Development/Moose/Shapes/Triangle.lua diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index c95366e45..c483b8d21 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -122,15 +122,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Route.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Account.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Actions/Act_Assist.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/ShapeBase.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Circle.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Cube.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Line.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Oval.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Polygon.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Triangle.lua' ) -__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Shapes/Arrow.lua' ) - __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/UserSound.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/SoundOutput.lua' ) __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Sound/Radio.lua' ) diff --git a/Moose Development/Moose/Shapes/Circle.lua b/Moose Development/Moose/Shapes/Circle.lua deleted file mode 100644 index 04c153d86..000000000 --- a/Moose Development/Moose/Shapes/Circle.lua +++ /dev/null @@ -1,259 +0,0 @@ --- --- --- ### Author: **nielsvaes/coconutcockpit** --- --- === --- @module Shapes.CIRCLE - ---- CIRCLE class. --- @type CIRCLE --- @field #string ClassName Name of the class. --- @field #number Radius Radius of the circle - ---- *It's NOT hip to be square* -- Someone, somewhere, probably --- --- === --- --- # CIRCLE --- CIRCLEs can be fetched from the drawings in the Mission Editor - --- This class has some of the standard CIRCLE functions you'd expect. One function of interest is CIRCLE:PointInSector() that you can use if a point is --- within a certain sector (pizza slice) of a circle. This can be useful for many things, including rudimentary, "radar-like" searches from a unit. - --- @field #CIRCLE - ---- CIRCLE class with properties and methods for handling circles. -CIRCLE = { - ClassName = "CIRCLE", - Radius = nil, -} ---- Finds a circle on the map by its name. The circle must have been added in the Mission Editor --- @param #string shape_name Name of the circle to find --- @return #CIRCLE The found circle, or nil if not found -function CIRCLE:FindOnMap(shape_name) - local self = BASE:Inherit(self, SHAPE_BASE:FindOnMap(shape_name)) - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.find(object["name"], shape_name, 1, true) then - if object["polygonMode"] == "circle" then - self.Radius = object["radius"] - end - end - end - end - - return self -end - ---- Finds a circle by its name in the database. --- @param #string shape_name Name of the circle to find --- @return #CIRCLE The found circle, or nil if not found -function CIRCLE:Find(shape_name) - return _DATABASE:FindShape(shape_name) -end - ---- Creates a new circle from a center point and a radius. --- @param #table vec2 The center point of the circle --- @param #number radius The radius of the circle --- @return #CIRCLE The new circle -function CIRCLE:New(vec2, radius) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - self.CenterVec2 = vec2 - self.Radius = radius - return self -end - ---- Gets the radius of the circle. --- @return #number The radius of the circle -function CIRCLE:GetRadius() - return self.Radius -end - ---- Checks if a point is contained within the circle. --- @param #table point The point to check --- @return #bool True if the point is contained, false otherwise -function CIRCLE:ContainsPoint(point) - if ((point.x - self.CenterVec2.x) ^ 2 + (point.y - self.CenterVec2.y) ^ 2) ^ 0.5 <= self.Radius then - return true - end - return false -end - ---- Checks if a point is contained within a sector of the circle. The start and end sector need to be clockwise --- @param #table point The point to check --- @param #table sector_start The start point of the sector --- @param #table sector_end The end point of the sector --- @param #table center The center point of the sector --- @param #number radius The radius of the sector --- @return #bool True if the point is contained, false otherwise -function CIRCLE:PointInSector(point, sector_start, sector_end, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - local function are_clockwise(v1, v2) - return -v1.x * v2.y + v1.y * v2.x > 0 - end - - local function is_in_radius(rp) - return rp.x * rp.x + rp.y * rp.y <= radius ^ 2 - end - - local rel_pt = { - x = point.x - center.x, - y = point.y - center.y - } - - local rel_sector_start = { - x = sector_start.x - center.x, - y = sector_start.y - center.y, - } - - local rel_sector_end = { - x = sector_end.x - center.x, - y = sector_end.y - center.y, - } - - return not are_clockwise(rel_sector_start, rel_pt) and - are_clockwise(rel_sector_end, rel_pt) and - is_in_radius(rel_pt, radius) -end - ---- Checks if a unit is contained within a sector of the circle. The start and end sector need to be clockwise --- @param #string unit_name The name of the unit to check --- @param #table sector_start The start point of the sector --- @param #table sector_end The end point of the sector --- @param #table center The center point of the sector --- @param #number radius The radius of the sector --- @return #bool True if the unit is contained, false otherwise -function CIRCLE:UnitInSector(unit_name, sector_start, sector_end, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - if self:PointInSector(UNIT:FindByName(unit_name):GetVec2(), sector_start, sector_end, center, radius) then - return true - end - return false -end - ---- Checks if any unit of a group is contained within a sector of the circle. The start and end sector need to be clockwise --- @param #string group_name The name of the group to check --- @param #table sector_start The start point of the sector --- @param #table sector_end The end point of the sector --- @param #table center The center point of the sector --- @param #number radius The radius of the sector --- @return #bool True if any unit of the group is contained, false otherwise -function CIRCLE:AnyOfGroupInSector(group_name, sector_start, sector_end, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - for _, unit in pairs(GROUP:FindByName(group_name):GetUnits()) do - if self:PointInSector(unit:GetVec2(), sector_start, sector_end, center, radius) then - return true - end - end - return false -end - ---- Checks if all units of a group are contained within a sector of the circle. The start and end sector need to be clockwise --- @param #string group_name The name of the group to check --- @param #table sector_start The start point of the sector --- @param #table sector_end The end point of the sector --- @param #table center The center point of the sector --- @param #number radius The radius of the sector --- @return #bool True if all units of the group are contained, false otherwise -function CIRCLE:AllOfGroupInSector(group_name, sector_start, sector_end, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - for _, unit in pairs(GROUP:FindByName(group_name):GetUnits()) do - if not self:PointInSector(unit:GetVec2(), sector_start, sector_end, center, radius) then - return false - end - end - return true -end - ---- Checks if a unit is contained within a radius of the circle. --- @param #string unit_name The name of the unit to check --- @param #table center The center point of the radius --- @param #number radius The radius to check --- @return #bool True if the unit is contained, false otherwise -function CIRCLE:UnitInRadius(unit_name, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - if UTILS.IsInRadius(center, UNIT:FindByName(unit_name):GetVec2(), radius) then - return true - end - return false -end - ---- Checks if any unit of a group is contained within a radius of the circle. --- @param #string group_name The name of the group to check --- @param #table center The center point of the radius --- @param #number radius The radius to check --- @return #bool True if any unit of the group is contained, false otherwise -function CIRCLE:AnyOfGroupInRadius(group_name, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - for _, unit in pairs(GROUP:FindByName(group_name):GetUnits()) do - if UTILS.IsInRadius(center, unit:GetVec2(), radius) then - return true - end - end - return false -end - ---- Checks if all units of a group are contained within a radius of the circle. --- @param #string group_name The name of the group to check --- @param #table center The center point of the radius --- @param #number radius The radius to check --- @return #bool True if all units of the group are contained, false otherwise -function CIRCLE:AllOfGroupInRadius(group_name, center, radius) - center = center or self.CenterVec2 - radius = radius or self.Radius - - for _, unit in pairs(GROUP:FindByName(group_name):GetUnits()) do - if not UTILS.IsInRadius(center, unit:GetVec2(), radius) then - return false - end - end - return true -end - ---- Returns a random Vec2 within the circle. --- @return #table The random Vec2 -function CIRCLE:GetRandomVec2() - local angle = math.random() * 2 * math.pi - - local rx = math.random(0, self.Radius) * math.cos(angle) + self.CenterVec2.x - local ry = math.random(0, self.Radius) * math.sin(angle) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Returns a random Vec2 on the border of the circle. --- @return #table The random Vec2 -function CIRCLE:GetRandomVec2OnBorder() - local angle = math.random() * 2 * math.pi - - local rx = self.Radius * math.cos(angle) + self.CenterVec2.x - local ry = self.Radius * math.sin(angle) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Calculates the bounding box of the circle. The bounding box is the smallest rectangle that contains the circle. --- @return #table The bounding box of the circle -function CIRCLE:GetBoundingBox() - local min_x = self.CenterVec2.x - self.Radius - local min_y = self.CenterVec2.y - self.Radius - local max_x = self.CenterVec2.x + self.Radius - local max_y = self.CenterVec2.y + self.Radius - - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - diff --git a/Moose Development/Moose/Shapes/Cube.lua b/Moose Development/Moose/Shapes/Cube.lua deleted file mode 100644 index ae3f73090..000000000 --- a/Moose Development/Moose/Shapes/Cube.lua +++ /dev/null @@ -1,66 +0,0 @@ -CUBE = { - ClassName = "CUBE", - Points = {}, - Coords = {} -} - ---- Points need to be added in the following order: ---- p1 -> p4 make up the front face of the cube ---- p5 -> p8 make up the back face of the cube ---- p1 connects to p5 ---- p2 connects to p6 ---- p3 connects to p7 ---- p4 connects to p8 ---- ---- 8-----------7 ---- /| /| ---- / | / | ---- 4--+--------3 | ---- | | | | ---- | | | | ---- | | | | ---- | 5--------+--6 ---- | / | / ---- |/ |/ ---- 1-----------2 ---- -function CUBE:New(p1, p2, p3, p4, p5, p6, p7, p8) - local self = BASE:Inherit(self, SHAPE_BASE) - self.Points = {p1, p2, p3, p4, p5, p6, p7, p8} - for _, point in spairs(self.Points) do - table.insert(self.Coords, COORDINATE:NewFromVec3(point)) - end - return self -end - -function CUBE:GetCenter() - local center = { x=0, y=0, z=0 } - for _, point in pairs(self.Points) do - center.x = center.x + point.x - center.y = center.y + point.y - center.z = center.z + point.z - end - - center.x = center.x / 8 - center.y = center.y / 8 - center.z = center.z / 8 - return center -end - -function CUBE:ContainsPoint(point, cube_points) - cube_points = cube_points or self.Points - local min_x, min_y, min_z = math.huge, math.huge, math.huge - local max_x, max_y, max_z = -math.huge, -math.huge, -math.huge - - -- Find the minimum and maximum x, y, and z values of the cube points - for _, p in ipairs(cube_points) do - if p.x < min_x then min_x = p.x end - if p.y < min_y then min_y = p.y end - if p.z < min_z then min_z = p.z end - if p.x > max_x then max_x = p.x end - if p.y > max_y then max_y = p.y end - if p.z > max_z then max_z = p.z end - end - - return point.x >= min_x and point.x <= max_x and point.y >= min_y and point.y <= max_y and point.z >= min_z and point.z <= max_z -end diff --git a/Moose Development/Moose/Shapes/Line.lua b/Moose Development/Moose/Shapes/Line.lua deleted file mode 100644 index 08f7c84a0..000000000 --- a/Moose Development/Moose/Shapes/Line.lua +++ /dev/null @@ -1,331 +0,0 @@ --- --- --- ### Author: **nielsvaes/coconutcockpit** --- --- === --- @module Shapes.LINE - ---- OVAL class. --- @type OVAL --- @field #string ClassName Name of the class. --- @field #number Points points of the line --- @field #number Coords coordinates of the line - --- --- === - --- @field #LINE -LINE = { - ClassName = "LINE", - Points = {}, - Coords = {}, -} - ---- Finds a line on the map by its name. The line must be drawn in the Mission Editor --- @param #string line_name Name of the line to find --- @return #LINE The found line, or nil if not found -function LINE:FindOnMap(line_name) - local self = BASE:Inherit(self, SHAPE_BASE:FindOnMap(line_name)) - - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if object["name"] == line_name then - if object["primitiveType"] == "Line" then - for _, point in UTILS.spairs(object["points"]) do - local p = {x = object["mapX"] + point["x"], - y = object["mapY"] + point["y"] } - local coord = COORDINATE:NewFromVec2(p) - table.insert(self.Points, p) - table.insert(self.Coords, coord) - end - end - end - end - end - - self:I(#self.Points) - if #self.Points == 0 then - return nil - end - - self.MarkIDs = {} - - return self -end - ---- Finds a line by its name in the database. --- @param #string shape_name Name of the line to find --- @return #LINE The found line, or nil if not found -function LINE:Find(shape_name) - return _DATABASE:FindShape(shape_name) -end - ---- Creates a new line from two points. --- @param #table vec2 The first point of the line --- @param #number radius The second point of the line --- @return #LINE The new line -function LINE:New(...) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - self.Points = {...} - self:I(self.Points) - for _, point in UTILS.spairs(self.Points) do - table.insert(self.Coords, COORDINATE:NewFromVec2(point)) - end - return self -end - ---- Creates a new line from a circle. --- @param #table center_point center point of the circle --- @param #number radius radius of the circle, half length of the line --- @param #number angle_degrees degrees the line will form from center point --- @return #LINE The new line -function LINE:NewFromCircle(center_point, radius, angle_degrees) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - self.CenterVec2 = center_point - local angleRadians = math.rad(angle_degrees) - - local point1 = { - x = center_point.x + radius * math.cos(angleRadians), - y = center_point.y + radius * math.sin(angleRadians) - } - - local point2 = { - x = center_point.x + radius * math.cos(angleRadians + math.pi), - y = center_point.y + radius * math.sin(angleRadians + math.pi) - } - - for _, point in pairs{point1, point2} do - table.insert(self.Points, point) - table.insert(self.Coords, COORDINATE:NewFromVec2(point)) - end - - return self -end - ---- Gets the coordinates of the line. --- @return #table The coordinates of the line -function LINE:Coordinates() - return self.Coords -end - ---- Gets the start coordinate of the line. The start coordinate is the first point of the line. --- @return #COORDINATE The start coordinate of the line -function LINE:GetStartCoordinate() - return self.Coords[1] -end - ---- Gets the end coordinate of the line. The end coordinate is the last point of the line. --- @return #COORDINATE The end coordinate of the line -function LINE:GetEndCoordinate() - return self.Coords[#self.Coords] -end - ---- Gets the start point of the line. The start point is the first point of the line. --- @return #table The start point of the line -function LINE:GetStartPoint() - return self.Points[1] -end - ---- Gets the end point of the line. The end point is the last point of the line. --- @return #table The end point of the line -function LINE:GetEndPoint() - return self.Points[#self.Points] -end - ---- Gets the length of the line. --- @return #number The length of the line -function LINE:GetLength() - local total_length = 0 - for i=1, #self.Points - 1 do - local x1, y1 = self.Points[i]["x"], self.Points[i]["y"] - local x2, y2 = self.Points[i+1]["x"], self.Points[i+1]["y"] - local segment_length = math.sqrt((x2 - x1)^2 + (y2 - y1)^2) - total_length = total_length + segment_length - end - return total_length -end - ---- Returns a random point on the line. --- @param #table points (optional) The points of the line or 2 other points if you're just using the LINE class without an object of it --- @return #table The random point -function LINE:GetRandomPoint(points) - points = points or self.Points - local rand = math.random() -- 0->1 - - local random_x = points[1].x + rand * (points[2].x - points[1].x) - local random_y = points[1].y + rand * (points[2].y - points[1].y) - - return { x= random_x, y= random_y } -end - ---- Gets the heading of the line. --- @param #table points (optional) The points of the line or 2 other points if you're just using the LINE class without an object of it --- @return #number The heading of the line -function LINE:GetHeading(points) - points = points or self.Points - - local angle = math.atan2(points[2].y - points[1].y, points[2].x - points[1].x) - - angle = math.deg(angle) - if angle < 0 then - angle = angle + 360 - end - - return angle -end - - ---- Return each part of the line as a new line --- @return #table The points -function LINE:GetIndividualParts() - local parts = {} - if #self.Points == 2 then - parts = {self} - end - - for i=1, #self.Points -1 do - local p1 = self.Points[i] - local p2 = self.Points[i % #self.Points + 1] - table.add(parts, LINE:New(p1, p2)) - end - - return parts -end - ---- Gets a number of points in between the start and end points of the line. --- @param #number amount The number of points to get --- @param #table start_point (Optional) The start point of the line, defaults to the object's start point --- @param #table end_point (Optional) The end point of the line, defaults to the object's end point --- @return #table The points -function LINE:GetPointsInbetween(amount, start_point, end_point) - start_point = start_point or self:GetStartPoint() - end_point = end_point or self:GetEndPoint() - if amount == 0 then return {start_point, end_point} end - - amount = amount + 1 - local points = {} - - local difference = { x = end_point.x - start_point.x, y = end_point.y - start_point.y } - local divided = { x = difference.x / amount, y = difference.y / amount } - - for j=0, amount do - local part_pos = {x = divided.x * j, y = divided.y * j} - -- add part_pos vector to the start point so the new point is placed along in the line - local point = {x = start_point.x + part_pos.x, y = start_point.y + part_pos.y} - table.insert(points, point) - end - return points -end - ---- Gets a number of points in between the start and end points of the line. --- @param #number amount The number of points to get --- @param #table start_point (Optional) The start point of the line, defaults to the object's start point --- @param #table end_point (Optional) The end point of the line, defaults to the object's end point --- @return #table The points -function LINE:GetCoordinatesInBetween(amount, start_point, end_point) - local coords = {} - for _, pt in pairs(self:GetPointsInbetween(amount, start_point, end_point)) do - table.add(coords, COORDINATE:NewFromVec2(pt)) - end - return coords -end - - -function LINE:GetRandomPoint(start_point, end_point) - start_point = start_point or self:GetStartPoint() - end_point = end_point or self:GetEndPoint() - - local fraction = math.random() - - local difference = { x = end_point.x - start_point.x, y = end_point.y - start_point.y } - local part_pos = {x = difference.x * fraction, y = difference.y * fraction} - local random_point = { x = start_point.x + part_pos.x, y = start_point.y + part_pos.y} - - return random_point -end - - -function LINE:GetRandomCoordinate(start_point, end_point) - start_point = start_point or self:GetStartPoint() - end_point = end_point or self:GetEndPoint() - - return COORDINATE:NewFromVec2(self:GetRandomPoint(start_point, end_point)) -end - - ---- Gets a number of points on a sine wave between the start and end points of the line. --- @param #number amount The number of points to get --- @param #table start_point (Optional) The start point of the line, defaults to the object's start point --- @param #table end_point (Optional) The end point of the line, defaults to the object's end point --- @param #number frequency (Optional) The frequency of the sine wave, default 1 --- @param #number phase (Optional) The phase of the sine wave, default 0 --- @param #number amplitude (Optional) The amplitude of the sine wave, default 100 --- @return #table The points -function LINE:GetPointsBetweenAsSineWave(amount, start_point, end_point, frequency, phase, amplitude) - amount = amount or 20 - start_point = start_point or self:GetStartPoint() - end_point = end_point or self:GetEndPoint() - frequency = frequency or 1 -- number of cycles per unit of x - phase = phase or 0 -- offset in radians - amplitude = amplitude or 100 -- maximum height of the wave - - local points = {} - - -- Returns the y-coordinate of the sine wave at x - local function sine_wave(x) - return amplitude * math.sin(2 * math.pi * frequency * (x - start_point.x) + phase) - end - - -- Plot x-amount of points on the sine wave between point_01 and point_02 - local x = start_point.x - local step = (end_point.x - start_point.x) / 20 - for _=1, amount do - local y = sine_wave(x) - x = x + step - table.add(points, {x=x, y=y}) - end - return points -end - ---- Calculates the bounding box of the line. The bounding box is the smallest rectangle that contains the line. --- @return #table The bounding box of the line -function LINE:GetBoundingBox() - local min_x, min_y, max_x, max_y = self.Points[1].x, self.Points[1].y, self.Points[2].x, self.Points[2].y - - for i = 2, #self.Points do - local x, y = self.Points[i].x, self.Points[i].y - - if x < min_x then - min_x = x - end - if y < min_y then - min_y = y - end - if x > max_x then - max_x = x - end - if y > max_y then - max_y = y - end - end - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Draws the line on the map. --- @param #table points The points of the line -function LINE:Draw() - for i=1, #self.Coords -1 do - local c1 = self.Coords[i] - local c2 = self.Coords[i % #self.Coords + 1] - table.add(self.MarkIDs, c1:LineToAll(c2)) - end -end - ---- Removes the drawing of the line from the map. -function LINE:RemoveDraw() - for _, mark_id in pairs(self.MarkIDs) do - UTILS.RemoveMark(mark_id) - end -end diff --git a/Moose Development/Moose/Shapes/Oval.lua b/Moose Development/Moose/Shapes/Oval.lua deleted file mode 100644 index d2f85a822..000000000 --- a/Moose Development/Moose/Shapes/Oval.lua +++ /dev/null @@ -1,213 +0,0 @@ --- --- --- ### Author: **nielsvaes/coconutcockpit** --- --- === --- @module Shapes.OVAL - ---- OVAL class. --- @type OVAL --- @field #string ClassName Name of the class. --- @field #number MajorAxis The major axis (radius) of the oval --- @field #number MinorAxis The minor axis (radius) of the oval --- @field #number Angle The angle the oval is rotated on - ---- *The little man removed his hat, what an egg shaped head he had* -- Agatha Christie --- --- === --- --- # OVAL --- OVALs can be fetched from the drawings in the Mission Editor - --- The major and minor axes define how elongated the shape of an oval is. This class has some basic functions that the other SHAPE classes have as well. --- Since it's not possible to draw the shape of an oval while the mission is running, right now the draw function draws 2 cicles. One with the major axis and one with --- the minor axis. It then draws a diamond shape on an angle where the corners touch the major and minor axes to give an indication of what the oval actually --- looks like. - --- Using ovals can be handy to find an area on the ground that is actually an intersection of a cone and a plane. So imagine you're faking the view cone of --- a targeting pod and - --- @field #OVAL - ---- OVAL class with properties and methods for handling ovals. -OVAL = { - ClassName = "OVAL", - MajorAxis = nil, - MinorAxis = nil, - Angle = 0, - DrawPoly=nil -} - ---- Finds an oval on the map by its name. The oval must be drawn on the map. --- @param #string shape_name Name of the oval to find --- @return #OVAL The found oval, or nil if not found -function OVAL:FindOnMap(shape_name) - local self = BASE:Inherit(self, SHAPE_BASE:FindOnMap(shape_name)) - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.find(object["name"], shape_name, 1, true) then - if object["polygonMode"] == "oval" then - self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } - self.MajorAxis = object["r1"] - self.MinorAxis = object["r2"] - self.Angle = object["angle"] - end - end - end - end - - return self -end - ---- Finds an oval by its name in the database. --- @param #string shape_name Name of the oval to find --- @return #OVAL The found oval, or nil if not found -function OVAL:Find(shape_name) - return _DATABASE:FindShape(shape_name) -end - ---- Creates a new oval from a center point, major axis, minor axis, and angle. --- @param #table vec2 The center point of the oval --- @param #number major_axis The major axis of the oval --- @param #number minor_axis The minor axis of the oval --- @param #number angle The angle of the oval --- @return #OVAL The new oval -function OVAL:New(vec2, major_axis, minor_axis, angle) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - self.CenterVec2 = vec2 - self.MajorAxis = major_axis - self.MinorAxis = minor_axis - self.Angle = angle or 0 - - return self -end - ---- Gets the major axis of the oval. --- @return #number The major axis of the oval -function OVAL:GetMajorAxis() - return self.MajorAxis -end - ---- Gets the minor axis of the oval. --- @return #number The minor axis of the oval -function OVAL:GetMinorAxis() - return self.MinorAxis -end - ---- Gets the angle of the oval. --- @return #number The angle of the oval -function OVAL:GetAngle() - return self.Angle -end - ---- Sets the major axis of the oval. --- @param #number value The new major axis -function OVAL:SetMajorAxis(value) - self.MajorAxis = value -end - ---- Sets the minor axis of the oval. --- @param #number value The new minor axis -function OVAL:SetMinorAxis(value) - self.MinorAxis = value -end - ---- Sets the angle of the oval. --- @param #number value The new angle -function OVAL:SetAngle(value) - self.Angle = value -end - ---- Checks if a point is contained within the oval. --- @param #table point The point to check --- @return #bool True if the point is contained, false otherwise -function OVAL:ContainsPoint(point) - local cos, sin = math.cos, math.sin - local dx = point.x - self.CenterVec2.x - local dy = point.y - self.CenterVec2.y - local rx = dx * cos(self.Angle) + dy * sin(self.Angle) - local ry = -dx * sin(self.Angle) + dy * cos(self.Angle) - return rx * rx / (self.MajorAxis * self.MajorAxis) + ry * ry / (self.MinorAxis * self.MinorAxis) <= 1 -end - ---- Returns a random Vec2 within the oval. --- @return #table The random Vec2 -function OVAL:GetRandomVec2() - local theta = math.rad(self.Angle) - - local random_point = math.sqrt(math.random()) --> uniformly - --local random_point = math.random() --> more clumped around center - local phi = math.random() * 2 * math.pi - local x_c = random_point * math.cos(phi) - local y_c = random_point * math.sin(phi) - local x_e = x_c * self.MajorAxis - local y_e = y_c * self.MinorAxis - local rx = (x_e * math.cos(theta) - y_e * math.sin(theta)) + self.CenterVec2.x - local ry = (x_e * math.sin(theta) + y_e * math.cos(theta)) + self.CenterVec2.y - - return {x=rx, y=ry} -end - ---- Calculates the bounding box of the oval. The bounding box is the smallest rectangle that contains the oval. --- @return #table The bounding box of the oval -function OVAL:GetBoundingBox() - local min_x = self.CenterVec2.x - self.MajorAxis - local min_y = self.CenterVec2.y - self.MinorAxis - local max_x = self.CenterVec2.x + self.MajorAxis - local max_y = self.CenterVec2.y + self.MinorAxis - - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Draws the oval on the map, for debugging --- @param #number angle (Optional) The angle of the oval. If nil will use self.Angle -function OVAL:Draw() - --for pt in pairs(self:PointsOnEdge(20)) do - -- COORDINATE:NewFromVec2(pt) - --end - - self.DrawPoly = POLYGON:NewFromPoints(self:PointsOnEdge(20)) - self.DrawPoly:Draw(true) - - - - - ---- TODO: draw a better shape using line segments - --angle = angle or self.Angle - --local coor = self:GetCenterCoordinate() - -- - --table.add(self.MarkIDs, coor:CircleToAll(self.MajorAxis)) - --table.add(self.MarkIDs, coor:CircleToAll(self.MinorAxis)) - --table.add(self.MarkIDs, coor:LineToAll(coor:Translate(self.MajorAxis, self.Angle))) - -- - --local pt_1 = coor:Translate(self.MajorAxis, self.Angle) - --local pt_2 = coor:Translate(self.MinorAxis, self.Angle - 90) - --local pt_3 = coor:Translate(self.MajorAxis, self.Angle - 180) - --local pt_4 = coor:Translate(self.MinorAxis, self.Angle - 270) - --table.add(self.MarkIDs, pt_1:QuadToAll(pt_2, pt_3, pt_4), -1, {0, 1, 0}, 1, {0, 1, 0}) -end - ---- Removes the drawing of the oval from the map -function OVAL:RemoveDraw() - self.DrawPoly:RemoveDraw() -end - - -function OVAL:PointsOnEdge(num_points) - num_points = num_points or 20 - local points = {} - local dtheta = 2 * math.pi / num_points - - for i = 0, num_points - 1 do - local theta = i * dtheta - local x = self.CenterVec2.x + self.MajorAxis * math.cos(theta) * math.cos(self.Angle) - self.MinorAxis * math.sin(theta) * math.sin(self.Angle) - local y = self.CenterVec2.y + self.MajorAxis * math.cos(theta) * math.sin(self.Angle) + self.MinorAxis * math.sin(theta) * math.cos(self.Angle) - table.insert(points, {x = x, y = y}) - end - - return points -end - - diff --git a/Moose Development/Moose/Shapes/Polygon.lua b/Moose Development/Moose/Shapes/Polygon.lua deleted file mode 100644 index a40256ecf..000000000 --- a/Moose Development/Moose/Shapes/Polygon.lua +++ /dev/null @@ -1,458 +0,0 @@ --- --- --- ### Author: **nielsvaes/coconutcockpit** --- --- === --- @module Shapes.POLYGON - ---- POLYGON class. --- @type POLYGON --- @field #string ClassName Name of the class. --- @field #table Points List of 3D points defining the shape, this will be assigned automatically if you're passing in a drawing from the Mission Editor --- @field #table Coords List of COORDINATE defining the path, this will be assigned automatically if you're passing in a drawing from the Mission Editor --- @field #table MarkIDs List any MARKIDs this class use, this will be assigned automatically if you're passing in a drawing from the Mission Editor --- @field #table Triangles List of TRIANGLEs that make up the shape of the POLYGON after being triangulated --- @extends Core.Base#BASE - ---- *Polygons are fashionable at the moment* -- Trip Hawkins --- --- === --- --- # POLYGON --- POLYGONs can be fetched from the drawings in the Mission Editor if the drawing is: --- * A closed shape made with line segments --- * A closed shape made with a freehand line --- * A freehand drawn polygon --- * A rect --- Use the POLYGON:FindOnMap() of POLYGON:Find() functions for this. You can also create a non existing polygon in memory using the POLYGON:New() function. Pass in a --- any number of Vec2s into this function to define the shape of the polygon you want. - --- You can draw very intricate and complex polygons in the Mission Editor to avoid (or include) map objects. You can then generate random points within this complex --- shape for spawning groups or checking positions. - --- When a POLYGON is made, it's automatically triangulated. The resulting triangles are stored in POLYGON.Triangles. This also immeadiately saves the surface area --- of the POLYGON. Because the POLYGON is triangulated, it's possible to generate random points within this POLYGON without having to use a trial and error method to see if --- the point is contained within the shape. --- Using POLYGON:GetRandomVec2() will result in a truly, non-biased, random Vec2 within the shape. You'll want to use this function most. There's also POLYGON:GetRandomNonWeightedVec2 --- which ignores the size of the triangles in the polygon to pick a random points. This will result in more points clumping together in parts of the polygon where the triangles are --- the smallest. - - --- @field #POLYGON - -POLYGON = { - ClassName = "POLYGON", - Points = {}, - Coords = {}, - Triangles = {}, - SurfaceArea = 0, - TriangleMarkIDs = {}, - OutlineMarkIDs = {}, - Angle = nil, -- for arrows - Heading = nil -- for arrows -} - ---- Finds a polygon on the map by its name. The polygon must be added in the mission editor. --- @param #string shape_name Name of the polygon to find --- @return #POLYGON The found polygon, or nil if not found -function POLYGON:FindOnMap(shape_name) - local self = BASE:Inherit(self, SHAPE_BASE:FindOnMap(shape_name)) - - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if object["name"] == shape_name then - if (object["primitiveType"] == "Line" and object["closed"] == true) or (object["polygonMode"] == "free") then - for _, point in UTILS.spairs(object["points"]) do - local p = {x = object["mapX"] + point["x"], - y = object["mapY"] + point["y"] } - local coord = COORDINATE:NewFromVec2(p) - self.Points[#self.Points + 1] = p - self.Coords[#self.Coords + 1] = coord - end - elseif object["polygonMode"] == "rect" then - local angle = object["angle"] - local half_width = object["width"] / 2 - local half_height = object["height"] / 2 - - local p1 = UTILS.RotatePointAroundPivot({ x = self.CenterVec2.x - half_height, y = self.CenterVec2.y + half_width }, self.CenterVec2, angle) - local p2 = UTILS.RotatePointAroundPivot({ x = self.CenterVec2.x + half_height, y = self.CenterVec2.y + half_width }, self.CenterVec2, angle) - local p3 = UTILS.RotatePointAroundPivot({ x = self.CenterVec2.x + half_height, y = self.CenterVec2.y - half_width }, self.CenterVec2, angle) - local p4 = UTILS.RotatePointAroundPivot({ x = self.CenterVec2.x - half_height, y = self.CenterVec2.y - half_width }, self.CenterVec2, angle) - - self.Points = {p1, p2, p3, p4} - for _, point in pairs(self.Points) do - self.Coords[#self.Coords + 1] = COORDINATE:NewFromVec2(point) - end - elseif object["polygonMode"] == "arrow" then - for _, point in UTILS.spairs(object["points"]) do - local p = {x = object["mapX"] + point["x"], - y = object["mapY"] + point["y"] } - local coord = COORDINATE:NewFromVec2(p) - self.Points[#self.Points + 1] = p - self.Coords[#self.Coords + 1] = coord - end - self.Angle = object["angle"] - self.Heading = UTILS.ClampAngle(self.Angle + 90) - end - end - end - end - - if #self.Points == 0 then - return nil - end - - self.CenterVec2 = self:GetCentroid() - self.Triangles = self:Triangulate() - self.SurfaceArea = self:__CalculateSurfaceArea() - - self.TriangleMarkIDs = {} - self.OutlineMarkIDs = {} - return self -end - ---- Creates a polygon from a zone. The zone must be defined in the mission. --- @param #string zone_name Name of the zone --- @return #POLYGON The polygon created from the zone, or nil if the zone is not found -function POLYGON:FromZone(zone_name) - for _, zone in pairs(env.mission.triggers.zones) do - if zone["name"] == zone_name then - return POLYGON:New(unpack(zone["verticies"] or {})) - end - end -end - ---- Finds a polygon by its name in the database. --- @param #string shape_name Name of the polygon to find --- @return #POLYGON The found polygon, or nil if not found -function POLYGON:Find(shape_name) - return _DATABASE:FindShape(shape_name) -end - ---- Creates a new polygon from a list of points. Each point is a table with 'x' and 'y' fields. --- @param #table ... Points of the polygon --- @return #POLYGON The new polygon -function POLYGON:New(...) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - - self.Points = {...} - self.Coords = {} - for _, point in UTILS.spairs(self.Points) do - table.insert(self.Coords, COORDINATE:NewFromVec2(point)) - end - self.Triangles = self:Triangulate() - self.SurfaceArea = self:__CalculateSurfaceArea() - - return self -end - ---- Calculates the centroid of the polygon. The centroid is the average of the 'x' and 'y' coordinates of the points. --- @return #table The centroid of the polygon -function POLYGON:GetCentroid() - local function sum(t) - local total = 0 - for _, value in pairs(t) do - total = total + value - end - return total - end - - local x_values = {} - local y_values = {} - local length = table.length(self.Points) - - for _, point in pairs(self.Points) do - table.insert(x_values, point.x) - table.insert(y_values, point.y) - end - - local x = sum(x_values) / length - local y = sum(y_values) / length - - return { - ["x"] = x, - ["y"] = y - } -end - ---- Returns the coordinates of the polygon. Each coordinate is a COORDINATE object. --- @return #table The coordinates of the polygon -function POLYGON:GetCoordinates() - return self.Coords -end - ---- Returns the start coordinate of the polygon. The start coordinate is the first point of the polygon. --- @return #COORDINATE The start coordinate of the polygon -function POLYGON:GetStartCoordinate() - return self.Coords[1] -end - ---- Returns the end coordinate of the polygon. The end coordinate is the last point of the polygon. --- @return #COORDINATE The end coordinate of the polygon -function POLYGON:GetEndCoordinate() - return self.Coords[#self.Coords] -end - ---- Returns the start point of the polygon. The start point is the first point of the polygon. --- @return #table The start point of the polygon -function POLYGON:GetStartPoint() - return self.Points[1] -end - ---- Returns the end point of the polygon. The end point is the last point of the polygon. --- @return #table The end point of the polygon -function POLYGON:GetEndPoint() - return self.Points[#self.Points] -end - ---- Returns the points of the polygon. Each point is a table with 'x' and 'y' fields. --- @return #table The points of the polygon -function POLYGON:GetPoints() - return self.Points -end - ---- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon. --- @return #number The surface area of the polygon -function POLYGON:GetSurfaceArea() - return self.SurfaceArea -end - ---- Calculates the bounding box of the polygon. The bounding box is the smallest rectangle that contains the polygon. --- @return #table The bounding box of the polygon -function POLYGON:GetBoundingBox() - local min_x, min_y, max_x, max_y = self.Points[1].x, self.Points[1].y, self.Points[1].x, self.Points[1].y - - for i = 2, #self.Points do - local x, y = self.Points[i].x, self.Points[i].y - - if x < min_x then - min_x = x - end - if y < min_y then - min_y = y - end - if x > max_x then - max_x = x - end - if y > max_y then - max_y = y - end - end - return { - {x=min_x, y=min_x}, {x=max_x, y=min_y}, {x=max_x, y=max_y}, {x=min_x, y=max_y} - } -end - ---- Triangulates the polygon. The polygon is divided into triangles. --- @param #table points (optional) Points of the polygon or other points if you're just using the POLYGON class without an object of it --- @return #table The triangles of the polygon -function POLYGON:Triangulate(points) - points = points or self.Points - local triangles = {} - - local function get_orientation(shape_points) - local sum = 0 - for i = 1, #shape_points do - local j = i % #shape_points + 1 - sum = sum + (shape_points[j].x - shape_points[i].x) * (shape_points[j].y + shape_points[i].y) - end - return sum >= 0 and "clockwise" or "counter-clockwise" -- sum >= 0, return "clockwise", else return "counter-clockwise" - end - - local function ensure_clockwise(shape_points) - local orientation = get_orientation(shape_points) - if orientation == "counter-clockwise" then - -- Reverse the order of shape_points so they're clockwise - local reversed = {} - for i = #shape_points, 1, -1 do - table.insert(reversed, shape_points[i]) - end - return reversed - end - return shape_points - end - - local function is_clockwise(p1, p2, p3) - local cross_product = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) - return cross_product < 0 - end - - local function divide_recursively(shape_points) - if #shape_points == 3 then - table.insert(triangles, TRIANGLE:New(shape_points[1], shape_points[2], shape_points[3])) - elseif #shape_points > 3 then -- find an ear -> a triangle with no other points inside it - for i, p1 in ipairs(shape_points) do - local p2 = shape_points[(i % #shape_points) + 1] - local p3 = shape_points[(i + 1) % #shape_points + 1] - local triangle = TRIANGLE:New(p1, p2, p3) - local is_ear = true - - if not is_clockwise(p1, p2, p3) then - is_ear = false - else - for _, point in ipairs(shape_points) do - if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then - is_ear = false - break - end - end - end - - if is_ear then - -- Check if any point in the original polygon is inside the ear triangle - local is_valid_triangle = true - for _, point in ipairs(points) do - if point ~= p1 and point ~= p2 and point ~= p3 and triangle:ContainsPoint(point) then - is_valid_triangle = false - break - end - end - if is_valid_triangle then - table.insert(triangles, triangle) - local remaining_points = {} - for j, point in ipairs(shape_points) do - if point ~= p2 then - table.insert(remaining_points, point) - end - end - divide_recursively(remaining_points) - break - end - end - end - end - end - - points = ensure_clockwise(points) - divide_recursively(points) - return triangles -end - -function POLYGON:CovarianceMatrix() - local cx, cy = self:GetCentroid() - local covXX, covYY, covXY = 0, 0, 0 - for _, p in ipairs(self.points) do - covXX = covXX + (p.x - cx)^2 - covYY = covYY + (p.y - cy)^2 - covXY = covXY + (p.x - cx) * (p.y - cy) - end - covXX = covXX / (#self.points - 1) - covYY = covYY / (#self.points - 1) - covXY = covXY / (#self.points - 1) - return covXX, covYY, covXY -end - -function POLYGON:Direction() - local covXX, covYY, covXY = self:CovarianceMatrix() - -- Simplified calculation for the largest eigenvector's direction - local theta = 0.5 * math.atan2(2 * covXY, covXX - covYY) - return math.cos(theta), math.sin(theta) -end - ---- Returns a random Vec2 within the polygon. The Vec2 is weighted by the areas of the triangles that make up the polygon. --- @return #table The random Vec2 -function POLYGON:GetRandomVec2() - local weights = {} - for _, triangle in pairs(self.Triangles) do - weights[triangle] = triangle.SurfaceArea / self.SurfaceArea - end - - local random_weight = math.random() - local accumulated_weight = 0 - for triangle, weight in pairs(weights) do - accumulated_weight = accumulated_weight + weight - if accumulated_weight >= random_weight then - return triangle:GetRandomVec2() - end - end -end - ---- Returns a random non-weighted Vec2 within the polygon. The Vec2 is chosen from one of the triangles that make up the polygon. --- @return #table The random non-weighted Vec2 -function POLYGON:GetRandomNonWeightedVec2() - return self.Triangles[math.random(1, #self.Triangles)]:GetRandomVec2() -end - ---- Checks if a point is contained within the polygon. The point is a table with 'x' and 'y' fields. --- @param #table point The point to check --- @param #table points (optional) Points of the polygon or other points if you're just using the POLYGON class without an object of it --- @return #bool True if the point is contained, false otherwise -function POLYGON:ContainsPoint(point, polygon_points) - local x = point.x - local y = point.y - - polygon_points = polygon_points or self.Points - - local counter = 0 - local num_points = #polygon_points - for current_index = 1, num_points do - local next_index = (current_index % num_points) + 1 - local current_x, current_y = polygon_points[current_index].x, polygon_points[current_index].y - local next_x, next_y = polygon_points[next_index].x, polygon_points[next_index].y - if ((current_y > y) ~= (next_y > y)) and (x < (next_x - current_x) * (y - current_y) / (next_y - current_y) + current_x) then - counter = counter + 1 - end - end - return counter % 2 == 1 -end - ---- Draws the polygon on the map. The polygon can be drawn with or without inner triangles. This is just for debugging --- @param #bool include_inner_triangles Whether to include inner triangles in the drawing -function POLYGON:Draw(include_inner_triangles) - include_inner_triangles = include_inner_triangles or false - for i=1, #self.Coords do - local c1 = self.Coords[i] - local c2 = self.Coords[i % #self.Coords + 1] - table.add(self.OutlineMarkIDs, c1:LineToAll(c2)) - end - - - if include_inner_triangles then - for _, triangle in ipairs(self.Triangles) do - triangle:Draw() - end - end -end - ---- Removes the drawing of the polygon from the map. -function POLYGON:RemoveDraw() - for _, triangle in pairs(self.Triangles) do - triangle:RemoveDraw() - end - for _, mark_id in pairs(self.OutlineMarkIDs) do - UTILS.RemoveMark(mark_id) - end -end - ---- Calculates the surface area of the polygon. The surface area is the sum of the areas of the triangles that make up the polygon. --- @return #number The surface area of the polygon -function POLYGON:__CalculateSurfaceArea() - local area = 0 - for _, triangle in pairs(self.Triangles) do - area = area + triangle.SurfaceArea - end - return area -end - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Moose Development/Moose/Shapes/ShapeBase.lua b/Moose Development/Moose/Shapes/ShapeBase.lua deleted file mode 100644 index 7042a44ad..000000000 --- a/Moose Development/Moose/Shapes/ShapeBase.lua +++ /dev/null @@ -1,216 +0,0 @@ ---- **Shapes** - Class that serves as the base shapes drawn in the Mission Editor --- --- --- ### Author: **nielsvaes/coconutcockpit** --- --- === --- @module Shapes.SHAPE_BASE --- @image CORE_Pathline.png - - ---- SHAPE_BASE class. --- @type SHAPE_BASE --- @field #string ClassName Name of the class. --- @field #string Name Name of the shape --- @field #table CenterVec2 Vec2 of the center of the shape, this will be assigned automatically --- @field #table Points List of 3D points defining the shape, this will be assigned automatically --- @field #table Coords List of COORDINATE defining the path, this will be assigned automatically --- @field #table MarkIDs List any MARKIDs this class use, this will be assigned automatically --- @extends Core.Base#BASE - ---- *I'm in love with the shape of you -- Ed Sheeran --- --- === --- --- # SHAPE_BASE --- The class serves as the base class to deal with these shapes using MOOSE. You should never use this class on its own, --- rather use: --- CIRCLE --- LINE --- OVAL --- POLYGON --- TRIANGLE (although this one's a bit special as well) - --- === --- The idea is that anything you draw on the map in the Mission Editor can be turned in a shape to work with in MOOSE. --- This is the base class that all other shape classes are built on. There are some shared functions, most of which are overridden in the derived classes - --- @field #SHAPE_BASE - - -SHAPE_BASE = { - ClassName = "SHAPE_BASE", - Name = "", - CenterVec2 = nil, - Points = {}, - Coords = {}, - MarkIDs = {}, - ColorString = "", - ColorRGBA = {} -} - ---- Creates a new instance of SHAPE_BASE. --- @return #SHAPE_BASE The new instance -function SHAPE_BASE:New() - local self = BASE:Inherit(self, BASE:New()) - return self -end - ---- Finds a shape on the map by its name. --- @param #string shape_name Name of the shape to find --- @return #SHAPE_BASE The found shape -function SHAPE_BASE:FindOnMap(shape_name) - local self = BASE:Inherit(self, BASE:New()) - - local found = false - - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if object["name"] == shape_name then - self.Name = object["name"] - self.CenterVec2 = { x = object["mapX"], y = object["mapY"] } - self.ColorString = object["colorString"] - self.ColorRGBA = UTILS.HexToRGBA(self.ColorString) - found = true - end - end - end - if not found then - self:E("Can't find a shape with name " .. shape_name) - end - return self -end - -function SHAPE_BASE:GetAllShapes(filter) - filter = filter or "" - local return_shapes = {} - for _, layer in pairs(env.mission.drawings.layers) do - for _, object in pairs(layer["objects"]) do - if string.contains(object["name"], filter) then - table.add(return_shapes, object) - end - end - end - - return return_shapes -end - ---- Offsets the shape to a new position. --- @param #table new_vec2 The new position -function SHAPE_BASE:Offset(new_vec2) - local offset_vec2 = UTILS.Vec2Subtract(new_vec2, self.CenterVec2) - self.CenterVec2 = new_vec2 - if self.ClassName == "POLYGON" then - for _, point in pairs(self.Points) do - point.x = point.x + offset_vec2.x - point.y = point.y + offset_vec2.y - end - end -end - ---- Gets the name of the shape. --- @return #string The name of the shape -function SHAPE_BASE:GetName() - return self.Name -end - -function SHAPE_BASE:GetColorString() - return self.ColorString -end - -function SHAPE_BASE:GetColorRGBA() - return self.ColorRGBA -end - -function SHAPE_BASE:GetColorRed() - return self.ColorRGBA.R -end - -function SHAPE_BASE:GetColorGreen() - return self.ColorRGBA.G -end - -function SHAPE_BASE:GetColorBlue() - return self.ColorRGBA.B -end - -function SHAPE_BASE:GetColorAlpha() - return self.ColorRGBA.A -end - ---- Gets the center position of the shape. --- @return #table The center position -function SHAPE_BASE:GetCenterVec2() - return self.CenterVec2 -end - ---- Gets the center coordinate of the shape. --- @return #COORDINATE The center coordinate -function SHAPE_BASE:GetCenterCoordinate() - return COORDINATE:NewFromVec2(self.CenterVec2) -end - ---- Gets the coordinate of the shape. --- @return #COORDINATE The coordinate -function SHAPE_BASE:GetCoordinate() - return self:GetCenterCoordinate() -end - ---- Checks if a point is contained within the shape. --- @param #table _ The point to check --- @return #bool True if the point is contained, false otherwise -function SHAPE_BASE:ContainsPoint(_) - self:E("This needs to be set in the derived class") -end - ---- Checks if a unit is contained within the shape. --- @param #string unit_name The name of the unit to check --- @return #bool True if the unit is contained, false otherwise -function SHAPE_BASE:ContainsUnit(unit_name) - local unit = UNIT:FindByName(unit_name) - - if unit == nil or not unit:IsAlive() then - return false - end - - if self:ContainsPoint(unit:GetVec2()) then - return true - end - return false -end - ---- Checks if any unit of a group is contained within the shape. --- @param #string group_name The name of the group to check --- @return #bool True if any unit of the group is contained, false otherwise -function SHAPE_BASE:ContainsAnyOfGroup(group_name) - local group = GROUP:FindByName(group_name) - - if group == nil or not group:IsAlive() then - return false - end - - for _, unit in pairs(group:GetUnits()) do - if self:ContainsPoint(unit:GetVec2()) then - return true - end - end - return false -end - ---- Checks if all units of a group are contained within the shape. --- @param #string group_name The name of the group to check --- @return #bool True if all units of the group are contained, false otherwise -function SHAPE_BASE:ContainsAllOfGroup(group_name) - local group = GROUP:FindByName(group_name) - - if group == nil or not group:IsAlive() then - return false - end - - for _, unit in pairs(group:GetUnits()) do - if not self:ContainsPoint(unit:GetVec2()) then - return false - end - end - return true -end diff --git a/Moose Development/Moose/Shapes/Triangle.lua b/Moose Development/Moose/Shapes/Triangle.lua deleted file mode 100644 index c60b2aeef..000000000 --- a/Moose Development/Moose/Shapes/Triangle.lua +++ /dev/null @@ -1,86 +0,0 @@ --- TRIANGLE class with properties and methods for handling triangles. This class is mostly used by the POLYGON class, but you can use it on its own as well --- --- ### Author: **nielsvaes/coconutcockpit** --- --- -TRIANGLE = { - ClassName = "TRIANGLE", - Points = {}, - Coords = {}, - SurfaceArea = 0 -} - ---- Creates a new triangle from three points. The points need to be given as Vec2s --- @param #table p1 The first point of the triangle --- @param #table p2 The second point of the triangle --- @param #table p3 The third point of the triangle --- @return #TRIANGLE The new triangle -function TRIANGLE:New(p1, p2, p3) - local self = BASE:Inherit(self, SHAPE_BASE:New()) - self.Points = {p1, p2, p3} - - local center_x = (p1.x + p2.x + p3.x) / 3 - local center_y = (p1.y + p2.y + p3.y) / 3 - self.CenterVec2 = {x=center_x, y=center_y} - - for _, pt in pairs({p1, p2, p3}) do - table.add(self.Coords, COORDINATE:NewFromVec2(pt)) - end - - self.SurfaceArea = math.abs((p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y)) * 0.5 - - self.MarkIDs = {} - return self -end - ---- Checks if a point is contained within the triangle. --- @param #table pt The point to check --- @param #table points (optional) The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it --- @return #bool True if the point is contained, false otherwise -function TRIANGLE:ContainsPoint(pt, points) - points = points or self.Points - - local function sign(p1, p2, p3) - return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y) - end - - local d1 = sign(pt, self.Points[1], self.Points[2]) - local d2 = sign(pt, self.Points[2], self.Points[3]) - local d3 = sign(pt, self.Points[3], self.Points[1]) - - local has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0) - local has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0) - - return not (has_neg and has_pos) -end - ---- Returns a random Vec2 within the triangle. --- @param #table points The points of the triangle, or 3 other points if you're just using the TRIANGLE class without an object of it --- @return #table The random Vec2 -function TRIANGLE:GetRandomVec2(points) - points = points or self.Points - local pt = {math.random(), math.random()} - table.sort(pt) - local s = pt[1] - local t = pt[2] - pt[1] - local u = 1 - pt[2] - - return {x = s * points[1].x + t * points[2].x + u * points[3].x, - y = s * points[1].y + t * points[2].y + u * points[3].y} -end - ---- Draws the triangle on the map, just for debugging -function TRIANGLE:Draw() - for i=1, #self.Coords do - local c1 = self.Coords[i] - local c2 = self.Coords[i % #self.Coords + 1] - table.add(self.MarkIDs, c1:LineToAll(c2)) - end -end - ---- Removes the drawing of the triangle from the map. -function TRIANGLE:RemoveDraw() - for _, mark_id in pairs(self.MarkIDs) do - UTILS.RemoveMark(mark_id) - end -end diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 96cf8c1b4..6bf7e1fc9 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -3513,25 +3513,6 @@ function string.contains(str, value) return string.match(str, value) end - ---- Moves an object from one table to another --- @param #obj object to move --- @param #from_table table to move from --- @param #to_table table to move to -function table.move_object(obj, from_table, to_table) - local index - for i, v in pairs(from_table) do - if v == obj then - index = i - end - end - - if index then - local moved = table.remove(from_table, index) - table.insert_unique(to_table, moved) - end -end - --- Given tbl is a indexed table ({"hello", "dcs", "world"}), checks if element exists in the table. --- The table can be made up out of complex tables or values as well -- @param #table tbl @@ -3750,25 +3731,6 @@ function UTILS.OctalToDecimal(Number) return tonumber(Number,8) end - ---- HexToRGBA --- @param hex_string table --- @return #table R, G, B, A -function UTILS.HexToRGBA(hex_string) - local hexNumber = tonumber(string.sub(hex_string, 3), 16) -- convert the string to a number - -- extract RGBA components - local alpha = hexNumber % 256 - hexNumber = (hexNumber - alpha) / 256 - local blue = hexNumber % 256 - hexNumber = (hexNumber - blue) / 256 - local green = hexNumber % 256 - hexNumber = (hexNumber - green) / 256 - local red = hexNumber % 256 - - return {R = red, G = green, B = blue, A = alpha} -end - - --- Function to save the position of a set of #OPSGROUP (ARMYGROUP) objects. -- @param Core.Set#SET_OPSGROUP Set of ops objects to save -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. @@ -3806,7 +3768,7 @@ function UTILS.SaveSetOfOpsGroups(Set,Path,Filename,Structured) data = string.format("%s%s,%s,%s,%s,%d,%d,%d,%d,%s\n",data,name,legion,template,alttemplate,units,position.x,position.y,position.z,strucdata) else data = string.format("%s%s,%s,%s,%s,%d,%d,%d,%d\n",data,name,legion,template,alttemplate,units,position.x,position.y,position.z) - end + end end end -- save the data @@ -3818,12 +3780,12 @@ end -- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems. -- @param #string Filename The name of the file. -- @return #table Returns a table of data entries: `{ groupname=groupname, size=size, coordinate=coordinate, template=template, structure=structure, legion=legion, alttemplate=alttemplate }` --- Returns nil when the file cannot be read. +-- Returns nil when the file cannot be read. function UTILS.LoadSetOfOpsGroups(Path,Filename) local filename = Filename or "SetOfGroups" local datatable = {} - + if UTILS.CheckFileExists(Path,filename) then local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename) -- remove header @@ -3858,20 +3820,20 @@ end -- @param #number tgtHdg The absolute heading from the reference object to the target object/point in 0-360 -- @return #string text Text in clock heading such as "4 O'CLOCK" -- @usage Display the range and clock distance of a BTR in relation to REAPER 1-1's heading: --- +-- -- myUnit = UNIT:FindByName( "REAPER 1-1" ) -- myTarget = GROUP:FindByName( "BTR-1" ) --- +-- -- coordUnit = myUnit:GetCoordinate() -- coordTarget = myTarget:GetCoordinate() --- +-- -- hdgUnit = myUnit:GetHeading() -- hdgTarget = coordUnit:HeadingTo( coordTarget ) -- distTarget = coordUnit:Get3DDistance( coordTarget ) --- +-- -- clockString = UTILS.ClockHeadingString( hdgUnit, hdgTarget ) --- --- -- Will show this message to REAPER 1-1 in-game: Contact BTR at 3 o'clock for 1134m! +-- +-- -- Will show this message to REAPER 1-1 in-game: Contact BTR at 3 o'clock for 1134m! -- MESSAGE:New("Contact BTR at " .. clockString .. " for " .. distTarget .. "m!):ToUnit( myUnit ) function UTILS.ClockHeadingString(refHdg,tgtHdg) local relativeAngle = tgtHdg - refHdg