Merge pull request #10 from davisdude/master

Added point.lua
This commit is contained in:
Davis 2015-12-14 14:56:21 -05:00
commit 8a3ee6a4fb

139
modules/point.lua Normal file
View File

@ -0,0 +1,139 @@
--- Point module
-- @module point
local current_folder = (...):gsub('%.[^%.]+$', '') .. "."
local point = {}
local function new(x, y)
return setmetatable({
x, y
}, point)
end
point.__index = point
point.__call = function(_, ...) return new(...) end
--- Rotate a point, optionally about another point.
-- @param point Table with two points as {x, y}
-- @param rotation Radian amount to rotate
-- @param[opt=0] offset_x Point to rotate about
-- @param[optchain=0] offset_y offset_y Point to rotatea about
-- @return New point rotated counter-clockwise
function point.rotate(point, rotation, offset_x, offset_y)
offset_x, offset_y = offset_x or 0, offset_y or 0
local x, y = unpack(point)
local distance_x, distance_y = x - offset_x, y - offset_y
local cos, sin = math.cos(rotation), math.sin(rotation)
return new(distance_x * cos + offset_x - distance_y * sin, distance_x * sin + distance_y * cos + offset_y)
end
--- Scale a point a point, optionally about another point.
-- @param point Table with two points as {x, y}
-- @param scale Number or Table in the form of scale or {scaleX [, scaleY=scaleX]}
-- @param[opt=0] offset_x Point to scale about
-- @param[optchain=0] offset_y Point to scale about
-- @return New scaled point
function point.scale(point, scale, offset_x, offset_y)
local scale_x, scale_y
if type( scale ) == 'table' then
scale_x, scale_y = unpack(scale)
scale_y = scale_y or scale_x
elseif type( scale ) == 'number' then
scale_x, scale_y = scale, scale
end
offset_x, offset_y = offset_x or 0, offset_y or 0
local x, y = unpack(point)
return new((x - offset_x) * scale_x + offset_x, (y - offset_y) * scale_y + offset_y)
end
--- Translate a point.
-- @param point Table with two points as {x, y}
-- @param distance_x Distance to translate along the x-axis
-- @param distance_y Distance to translate along the y-axis
-- @return Translated point
function point.translate(point, distance_x, distance_y)
return new(point[1] + distance_x, point[2] + distance_y)
end
--- Print point.
-- @param point Table with two numbers as {x, y}
-- @return "[ x, y ]"
function point.__tostring(point)
return string.format( "[ %d, %d ]", point[1], point[2] )
end
local function ispoint( p )
return p[1] and p[2]
end
--- Add points.
-- @param a Table with two numbers as {x, y} or a number
-- @param b Table with two numbers as {x, y} or a number
-- @return Translated point
function point.__add(a, b)
if type(a) == "number" then
return point.translate(b, a, a)
elseif type(b) == "number" then
return point.translate(a, b, b)
else
assert(ispoint(a) and ispoint(b), "Add: wrong argument types (<point> expected)")
return point.translate(a, b[1], b[2])
end
end
--- Subtract points.
-- @param a Table with two numbers as {x, y} or a number
-- @param b Table with two numbers as {x, y} or a number
-- @return Translated point
function point.__sub(a, b)
if type(a) == "number" then
return point.translate(b, -a, -a)
elseif type(b) == "number" then
return point.translate(a, -b, -b)
else
assert(ispoint(a) and ispoint(b), "Subtract: wrong argument types (<point> expected)")
return point.translate(a, -b[1], -b[2])
end
end
--- Multiply points.
-- @param a Table with two numbers as {x, y} or amount to scale
-- @param b Table with two numbers as {x, y} or amount to scale
-- @return Scaled point
function point.__mul(a, b)
if type(a) == "number" then
return point.scale(b, a)
elseif type(b) == "number" then
return point.scale(a, b)
end
error( "Multiply: cannot multiply two points" )
end
--- Convert point from polar to cartesian.
-- @param radius Radius of the point
-- @param theta Angle of the point
-- @param[opt=0] offset_radius Distance from origin
-- @param[optchain=0] offset_theta Angle for offset
-- @return Converted point
function point.from_polar(radius, theta, offset_radius, offset_theta)
local offset_x, offset_y = 0, 0
if offset_radius and offset_theta then
offset_x, offset_y = point.from_polar(offset_radius, offset_theta)
end
return new(radius * math.cos(theta) + offset_x, radius * math.sin(theta) + offset_y)
end
--- Convert point from cartesian to polar.
-- @param point Table with two numbers as {x, y}
-- @param[opt=0] offset_x Horizontal offset
-- @param[optchain=0] offset_y Vertical offset
-- @return Table in the form {radius, theta}
function point.to_polar(point, offset_x, offset_y)
local offset_x, offset_y = offset_x or 0, offset_y or 0
local x, y = point[1] - offset_x, point[2] - offset_y
local theta = math.atan2(y, x)
-- Convert to absolute angle
theta = theta > 0 and theta or theta + 2 * math.pi
local radius = math.sqrt(x ^ 2 + y ^ 2)
return {radius, theta}
end
return setmetatable({new = new}, point)