Manual merge PR#52

This commit is contained in:
mcc 2020-05-03 12:50:42 -04:00
commit f7497b9bf7
14 changed files with 308 additions and 38 deletions

View File

@ -35,3 +35,26 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# The BSD License (BSD-2-Clause)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,11 @@
-- Functions exported by utils.lua but needed by vec2 or vec3 (which utils.lua requires)
local private = {}
local floor = math.floor
function private.round(value, precision)
if precision then return utils.round(value / precision) * precision end
return value >= 0 and floor(value+0.5) or ceil(value-0.5)
end
return private

View File

@ -62,6 +62,22 @@ function bound2.at(a, b) -- "bounded by". b may be nil
end
end
--- Extend bound to include point
-- @tparam bound2 a bound
-- @tparam vec2 point to include
-- @treturn bound2 Bound covering current min, current max and new point
function bound2.extend(a, center)
return bound2.new(a.min:component_min(center), a.max:component_max(center))
end
--- Extend bound to entirety of other bound
-- @tparam bound2 a bound
-- @tparam bound2 bound to cover
-- @treturn bound2 Bound covering current min and max of each bound in the pair
function bound2.extend_bound(a, b)
return a:extend(b.min):extend(b.max)
end
--- Get size of bounding box as a vector
-- @tparam bound2 a bound
-- @treturn vec2 Vector spanning min to max points
@ -152,6 +168,14 @@ function bound2.contains(a, v)
and a.max.x >= v.x and a.max.y >= v.y
end
-- Round all components of all vectors to nearest int (or other precision).
-- @tparam vec3 a bound to round.
-- @tparam precision Digits after the decimal (round number if unspecified)
-- @treturn vec3 Rounded bound
function bound2.round(a, precision)
return bound2.new(a.min:round(precision), a.max.round(precision))
end
--- Return a formatted string.
-- @tparam bound2 a bound to be turned into a string
-- @treturn string formatted
@ -167,7 +191,9 @@ function bound2_mt.__call(_, a, b)
end
if status then
ffi.metatype(new, bound2_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, bound2_mt)
end, function() end)
end
return setmetatable({}, bound2_mt)

View File

@ -62,6 +62,22 @@ function bound3.at(a, b) -- "bounded by". b may be nil
end
end
--- Extend bound to include point
-- @tparam bound3 a bound
-- @tparam vec3 point to include
-- @treturn bound3 Bound covering current min, current max and new point
function bound3.extend(a, center)
return bound3.new(a.min:component_min(center), a.max:component_max(center))
end
--- Extend bound to entirety of other bound
-- @tparam bound3 a bound
-- @tparam bound3 bound to cover
-- @treturn bound3 Bound covering current min and max of each bound in the pair
function bound3.extend_bound(a, b)
return a:extend(b.min):extend(b.max)
end
--- Get size of bounding box as a vector
-- @tparam bound3 a bound
-- @treturn vec3 Vector spanning min to max points
@ -152,6 +168,14 @@ function bound3.contains(a, v)
and a.max.x >= v.x and a.max.y >= v.y and a.max.z >= v.z
end
-- Round all components of all vectors to nearest int (or other precision).
-- @tparam vec3 a bound to round.
-- @tparam precision Digits after the decimal (round number if unspecified)
-- @treturn vec3 Rounded bound
function bound3.round(a, precision)
return bound3.new(a.min:round(precision), a.max:round(precision))
end
--- Return a formatted string.
-- @tparam bound3 a bound to be turned into a string
-- @treturn string formatted
@ -167,7 +191,9 @@ function bound3_mt.__call(_, a, b)
end
if status then
ffi.metatype(new, bound3_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, bound3_mt)
end, function() end)
end
return setmetatable({}, bound3_mt)

View File

