--- -- -- ### 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