|
|
|
local math, getmetatable, setmetatable, vector
|
|
|
|
= math, getmetatable, setmetatable, vector
|
|
|
|
|
|
|
|
local math_min, math_max, math_floor, math_ceil, math_round
|
|
|
|
= math.min, math.max, math.floor, math.ceil, math.round
|
|
|
|
|
|
|
|
include("vector_extensions")
|
|
|
|
|
|
|
|
local region = {}
|
|
|
|
local metatable = { __index = region }
|
|
|
|
region.metatable = metatable
|
|
|
|
|
|
|
|
local function fast_new(min, max) return setmetatable({ min = min, max = max }, metatable) end
|
|
|
|
|
|
|
|
-- Returns a new region from the specified `min` and `max` position vectors.
|
|
|
|
function region.new(min, max) return fast_new(vector.copy(min), vector.copy(max)) end
|
|
|
|
-- Returns a deep copy of this region. Can also be called as `region.copy(r))`.
|
|
|
|
function region:copy() return region.new(self.min, self.max) end
|
|
|
|
|
|
|
|
-- Returns this region's bounds as an array in the form of
|
|
|
|
-- `{ min.x, min.y, min.z, max.x, max.y, max.z }`, similar to `collisionbox`.
|
|
|
|
function region:to_array() return { self.min.x, self.min.y, self.min.z, self.max.x, self.max.y, self.max.z } end
|
|
|
|
-- Returns a new region from the specified array `a` in the form of
|
|
|
|
-- `{ min.x, min.y, min.z, max.x, max.y, max.z }`, similar to `collisionbox`.
|
|
|
|
function region.from_array(a) return fast_new(vector.new(a[1], a[2], a[3]), vector.new(a[4], a[5], a[6])) end
|
|
|
|
-- Returns a new region from the specified `ObjectRef` using its position and collision box.
|
|
|
|
function region.from_object(obj) return region.from_array(obj:get_properties().collisionbox):offset_in_place(obj:get_pos()) end
|
|
|
|
|
|
|
|
function region:size() return self.max - self.min end
|
|
|
|
function region:center() return (self.min + self.max) / 2 end
|
|
|
|
|
|
|
|
-- Offsets this region by the specified vector.
|
|
|
|
function region:offset_in_place(...)
|
|
|
|
local x, y, z = vector.get(...)
|
|
|
|
local min, max = self.min, self.max
|
|
|
|
min.x = min.x + x
|
|
|
|
min.y = min.y + y
|
|
|
|
min.z = min.z + z
|
|
|
|
max.x = max.x + x
|
|
|
|
max.y = max.y + y
|
|
|
|
max.z = max.z + z
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
-- Returns a new region offset by the specified vector.
|
|
|
|
function region:offset(...)
|
|
|
|
return self:copy():offset_in_place(...)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Expanded this region in all directions by the specified amount.
|
|
|
|
-- For example, `region:expand_in_place(2)` expands the region by 2 meters in all directions.
|
|
|
|
function region:expand_in_place(...)
|
|
|
|
local x, y, z = vector.get(...)
|
|
|
|
local min, max = self.min, self.max
|
|
|
|
min.x = min.x - x
|
|
|
|
min.y = min.y - y
|
|
|
|
min.z = min.z - z
|
|
|
|
max.x = max.x + x
|
|
|
|
max.y = max.y + y
|
|
|
|
max.z = max.z + z
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
-- Returns a new region expanded in all directions by the specified amount.
|
|
|
|
-- For example, `region:expand(2)` expands the region by 2 meters in all directions.
|
|
|
|
function region:expand(...)
|
|
|
|
return self:copy():expand_in_place(...)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Extends this region by the specified vector depending on the signedness of its components.
|
|
|
|
-- For example, `region:extend_in_place(0, -2, 0)` extends the region downwards by 2 meters.
|
|
|
|
function region:extend_in_place(...)
|
|
|
|
local x, y, z = vector.get(...)
|
|
|
|
local min, max = self.min, self.max
|
|
|
|
min.x = min.x + math_min(0, x)
|
|
|
|
min.y = min.y + math_min(0, y)
|
|
|
|
min.z = min.z + math_min(0, z)
|
|
|
|
max.x = max.x + math_max(0, x)
|
|
|
|
max.y = max.y + math_max(0, y)
|
|
|
|
max.z = max.z + math_max(0, z)
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
-- Returns a new region extended by the specified vector depending on the signedness of its components.
|
|
|
|
-- For example, `region:extend(0, -2, 0)` extends the region downwards by 2 meters.
|
|
|
|
function region:extend(...)
|
|
|
|
return self:copy():extend_in_place(...)
|
|
|
|
end
|
|
|
|
|
|
|
|
function region:round_in_place()
|
|
|
|
local min, max = self.min, self.max
|
|
|
|
min.x = math_round(min.x)
|
|
|
|
min.y = math_round(min.y)
|
|
|
|
min.z = math_round(min.z)
|
|
|
|
max.x = math_round(max.x)
|
|
|
|
max.y = math_round(max.y)
|
|
|
|
max.z = math_round(max.z)
|
|
|
|
return self
|
|
|
|
end
|
|
|
|
function region:round()
|
|
|
|
return self:copy():round_in_place()
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns if the specified `r` overlaps with this region.
|
|
|
|
function region:overlaps(r)
|
|
|
|
return r.min.x < self.max.x and r.max.x > self.min.x
|
|
|
|
and r.min.y < self.max.y and r.max.y > self.min.y
|
|
|
|
and r.min.z < self.max.z and r.max.z > self.min.z
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns if the specified `pos` is contained within this region.
|
|
|
|
function region:contains(pos)
|
|
|
|
return pos.x >= self.min.x and pos.x <= self.max.x
|
|
|
|
and pos.y >= self.min.y and pos.y <= self.max.y
|
|
|
|
and pos.z >= self.min.z and pos.z <= self.max.z
|
|
|
|
end
|
|
|
|
|
|
|
|
function region:to_relative(abs_pos) return vector.subtract(abs_pos, self.min) end
|
|
|
|
function region:to_absolute(rel_pos) return vector.add(rel_pos, self.min) end
|
|
|
|
|
|
|
|
-- Returns an iterator over the node positions contained in this region.
|
|
|
|
function region:iter_node_positions()
|
|
|
|
local min_x = math_ceil(self.min.x)
|
|
|
|
local min_y = math_ceil(self.min.y)
|
|
|
|
local min_z = math_ceil(self.min.z)
|
|
|
|
local max_x = math_floor(self.max.x)
|
|
|
|
local max_y = math_floor(self.max.y)
|
|
|
|
local max_z = math_floor(self.max.z)
|
|
|
|
local x, y, z = min_x, min_y, min_z
|
|
|
|
return function()
|
|
|
|
local result = vector.new(x, y, z)
|
|
|
|
x = x + 1
|
|
|
|
if x > max_x then x = min_x; y = y + 1 end
|
|
|
|
if y > max_y then y = min_y; z = z + 1 end
|
|
|
|
if z > max_z then return nil end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function region:check()
|
|
|
|
return getmetatable(self) == metatable
|
|
|
|
end
|
|
|
|
|
|
|
|
function region.equals(a, b)
|
|
|
|
return vector.equals(a.min, b.min)
|
|
|
|
and vector.equals(a.max, b.max)
|
|
|
|
end
|
|
|
|
metatable.__eq = region.equals
|
|
|
|
|
|
|
|
return region
|