207 lines
7.2 KiB
Lua
207 lines
7.2 KiB
Lua
--- Position module
|
|
-- @module Position
|
|
|
|
Position = {}
|
|
|
|
require 'stdlib/core'
|
|
|
|
|
|
--- Creates a table representing the position from x and y
|
|
-- @param x x-position
|
|
-- @param y y-position
|
|
-- @return Position
|
|
function Position.construct(x, y)
|
|
fail_if_missing(x, "missing x position argument")
|
|
fail_if_missing(y, "missing y position argument")
|
|
return { x = x, y = y }
|
|
end
|
|
|
|
--- Creates a position that is a copy of the given position
|
|
-- @param pos the position to copy
|
|
-- @return Position
|
|
function Position.copy(pos)
|
|
fail_if_missing(pos, "missing position argument")
|
|
pos = Position.to_table(pos)
|
|
return { x = pos.x, y = pos.y }
|
|
end
|
|
|
|
--- Creates a position that is offset by x,y coordinate pair
|
|
-- @param pos the position to offset
|
|
-- @param x the amount to offset the position in the x direction
|
|
-- @param y the amount to offset the position in the y direction
|
|
-- @return a new position, offset by the x,y coordinates
|
|
function Position.offset(pos, x, y)
|
|
fail_if_missing(pos, "missing position argument")
|
|
fail_if_missing(x, "missing x-coordinate value")
|
|
fail_if_missing(y, "missing y-coordinate value")
|
|
|
|
if #pos == 2 then
|
|
return { x = pos[1] + x, y = pos[2] + y }
|
|
else
|
|
return { x = pos.x + x, y = pos.y + y }
|
|
end
|
|
end
|
|
|
|
--- Adds 2 positions
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return a new position
|
|
function Position.add(pos1, pos2)
|
|
fail_if_missing(pos1, "missing first position argument")
|
|
fail_if_missing(pos2, "missing second position argument")
|
|
|
|
pos1 = Position.to_table(pos1)
|
|
pos2 = Position.to_table(pos2)
|
|
return { x = pos1.x + pos2.x, y = pos1.y + pos2.y}
|
|
end
|
|
|
|
--- Subtracts 2 positions
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return a new position
|
|
function Position.subtract(pos1, pos2)
|
|
fail_if_missing(pos1, "missing first position argument")
|
|
fail_if_missing(pos2, "missing second position argument")
|
|
|
|
pos1 = Position.to_table(pos1)
|
|
pos2 = Position.to_table(pos2)
|
|
return { x = pos1.x - pos2.x, y = pos1.y - pos2.y }
|
|
end
|
|
|
|
--- Translates a position in the given direction
|
|
-- @param pos the position to translate
|
|
-- @param direction in which direction to translate (see defines.direction)
|
|
-- @param distance distance of the translation
|
|
-- @return the translated position
|
|
function Position.translate(pos, direction, distance)
|
|
fail_if_missing(pos, "missing position argument")
|
|
fail_if_missing(direction, "missing direction argument")
|
|
fail_if_missing(distance, "missing distance argument")
|
|
|
|
pos = Position.to_table(pos)
|
|
|
|
if direction == defines.direction.north then
|
|
return { x = pos.x, y = pos.y - distance }
|
|
elseif direction == defines.direction.northeast then
|
|
return { x = pos.x + distance, y = pos.y - distance }
|
|
elseif direction == defines.direction.east then
|
|
return { x = pos.x + distance, y = pos.y }
|
|
elseif direction == defines.direction.southeast then
|
|
return { x = pos.x + distance, y = pos.y + distance }
|
|
elseif direction == defines.direction.south then
|
|
return { x = pos.x, y = pos.y + distance }
|
|
elseif direction == defines.direction.southwest then
|
|
return { x = pos.x - distance, y = pos.y + distance }
|
|
elseif direction == defines.direction.west then
|
|
return { x = pos.x - distance, y = pos.y }
|
|
elseif direction == defines.direction.northwest then
|
|
return { x = pos.x - distance, y = pos.y - distance }
|
|
end
|
|
end
|
|
|
|
--- Expands a position to a square area
|
|
-- @param pos the position to expand into an area
|
|
-- @param radius half the side length of the area
|
|
-- @return a bounding box
|
|
function Position.expand_to_area(pos, radius)
|
|
fail_if_missing(pos, "missing position argument")
|
|
fail_if_missing(radius, "missing radius argument")
|
|
|
|
if #pos == 2 then
|
|
return { left_top = { x = pos[1] - radius, y = pos[2] - radius }, right_bottom = { x = pos[1] + radius, y = pos[2] + radius } }
|
|
end
|
|
return { left_top = { x = pos.x - radius, y = pos.y - radius}, right_bottom = { x = pos.x + radius, y = pos.y + radius } }
|
|
end
|
|
|
|
--- Calculates the Euclidean distance squared between two positions, useful when sqrt is not needed
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return the square of the Euclidean distance
|
|
function Position.distance_squared(pos1, pos2)
|
|
fail_if_missing(pos1, "missing first position argument")
|
|
fail_if_missing(pos2, "missing second position argument")
|
|
|
|
pos1 = Position.to_table(pos1)
|
|
pos2 = Position.to_table(pos2)
|
|
local axbx = pos1.x - pos2.x
|
|
local ayby = pos1.y - pos2.y
|
|
return axbx * axbx + ayby * ayby
|
|
end
|
|
|
|
--- Calculates the Euclidean distance between two positions
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return the square of the Euclidean distance
|
|
function Position.distance(pos1, pos2)
|
|
fail_if_missing(pos1, "missing first position argument")
|
|
fail_if_missing(pos2, "missing second position argument")
|
|
|
|
return math.sqrt(Position.distance_squared(pos1, pos2))
|
|
end
|
|
|
|
--- Calculates the manhatten distance between two positions
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return the square of the Euclidean distance
|
|
function Position.manhattan_distance(pos1, pos2)
|
|
fail_if_missing(pos1, "missing first position argument")
|
|
fail_if_missing(pos2, "missing second position argument")
|
|
pos1 = Position.to_table(pos1)
|
|
pos2 = Position.to_table(pos2)
|
|
|
|
return math.abs(pos2.x - pos1.x) + math.abs(pos2.y - pos1.y)
|
|
end
|
|
|
|
-- see: https://en.wikipedia.org/wiki/Machine_epsilon
|
|
Position._epsilon = 1.19e-07
|
|
|
|
--- Whether 2 positions are equal
|
|
-- @param pos1 the first position
|
|
-- @param pos2 the second position
|
|
-- @return true if positions are equal
|
|
function Position.equals(pos1, pos2)
|
|
if not pos1 or not pos2 then return false end
|
|
-- optimize for a shallow equality check first
|
|
if pos1 == pos2 then return true end
|
|
|
|
local epsilon = Position._epsilon
|
|
local abs = math.abs
|
|
if #pos1 == 2 and #pos2 == 2 then
|
|
return abs(pos1[1] - pos2[1]) < epsilon and abs(pos1[2] - pos2[2]) < epsilon
|
|
elseif #pos1 == 2 and #pos2 == 0 then
|
|
return abs(pos1[1] - pos2.x) < epsilon and abs(pos1[2] - pos2.y) < epsilon
|
|
elseif #pos1 == 0 and #pos2 == 2 then
|
|
return abs(pos1.x - pos2[1]) < epsilon and abs(pos1.y - pos2[2]) < epsilon
|
|
elseif #pos1 == 0 and #pos2 == 0 then
|
|
return abs(pos1.x - pos2.x) < epsilon and abs(pos1.y - pos2.y) < epsilon
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
--- Converts a position in the array format to a position in the table format
|
|
-- @param pos_arr the position to convert
|
|
-- @return a converted position, { x = pos_arr[1], y = pos_arr[2] }
|
|
function Position.to_table(pos_arr)
|
|
fail_if_missing(pos_arr, "missing position argument")
|
|
|
|
if #pos_arr == 2 then
|
|
return { x = pos_arr[1], y = pos_arr[2] }
|
|
end
|
|
return pos_arr
|
|
end
|
|
|
|
--- Converts a position to a string
|
|
-- @param pos the position to convert
|
|
-- @return string representation of pos
|
|
function Position.tostring(pos)
|
|
fail_if_missing(pos, "missing position argument")
|
|
if #pos == 2 then
|
|
return "Position {x = " .. pos[1] .. ", y = " .. pos[2] .. "}"
|
|
else
|
|
return "Position {x = " .. pos.x .. ", y = " .. pos.y .. "}"
|
|
end
|
|
end
|
|
|
|
return Position
|