Merge pull request #30 from mcclure/aabb
Axis-aligned bounding box classes for 2 and 3 dimensions
This commit is contained in:
commit
130fe2aca0
@ -10,6 +10,8 @@ Portions of mat4.lua are from LuaMatrix, (c) 2010 Michael Lutz. MIT.
|
||||
|
||||
Code in simplex.lua is (c) 2011 Stefan Gustavson. MIT.
|
||||
|
||||
Code in bound2.lua and bound3.lua are (c) 2018 Andi McClure. MIT.
|
||||
|
||||
Code in quat.lua is from Andrew Stacey and covered under the CC0 license.
|
||||
|
||||
Code in octree.lua is derived from UnityOctree. (c) 2014 Nition. BSD-2-Clause.
|
||||
|
166
modules/bound2.lua
Normal file
166
modules/bound2.lua
Normal file
@ -0,0 +1,166 @@
|
||||
--- A 2 component bounding box.
|
||||
-- @module bound2
|
||||
|
||||
local modules = (...):gsub('%.[^%.]+$', '') .. "."
|
||||
local vec2 = require(modules .. "vec2")
|
||||
|
||||
local bound2 = {}
|
||||
local bound2_mt = {}
|
||||
|
||||
-- Private constructor.
|
||||
local function new(min, max)
|
||||
return setmetatable({
|
||||
min=min, -- min: vec2, minimum value for each component
|
||||
max=max, -- max: vec2, maximum value for each component
|
||||
}, bound2_mt)
|
||||
end
|
||||
|
||||
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
|
||||
local status, ffi
|
||||
if type(jit) == "table" and jit.status() then
|
||||
status, ffi = pcall(require, "ffi")
|
||||
if status then
|
||||
ffi.cdef "typedef struct { cpml_vec2 min, max; } cpml_bound2;"
|
||||
new = ffi.typeof("cpml_bound2")
|
||||
end
|
||||
end
|
||||
|
||||
bound2.zero = new(vec2.zero, vec2.zero)
|
||||
|
||||
--- The public constructor.
|
||||
-- @param min Can be of two types: </br>
|
||||
-- vec2 min, minimum value for each component
|
||||
-- nil Create bound at single point 0,0
|
||||
-- @tparam vec2 max, maximum value for each component
|
||||
-- @treturn bound2 out
|
||||
function bound2.new(min, max)
|
||||
if min and max then
|
||||
return new(min:clone(), max:clone())
|
||||
elseif min or max then
|
||||
error("Unexpected nil argument to bound2.new")
|
||||
else
|
||||
return new(vec2.zero, vec2.zero)
|
||||
end
|
||||
end
|
||||
|
||||
--- Clone a bound.
|
||||
-- @tparam bound2 a bound to be cloned
|
||||
-- @treturn bound2 out
|
||||
function bound2.clone(a)
|
||||
return new(a.min, a.max)
|
||||
end
|
||||
|
||||
--- Construct a bound covering one or two points
|
||||
-- @tparam vec2 a Any vector
|
||||
-- @tparam vec2 b Any second vector (optional)
|
||||
-- @treturn vec2 Minimum bound containing the given points
|
||||
function bound2.at(a, b) -- "bounded by". b may be nil
|
||||
if b then
|
||||
return bound2.new(a,b):check()
|
||||
else
|
||||
return bound2.zero:with_center(a)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get size of bounding box as a vector
|
||||
-- @tparam bound2 a bound
|
||||
-- @treturn vec2 Vector spanning min to max points
|
||||
function bound2.size(a)
|
||||
return a.max - a.min
|
||||
end
|
||||
|
||||
--- Resize bounding box from minimum corner
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 new size
|
||||
-- @treturn bound2 resized bound
|
||||
function bound2.with_size(a, size)
|
||||
return bound2.new(a.min, a.min + size)
|
||||
end
|
||||
|
||||
--- Get half-size of bounding box as a vector. A more correct term for this is probably "apothem"
|
||||
-- @tparam bound2 a bound
|
||||
-- @treturn vec2 Vector spanning center to max point
|
||||
function bound2.radius(a)
|
||||
return a:size()/2
|
||||
end
|
||||
|
||||
--- Get center of bounding box
|
||||
-- @tparam bound2 a bound
|
||||
-- @treturn bound2 Point in center of bound
|
||||
function bound2.center(a)
|
||||
return (a.min + a.max)/2
|
||||
end
|
||||
|
||||
--- Move bounding box to new center
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 new center
|
||||
-- @treturn bound2 Bound with same size as input but different center
|
||||
function bound2.with_center(a, center)
|
||||
return bound2.offset(a, center - a:center())
|
||||
end
|
||||
|
||||
--- Resize bounding box from center
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 new size
|
||||
-- @treturn bound2 resized bound
|
||||
function bound2.with_size_centered(a, size)
|
||||
local center = a:center()
|
||||
local rad = size/2
|
||||
return bound2.new(center - rad, center + rad)
|
||||
end
|
||||
|
||||
--- Convert possibly-invalid bounding box to valid one
|
||||
-- @tparam bound2 a bound
|
||||
-- @treturn bound2 bound with all components corrected for min-max property
|
||||
function bound2.check(a)
|
||||
if a.min.x > a.max.x or a.min.y > a.max.y then
|
||||
return bound2.new(vec2.component_min(a.min, a.max), vec2.component_max(a.min, a.max))
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
--- Shrink bounding box with fixed margin
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 a margin
|
||||
-- @treturn bound2 bound with margin subtracted from all edges. May not be valid, consider calling check()
|
||||
function bound2.inset(a, v)
|
||||
return bound2.new(a.min + v, a.max - v)
|
||||
end
|
||||
|
||||
--- Expand bounding box with fixed margin
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 a margin
|
||||
-- @treturn bound2 bound with margin added to all edges. May not be valid, consider calling check()
|
||||
function bound2.outset(a, v)
|
||||
return bound2.new(a.min - v, a.max + v)
|
||||
end
|
||||
|
||||
--- Offset bounding box
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 offset
|
||||
-- @treturn bound2 bound with same size, but position moved by offset
|
||||
function bound2.offset(a, v)
|
||||
return bound2.new(a.min + v, a.max + v)
|
||||
end
|
||||
|
||||
--- Test if point in bound
|
||||
-- @tparam bound2 a bound
|
||||
-- @tparam vec2 point to test
|
||||
-- @treturn boolean true if point in bounding box
|
||||
function bound2.contains(a, v)
|
||||
return a.min.x <= v.x and a.min.y <= v.y and a.min.z <= v.z
|
||||
and a.max.x >= v.x and a.max.y >= v.y and a.max.z >= v.z
|
||||
end
|
||||
|
||||
bound2_mt.__index = bound2
|
||||
bound2_mt.__tostring = bound2.to_string
|
||||
|
||||
function bound2_mt.__call(_, a, b)
|
||||
return bound2.new(a, b)
|
||||
end
|
||||
|
||||
if status then
|
||||
ffi.metatype(new, bound2_mt)
|
||||
end
|
||||
|
||||
return setmetatable({}, bound2_mt)
|
166
modules/bound3.lua
Normal file
166
modules/bound3.lua
Normal file
@ -0,0 +1,166 @@
|
||||
--- A 3-component axis-aligned bounding box.
|
||||
-- @module bound3
|
||||
|
||||
local modules = (...):gsub('%.[^%.]+$', '') .. "."
|
||||
local vec3 = require(modules .. "vec3")
|
||||
|
||||
local bound3 = {}
|
||||
local bound3_mt = {}
|
||||
|
||||
-- Private constructor.
|
||||
local function new(min, max)
|
||||
return setmetatable({
|
||||
min=min, -- min: vec3, minimum value for each component
|
||||
max=max -- max: vec3, maximum value for each component
|
||||
}, bound3_mt)
|
||||
end
|
||||
|
||||
-- Do the check to see if JIT is enabled. If so use the optimized FFI structs.
|
||||
local status, ffi
|
||||
if type(jit) == "table" and jit.status() then
|
||||
status, ffi = pcall(require, "ffi")
|
||||
if status then
|
||||
ffi.cdef "typedef struct { cpml_vec3 min, max; } cpml_bound3;"
|
||||
new = ffi.typeof("cpml_bound3")
|
||||
end
|
||||
end
|
||||
|
||||
bound3.zero = new(vec3.zero, vec3.zero)
|
||||
|
||||
--- The public constructor.
|
||||
-- @param min Can be of two types: </br>
|
||||
-- vec3 min, minimum value for each component
|
||||
-- nil Create bound at single point 0,0,0
|
||||
-- @tparam vec3 max, maximum value for each component
|
||||
-- @treturn bound3 out
|
||||
function bound3.new(min, max)
|
||||
if min and max then
|
||||
return new(min:clone(), max:clone())
|
||||
elseif min or max then
|
||||
error("Unexpected nil argument to bound3.new")
|
||||
else
|
||||
return new(vec3.zero, vec3.zero)
|
||||
end
|
||||
end
|
||||
|
||||
--- Clone a bound.
|
||||
-- @tparam bound3 a bound to be cloned
|
||||
-- @treturn bound3 out
|
||||
function bound3.clone(a)
|
||||
return new(a.min, a.max)
|
||||
end
|
||||
|
||||
--- Construct a bound covering one or two points
|
||||
-- @tparam vec3 a Any vector
|
||||
-- @tparam vec3 b Any second vector (optional)
|
||||
-- @treturn vec3 Minimum bound containing the given points
|
||||
function bound3.at(a, b) -- "bounded by". b may be nil
|
||||
if b then
|
||||
return bound3.new(a,b):check()
|
||||
else
|
||||
return bound3.zero:with_center(a)
|
||||
end
|
||||
end
|
||||
|
||||
--- Get size of bounding box as a vector
|
||||
-- @tparam bound3 a bound
|
||||
-- @treturn vec3 Vector spanning min to max points
|
||||
function bound3.size(a)
|
||||
return a.max - a.min
|
||||
end
|
||||
|
||||
--- Resize bounding box from minimum corner
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 new size
|
||||
-- @treturn bound3 resized bound
|
||||
function bound3.with_size(a, size)
|
||||
return bound3.new(a.min, a.min + size)
|
||||
end
|
||||
|
||||
--- Get half-size of bounding box as a vector. A more correct term for this is probably "apothem"
|
||||
-- @tparam bound3 a bound
|
||||
-- @treturn vec3 Vector spanning center to max point
|
||||
function bound3.radius(a)
|
||||
return a:size()/2
|
||||
end
|
||||
|
||||
--- Get center of bounding box
|
||||
-- @tparam bound3 a bound
|
||||
-- @treturn bound3 Point in center of bound
|
||||
function bound3.center(a)
|
||||
return (a.min + a.max)/2
|
||||
end
|
||||
|
||||
--- Move bounding box to new center
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 new center
|
||||
-- @treturn bound3 Bound with same size as input but different center
|
||||
function bound3.with_center(a, center)
|
||||
return bound3.offset(a, center - a:center())
|
||||
end
|
||||
|
||||
--- Resize bounding box from center
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 new size
|
||||
-- @treturn bound3 resized bound
|
||||
function bound3.with_size_centered(a, size)
|
||||
local center = a:center()
|
||||
local rad = size/2
|
||||
return bound3.new(center - rad, center + rad)
|
||||
end
|
||||
|
||||
--- Convert possibly-invalid bounding box to valid one
|
||||
-- @tparam bound3 a bound
|
||||
-- @treturn bound3 bound with all components corrected for min-max property
|
||||
function bound3.check(a)
|
||||
if a.min.x > a.max.x or a.min.y > a.max.y or a.min.z > a.max.z then
|
||||
return bound3.new(vec3.component_min(a.min, a.max), vec3.component_max(a.min, a.max))
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
--- Shrink bounding box with fixed margin
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 a margin
|
||||
-- @treturn bound3 bound with margin subtracted from all edges. May not be valid, consider calling check()
|
||||
function bound3.inset(a, v)
|
||||
return bound3.new(a.min + v, a.max - v)
|
||||
end
|
||||
|
||||
--- Expand bounding box with fixed margin
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 a margin
|
||||
-- @treturn bound3 bound with margin added to all edges. May not be valid, consider calling check()
|
||||
function bound3.outset(a, v)
|
||||
return bound3.new(a.min - v, a.max + v)
|
||||
end
|
||||
|
||||
--- Offset bounding box
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 offset
|
||||
-- @treturn bound3 bound with same size, but position moved by offset
|
||||
function bound3.offset(a, v)
|
||||
return bound3.new(a.min + v, a.max + v)
|
||||
end
|
||||
|
||||
--- Test if point in bound
|
||||
-- @tparam bound3 a bound
|
||||
-- @tparam vec3 point to test
|
||||
-- @treturn boolean true if point in bounding box
|
||||
function bound3.contains(a, v)
|
||||
return a.min.x <= v.x and a.min.y <= v.y and a.min.z <= v.z
|
||||
and a.max.x >= v.x and a.max.y >= v.y and a.max.z >= v.z
|
||||
end
|
||||
|
||||
bound3_mt.__index = bound3
|
||||
bound3_mt.__tostring = bound3.to_string
|
||||
|
||||
function bound3_mt.__call(_, a, b)
|
||||
return bound3.new(a, b)
|
||||
end
|
||||
|
||||
if status then
|
||||
ffi.metatype(new, bound3_mt)
|
||||
end
|
||||
|
||||
return setmetatable({}, bound3_mt)
|
@ -272,6 +272,23 @@ function vec2.unpack(a)
|
||||
return a.x, a.y
|
||||
end
|
||||
|
||||
--- Return the component-wise minimum of two vectors.
|
||||
-- @tparam vec2 a Left hand operand
|
||||
-- @tparam vec2 b Right hand operand
|
||||
-- @treturn vec2 A vector where each component is the lesser value for that component between the two given vectors.
|
||||
function vec2.component_min(a, b)
|
||||
return new(math.min(a.x, b.x), math.min(a.y, b.y))
|
||||
end
|
||||
|
||||
--- Return the component-wise maximum of two vectors.
|
||||
-- @tparam vec2 a Left hand operand
|
||||
-- @tparam vec2 b Right hand operand
|
||||
-- @treturn vec2 A vector where each component is the lesser value for that component between the two given vectors.
|
||||
function vec2.component_max(a, b)
|
||||
return new(math.max(a.x, b.x), math.max(a.y, b.y))
|
||||
end
|
||||
|
||||
|
||||
--- Return a boolean showing if a table is or is not a vec2.
|
||||
-- @tparam vec2 a Vector to be tested
|
||||
-- @treturn boolean is_vec2
|
||||
|
@ -263,6 +263,22 @@ function vec3.unpack(a)
|
||||
return a.x, a.y, a.z
|
||||
end
|
||||
|
||||
--- Return the component-wise minimum of two vectors.
|
||||
-- @tparam vec3 a Left hand operand
|
||||
-- @tparam vec3 b Right hand operand
|
||||
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
|
||||
function vec3.component_min(a, b)
|
||||
return new(math.min(a.x, b.x), math.min(a.y, b.y), math.min(a.z, b.z))
|
||||
end
|
||||
|
||||
--- Return the component-wise maximum of two vectors.
|
||||
-- @tparam vec3 a Left hand operand
|
||||
-- @tparam vec3 b Right hand operand
|
||||
-- @treturn vec3 A vector where each component is the lesser value for that component between the two given vectors.
|
||||
function vec3.component_max(a, b)
|
||||
return new(math.max(a.x, b.x), math.max(a.y, b.y), math.max(a.z, b.z))
|
||||
end
|
||||
|
||||
--- Return a boolean showing if a table is or is not a vec3.
|
||||
-- @tparam vec3 a Vector to be tested
|
||||
-- @treturn boolean is_vec3
|
||||
|
180
spec/bound2_spec.lua
Normal file
180
spec/bound2_spec.lua
Normal file
@ -0,0 +1,180 @@
|
||||
local bound2 = require "modules.bound2"
|
||||
local vec2 = require "modules.vec2"
|
||||
local DBL_EPSILON = require("modules.constants").DBL_EPSILON
|
||||
|
||||
describe("bound2:", function()
|
||||
it("creates an empty bound2", function()
|
||||
local a = bound2()
|
||||
assert.is.equal(0, a.min.x)
|
||||
assert.is.equal(0, a.min.y)
|
||||
assert.is.equal(0, a.max.x)
|
||||
assert.is.equal(0, a.max.y)
|
||||
end)
|
||||
|
||||
it("creates a bound2 from vec2s", function()
|
||||
local a = bound2(vec2(1,2), vec2(4,5))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
end)
|
||||
|
||||
it("creates a bound2 using new()", function()
|
||||
local a = bound2.new(vec2(1,2), vec2(4,5))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
end)
|
||||
|
||||
it("creates a bound2 using at()", function()
|
||||
local a = bound2.at(vec2(4,5), vec2(1,2))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
end)
|
||||
|
||||
it("clones a bound2", function()
|
||||
local a = bound2(vec2(1,2), vec2(4,5))
|
||||
local b = a:clone()
|
||||
a.max = new vec2(9,9)
|
||||
assert.is.equal(a.min, b.min)
|
||||
assert.is.not_equal(a.max, b.max)
|
||||
end)
|
||||
|
||||
it("uses bound2 check()", function()
|
||||
local a = bound2(vec2(4,2), vec2(1,5)):check()
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
end)
|
||||
|
||||
it("queries a bound2 size", function()
|
||||
local a = bound2(vec2(1,2), vec2(4,6))
|
||||
local v = a:size()
|
||||
local r = a:radius()
|
||||
assert.is.equal(3, v.x)
|
||||
assert.is.equal(4, v.y)
|
||||
|
||||
assert.is.equal(1.5, r.x)
|
||||
assert.is.equal(2, r.y)
|
||||
end)
|
||||
|
||||
it("sets a bound2 size", function()
|
||||
local a = bound2(vec2(1,2), vec2(4,5))
|
||||
local b = a:with_size(vec2(1,1))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
|
||||
assert.is.equal(1, b.min.x)
|
||||
assert.is.equal(2, b.min.y)
|
||||
assert.is.equal(2, b.max.x)
|
||||
assert.is.equal(3, b.max.y)
|
||||
end)
|
||||
|
||||
it("queries a bound2 center", function()
|
||||
local a = bound2(vec2(1,2), vec2(3,4))
|
||||
local v = a:center()
|
||||
assert.is.equal(2, v.x)
|
||||
assert.is.equal(3, v.y)
|
||||
end)
|
||||
|
||||
it("sets a bound2 center", function()
|
||||
local a = bound2(vec2(1,2), vec2(3,4))
|
||||
local b = a:with_center(vec2(1,1))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.max.x)
|
||||
assert.is.equal(4, a.max.y)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(0, b.min.y)
|
||||
assert.is.equal(2, b.max.x)
|
||||
assert.is.equal(2, b.max.y)
|
||||
end)
|
||||
|
||||
it("sets a bound2 size centered", function()
|
||||
local a = bound2(vec2(1,2), vec2(3,4))
|
||||
local b = a:with_size_centered(vec2(4,4))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.max.x)
|
||||
assert.is.equal(4, a.max.y)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(1, b.min.y)
|
||||
assert.is.equal(4, b.max.x)
|
||||
assert.is.equal(5, b.max.y)
|
||||
end)
|
||||
|
||||
it("insets a bound2", function()
|
||||
local a = bound2(vec2(1,2), vec2(5,10))
|
||||
local b = a:inset(vec2(1,2))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(10, a.max.y)
|
||||
|
||||
assert.is.equal(2, b.min.x)
|
||||
assert.is.equal(4, b.min.y)
|
||||
assert.is.equal(4, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
end)
|
||||
|
||||
it("outsets a bound2", function()
|
||||
local a = bound2(vec2(1,2), vec2(5,6))
|
||||
local b = a:outset(vec2(1,2))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(6, a.max.y)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(0, b.min.y)
|
||||
assert.is.equal(6, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
end)
|
||||
|
||||
it("offsets a bound2", function()
|
||||
local a = bound2(vec2(1,2), vec2(5,6))
|
||||
local b = a:offset(vec2(1,2))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(6, a.max.y)
|
||||
|
||||
assert.is.equal(2, b.min.x)
|
||||
assert.is.equal(4, b.min.y)
|
||||
assert.is.equal(6, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
end)
|
||||
|
||||
it("tests for points inside bound2", function()
|
||||
local a = bound2(vec2(1,2), vec2(4,5))
|
||||
|
||||
assert.is_true(a:contains(vec2(2,3)))
|
||||
assert.is_not_true(a:contains(vec2(0,3)))
|
||||
assert.is_not_true(a:contains(vec2(5,3)))
|
||||
assert.is_not_true(a:contains(vec2(2,1)))
|
||||
assert.is_not_true(a:contains(vec2(2,6)))
|
||||
assert.is_not_true(a:contains(vec2(2,3)))
|
||||
assert.is_not_true(a:contains(vec2(2,3)))
|
||||
end)
|
||||
|
||||
it("checks for bound2.zero", function()
|
||||
assert.is.equal(0, bound2.zero.min.x)
|
||||
assert.is.equal(0, bound2.zero.min.y)
|
||||
assert.is.equal(0, bound2.zero.max.x)
|
||||
assert.is.equal(0, bound2.zero.max.y)
|
||||
end)
|
||||
end)
|
219
spec/bound3_spec.lua
Normal file
219
spec/bound3_spec.lua
Normal file
@ -0,0 +1,219 @@
|
||||
local bound3 = require "modules.bound3"
|
||||
local vec3 = require "modules.vec3"
|
||||
local DBL_EPSILON = require("modules.constants").DBL_EPSILON
|
||||
|
||||
describe("bound3:", function()
|
||||
it("creates an empty bound3", function()
|
||||
local a = bound3()
|
||||
assert.is.equal(0, a.min.x)
|
||||
assert.is.equal(0, a.min.y)
|
||||
assert.is.equal(0, a.min.z)
|
||||
assert.is.equal(0, a.max.x)
|
||||
assert.is.equal(0, a.max.y)
|
||||
assert.is.equal(0, a.max.z)
|
||||
end)
|
||||
|
||||
it("creates a bound3 from vec3s", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(4,5,6))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
assert.is.equal(6, a.max.z)
|
||||
end)
|
||||
|
||||
it("creates a bound3 using new()", function()
|
||||
local a = bound3.new(vec3(1,2,3), vec3(4,5,6))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
assert.is.equal(6, a.max.z)
|
||||
end)
|
||||
|
||||
it("creates a bound3 using at()", function()
|
||||
local a = bound3.at(vec3(4,5,6), vec3(1,2,3))
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
assert.is.equal(6, a.max.z)
|
||||
end)
|
||||
|
||||
it("clones a bound3", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(4,5,6))
|
||||
local b = a:clone()
|
||||
a.max = new vec3(9,9,9)
|
||||
assert.is.equal(a.min, b.min)
|
||||
assert.is.not_equal(a.max, b.max)
|
||||
end)
|
||||
|
||||
it("uses bound3 check()", function()
|
||||
local a = bound3(vec3(4,2,6), vec3(1,5,3)):check()
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
assert.is.equal(6, a.max.z)
|
||||
end)
|
||||
|
||||
it("queries a bound3 size", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(4,6,8))
|
||||
local v = a:size()
|
||||
local r = a:radius()
|
||||
assert.is.equal(3, v.x)
|
||||
assert.is.equal(4, v.y)
|
||||
assert.is.equal(5, v.z)
|
||||
|
||||
assert.is.equal(1.5, r.x)
|
||||
assert.is.equal(2, r.y)
|
||||
assert.is.equal(2.5, r.z)
|
||||
end)
|
||||
|
||||
it("sets a bound3 size", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(4,5,6))
|
||||
local b = a:with_size(vec3(1,1,1))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(4, a.max.x)
|
||||
assert.is.equal(5, a.max.y)
|
||||
assert.is.equal(6, a.max.z)
|
||||
|
||||
assert.is.equal(1, b.min.x)
|
||||
assert.is.equal(2, b.min.y)
|
||||
assert.is.equal(3, b.min.z)
|
||||
assert.is.equal(2, b.max.x)
|
||||
assert.is.equal(3, b.max.y)
|
||||
assert.is.equal(4, b.max.z)
|
||||
end)
|
||||
|
||||
it("queries a bound3 center", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(3,4,5))
|
||||
local v = a:center()
|
||||
assert.is.equal(2, v.x)
|
||||
assert.is.equal(3, v.y)
|
||||
assert.is.equal(4, v.z)
|
||||
end)
|
||||
|
||||
it("sets a bound3 center", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(3,4,5))
|
||||
local b = a:with_center(vec3(1,1,1))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(3, a.max.x)
|
||||
assert.is.equal(4, a.max.y)
|
||||
assert.is.equal(5, a.max.z)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(0, b.min.y)
|
||||
assert.is.equal(0, b.min.z)
|
||||
assert.is.equal(2, b.max.x)
|
||||
assert.is.equal(2, b.max.y)
|
||||
assert.is.equal(2, b.max.z)
|
||||
end)
|
||||
|
||||
it("sets a bound3 size centered", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(3,4,5))
|
||||
local b = a:with_size_centered(vec3(4,4,4))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(3, a.max.x)
|
||||
assert.is.equal(4, a.max.y)
|
||||
assert.is.equal(5, a.max.z)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(1, b.min.y)
|
||||
assert.is.equal(2, b.min.z)
|
||||
assert.is.equal(4, b.max.x)
|
||||
assert.is.equal(5, b.max.y)
|
||||
assert.is.equal(6, b.max.z)
|
||||
end)
|
||||
|
||||
it("insets a bound3", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(5,10,11))
|
||||
local b = a:inset(vec3(1,2,3))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(10, a.max.y)
|
||||
assert.is.equal(11, a.max.z)
|
||||
|
||||
assert.is.equal(2, b.min.x)
|
||||
assert.is.equal(4, b.min.y)
|
||||
assert.is.equal(6, b.min.z)
|
||||
assert.is.equal(4, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
assert.is.equal(8, b.max.z)
|
||||
end)
|
||||
|
||||
it("outsets a bound3", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(5,6,7))
|
||||
local b = a:outset(vec3(1,2,3))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(6, a.max.y)
|
||||
assert.is.equal(7, a.max.z)
|
||||
|
||||
assert.is.equal(0, b.min.x)
|
||||
assert.is.equal(0, b.min.y)
|
||||
assert.is.equal(0, b.min.z)
|
||||
assert.is.equal(6, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
assert.is.equal(10, b.max.z)
|
||||
end)
|
||||
|
||||
it("offsets a bound3", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(5,6,7))
|
||||
local b = a:offset(vec3(1,2,3))
|
||||
|
||||
assert.is.equal(1, a.min.x)
|
||||
assert.is.equal(2, a.min.y)
|
||||
assert.is.equal(3, a.min.z)
|
||||
assert.is.equal(5, a.max.x)
|
||||
assert.is.equal(6, a.max.y)
|
||||
assert.is.equal(7, a.max.z)
|
||||
|
||||
assert.is.equal(2, b.min.x)
|
||||
assert.is.equal(4, b.min.y)
|
||||
assert.is.equal(6, b.min.z)
|
||||
assert.is.equal(6, b.max.x)
|
||||
assert.is.equal(8, b.max.y)
|
||||
assert.is.equal(10, b.max.z)
|
||||
end)
|
||||
|
||||
it("tests for points inside bound3", function()
|
||||
local a = bound3(vec3(1,2,3), vec3(4,5,6))
|
||||
|
||||
assert.is_true(a:contains(vec3(2,3,4)))
|
||||
assert.is_not_true(a:contains(vec3(0,3,4)))
|
||||
assert.is_not_true(a:contains(vec3(5,3,4)))
|
||||
assert.is_not_true(a:contains(vec3(2,1,4)))
|
||||
assert.is_not_true(a:contains(vec3(2,6,4)))
|
||||
assert.is_not_true(a:contains(vec3(2,3,2)))
|
||||
assert.is_not_true(a:contains(vec3(2,3,7)))
|
||||
end)
|
||||
|
||||
it("checks for bound3.zero", function()
|
||||
assert.is.equal(0, bound3.zero.min.x)
|
||||
assert.is.equal(0, bound3.zero.min.y)
|
||||
assert.is.equal(0, bound3.zero.min.z)
|
||||
assert.is.equal(0, bound3.zero.max.x)
|
||||
assert.is.equal(0, bound3.zero.max.y)
|
||||
assert.is.equal(0, bound3.zero.max.z)
|
||||
end)
|
||||
end)
|
Loading…
x
Reference in New Issue
Block a user