mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
214 lines
7.2 KiB
Lua
214 lines
7.2 KiB
Lua
---
|
|
--
|
|
-- ### Author: **nielsvaes/coconutcockpit**
|
|
--
|
|
-- ===
|
|
-- @module Shapes.OVAL
|
|
-- @image MOOSE.JPG
|
|
|
|
--- 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
|
|
|
|
--- OVAL class with properties and methods for handling ovals.
|
|
-- @field #OVAL
|
|
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
|
|
|
|
|