@ -542,10 +542,10 @@ function mat4.look_at(out, a, eye, look_at, up)
out[10] = y_axis.z
out[11] = z_axis.z
out[12] = 0
out[13] = 0
out[14] = 0
out[15] = 0
out[16] = 1
out[13] = -out[ 1]*eye.x - out[4+1]*eye.y - out[8+1]*eye.z
out[14] = -out[ 2]*eye.x - out[4+2]*eye.y - out[8+2]*eye.z
out[15] = -out[ 3]*eye.x - out[4+3]*eye.y - out[8+3]*eye.z
out[16] = -out[ 4]*eye.x - out[4+4]*eye.y - out[8+4]*eye.z + 1
return out
end
@ -665,7 +665,7 @@ function mat4.to_string(a)
return str
end
--- Convert a matrix to vec4s.
--- Convert a matrix to row vec4s.
-- @tparam mat4 a Matrix to be converted
-- @treturn table vec4s
function mat4.to_vec4s(a)
@ -677,6 +677,18 @@ function mat4.to_vec4s(a)
}
end
--- Convert a matrix to col vec4s.
-- @tparam mat4 a Matrix to be converted
-- @treturn table vec4s
function mat4.to_vec4s_cols(a)
return {
{ a[1], a[5], a[9], a[13] },
{ a[2], a[6], a[10], a[14] },
{ a[3], a[7], a[11], a[15] },
{ a[4], a[8], a[12], a[16] }
}
end
-- http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/
--- Convert a matrix to a quaternion.
-- @tparam mat4 a Matrix to be converted
@ -853,7 +865,9 @@ function mat4_mt.__mul(a, b)
end
if status then
ffi.metatype(new, mat4_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, mat4_mt)
end, function() end)
end
return setmetatable({}, mat4_mt)

View File

@ -465,7 +465,9 @@ function quat_mt.__pow(a, n)
end
if status then
ffi.metatype(new, quat_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, quat_mt)
end, function() end)
end
return setmetatable({}, quat_mt)

View File

@ -4,6 +4,7 @@
local modules = (...): gsub('%.[^%.]+$', '') .. "."
local vec2 = require(modules .. "vec2")
local vec3 = require(modules .. "vec3")
local private = require(modules .. "_private_utils")
local abs = math.abs
local ceil = math.ceil
local floor = math.floor
@ -103,10 +104,7 @@ end
-- @param value
-- @param precision
-- @return number
function utils.round(value, precision)
if precision then return utils.round(value / precision) * precision end
return value >= 0 and floor(value+0.5) or ceil(value-0.5)
end
utils.round = private.round
--- Wrap `value` around if it exceeds `limit`.
-- @param value

View File

@ -3,6 +3,7 @@
local modules = (...):gsub('%.[^%.]+$', '') .. "."
local vec3 = require(modules .. "vec3")
local private = require(modules .. "_private_utils")
local acos = math.acos
local atan2 = math.atan2
local sqrt = math.sqrt
@ -321,6 +322,14 @@ function vec2.to_polar(a)
return radius, theta
end
-- Round all components to nearest int (or other precision).
-- @tparam vec2 a Vector to round.
-- @tparam precision Digits after the decimal (round numebr if unspecified)
-- @treturn vec2 Rounded vector
function vec2.round(a, precision)
return vec2.new(private.round(a.x, precision), private.round(a.y, precision))
end
-- Negate x axis only of vector.
-- @tparam vec2 a Vector to x-flip.
-- @treturn vec2 x-flipped vector
@ -395,7 +404,9 @@ function vec2_mt.__div(a, b)
end
if status then
ffi.metatype(new, vec2_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, vec2_mt)
end, function() end)
end
return setmetatable({}, vec2_mt)

View File

@ -1,6 +1,8 @@
--- A 3 component vector.
-- @module vec3
local modules = (...):gsub('%.[^%.]+$', '') .. "."
local private = require(modules .. "_private_utils")
local sqrt = math.sqrt
local cos = math.cos
local sin = math.sin
@ -254,6 +256,14 @@ function vec3.lerp(a, b, s)
return a + (b - a) * s
end
-- Round all components to nearest int (or other precision).
-- @tparam vec3 a Vector to round.
-- @tparam precision Digits after the decimal (round numebr if unspecified)
-- @treturn vec3 Rounded vector
function vec3.round(a, precision)
return vec3.new(private.round(a.x, precision), private.round(a.y, precision), private.round(a.z, precision))
end
--- Unpack a vector into individual components.
-- @tparam vec3 a Vector to unpack
-- @treturn number x
@ -382,7 +392,9 @@ function vec3_mt.__div(a, b)
end
if status then
ffi.metatype(new, vec3_mt)
xpcall(function() -- Allow this to silently fail; assume failure means someone messed with package.loaded
ffi.metatype(new, vec3_mt)
end, function() end)
end
return setmetatable({}, vec3_mt)

View File

