Merge pull request #30 from mcclure/aabb

Axis-aligned bounding box classes for 2 and 3 dimensions
This commit is contained in:
Colby Klein 2018-02-22 04:09:36 -08:00 committed by GitHub
commit 130fe2aca0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 766 additions and 0 deletions

View File

@ -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
View 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
View 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)

View File

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

View File

@ -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
View 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
View 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)