@ -38,7 +38,7 @@ describe("bound2:", function()
it("clones a bound2", function()
local a = bound2(vec2(1,2), vec2(4,5))
local b = a:clone()
a.max = new vec2(9,9)
a.max = vec2.new(9,9)
assert.is.equal(a.min, b.min)
assert.is.not_equal(a.max, b.max)
end)
@ -162,15 +162,60 @@ describe("bound2:", function()
it("tests for points inside bound2", function()
local a = bound2(vec2(1,2), vec2(4,5))
assert.is_true(a:contains(vec2(1,2)))
assert.is_true(a:contains(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("rounds a bound2", function()
local a = bound2(vec2(1.1,1.9), vec2(3.9,5.1)):round()
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("extends a bound2 with a point", function()
local min = vec2(1,2)
local max = vec2(4,5)
local downright = vec2(8,8)
local downleft = vec2(-4,8)
local top = vec2(2, 0)
local a = bound2(min, max)
local temp
temp = a:extend(downright)
assert.is_true(a.min == min and a.max == max)
assert.is_true(temp.min == min and temp.max == downright)
temp = a:extend(downleft)
assert.is_true(temp.min == vec2(-4,2) and temp.max == vec2(4,8))
temp = a:extend(top)
assert.is_true(temp.min == vec2(1,0) and temp.max == max)
end)
it("extends a bound with another bound", function()
local min = vec2(1,2)
local max = vec2(4,5)
local leftexpand = bound2.new(vec2(0,0), vec2(1.5, 6))
local rightexpand = bound2.new(vec2(1.5,0), vec2(5, 6))
local a = bound2(min, max)
local temp
temp = a:extend_bound(leftexpand)
assert.is_equal(temp.min, vec2(0,0))
assert.is_equal(temp.max, vec2(4,6))
temp = temp:extend_bound(rightexpand)
assert.is_equal(temp.min, vec2(0,0))
assert.is_equal(temp.max, vec2(5,6))
end)
it("checks for bound2.zero", function()
assert.is.equal(0, bound2.zero.min.x)
assert.is.equal(0, bound2.zero.min.y)

View File

@ -46,7 +46,7 @@ describe("bound3:", function()
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)
a.max = vec3.new(9,9,9)
assert.is.equal(a.min, b.min)
assert.is.not_equal(a.max, b.max)
end)
@ -199,6 +199,8 @@ describe("bound3:", function()
it("tests for points inside bound3", function()
local a = bound3(vec3(1,2,3), vec3(4,5,6))
assert.is_true(a:contains(vec3(1,2,3)))
assert.is_true(a:contains(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)))
@ -208,6 +210,53 @@ describe("bound3:", function()
assert.is_not_true(a:contains(vec3(2,3,7)))
end)
it("rounds a bound3", function()
local a = bound3(vec3(1.1,1.9,3), vec3(3.9,5.1,6)):round()
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("extends a bound3 with a point", function()
local min = vec3(1,2,6)
local max = vec3(4,5,9)
local downright = vec3(8,8,10)
local downleft = vec3(-4,8,10)
local top = vec3(2, 0, 7)
local a = bound3(min, max)
local temp
temp = a:extend(downright)
assert.is_true(a.min == min and a.max == max)
assert.is_true(temp.min == min and temp.max == downright)
temp = a:extend(downleft)
assert.is_true(temp.min == vec3(-4,2,6) and temp.max == vec3(4,8,10))
temp = a:extend(top)
assert.is_true(temp.min == vec3(1,0,6) and temp.max == max)
end)
it("extends a bound with another bound", function()
local min = vec3(1,2,3)
local max = vec3(4,5,6)
local leftexpand = bound3.new(vec3(0,0,4), vec3(1.5,6,5))
local rightexpand = bound3.new(vec3(1.5,0,1), vec3(5,6,7))
local a = bound3(min, max)
local temp
temp = a:extend_bound(leftexpand)
assert.is_equal(temp.min, vec3(0,0,3))
assert.is_equal(temp.max, vec3(4,6,6))
temp = temp:extend_bound(rightexpand)
assert.is_equal(temp.min, vec3(0,0,1))
assert.is_equal(temp.max, vec3(5,6,7))
end)
it("checks for bound3.zero", function()
assert.is.equal(0, bound3.zero.min.x)
assert.is.equal(0, bound3.zero.min.y)

View File

@ -322,7 +322,12 @@ describe("mat4:", function()
end)
it("converts a matrix to vec4s", function()
local a = mat4()
local a = mat4 {
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16
}
local v = a:to_vec4s()
assert.is_true(type(v) == "table")
assert.is_true(type(v[1]) == "table")
@ -330,25 +335,60 @@ describe("mat4:", function()
assert.is_true(type(v[3]) == "table")
assert.is_true(type(v[4]) == "table")
assert.is.equal(1, v[1][1])
assert.is.equal(0, v[1][2])
assert.is.equal(0, v[1][3])
assert.is.equal(0, v[1][4])
assert.is.equal(1, v[1][1])
assert.is.equal(2, v[1][2])
assert.is.equal(3, v[1][3])
assert.is.equal(4, v[1][4])
assert.is.equal(0, v[2][1])
assert.is.equal(1, v[2][2])
assert.is.equal(0, v[2][3])
assert.is.equal(0, v[2][4])
assert.is.equal(5, v[2][1])
assert.is.equal(6, v[2][2])
assert.is.equal(7, v[2][3])
assert.is.equal(8, v[2][4])
assert.is.equal(0, v[3][1])
assert.is.equal(0, v[3][2])
assert.is.equal(1, v[3][3])
assert.is.equal(0, v[3][4])
assert.is.equal(9, v[3][1])
assert.is.equal(10, v[3][2])
assert.is.equal(11, v[3][3])
assert.is.equal(12, v[3][4])
assert.is.equal(0, v[4][1])
assert.is.equal(0, v[4][2])
assert.is.equal(0, v[4][3])
assert.is.equal(1, v[4][4])
assert.is.equal(13, v[4][1])
assert.is.equal(14, v[4][2])
assert.is.equal(15, v[4][3])
assert.is.equal(16, v[4][4])
end)
it("converts a matrix to vec4s, column-wise", function()
local a = mat4 {
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16
}
local v = a:to_vec4s_cols()
assert.is_true(type(v) == "table")
assert.is_true(type(v[1]) == "table")
assert.is_true(type(v[2]) == "table")
assert.is_true(type(v[3]) == "table")
assert.is_true(type(v[4]) == "table")
assert.is.equal(1, v[1][1])
assert.is.equal(5, v[1][2])
assert.is.equal(9, v[1][3])
assert.is.equal(13, v[1][4])
assert.is.equal(2, v[2][1])
assert.is.equal(6, v[2][2])
assert.is.equal(10, v[2][3])
assert.is.equal(14, v[2][4])
assert.is.equal(3, v[3][1])
assert.is.equal(7, v[3][2])
assert.is.equal(11, v[3][3])
assert.is.equal(15, v[3][4])
assert.is.equal(4, v[4][1])
assert.is.equal(8, v[4][2])
assert.is.equal(12, v[4][3])
assert.is.equal(16, v[4][4])
end)
it("converts a matrix to a quaternion", function()

View File

@ -172,8 +172,8 @@ describe("vec2:", function()
local a = vec2(3, 5)
local r, t = a:to_polar()
local b = vec2.from_cartesian(r, t)
assert.is.equal(a.x, b.x)
assert.is.equal(a.y, b.y)
assert.is_true(abs(a.x - b.x) <= DBL_EPSILON*2) -- Allow 2X epsilon error because there were 2 operations.
assert.is_true(abs(a.y - b.y) <= DBL_EPSILON*2)
end)
it("gets a perpendicular vector", function()
@ -189,6 +189,12 @@ describe("vec2:", function()
assert.is.equal("(+0.000,+0.000)", b)
end)
it("rounds a 2-vector", function()
local a = vec2(1.1,1.9):round()
assert.is.equal(a.x, 1)
assert.is.equal(a.y, 2)
end)
it("flips a 2-vector", function()
local a = vec2(1,2)
local temp = a:flip_x()

View File

@ -203,6 +203,13 @@ describe("vec3:", function()
assert.is.equal("(+0.000,+0.000,+0.000)", b)
end)
it("rounds a 3-vector", function()
local a = vec3(1.1,1.9,3):round()
assert.is.equal(a.x, 1)
assert.is.equal(a.y, 2)
assert.is.equal(a.z, 3)
end)
it("flips a 3-vector", function()
local a = vec3(1,2,3)
local temp = a:flip_x()