230 lines
5.3 KiB
Lua
230 lines
5.3 KiB
Lua
local assert = assert -- function() end
|
|
local sqrt, cos, sin, atan2, acos = math.sqrt, math.cos, math.sin, math.atan2, math.acos
|
|
local ffi = require "ffi"
|
|
|
|
ffi.cdef[[
|
|
typedef struct {
|
|
float x, y, z;
|
|
} cpml_vec3;
|
|
]]
|
|
|
|
local vec3 = {}
|
|
local new_vec3 = ffi.typeof("cpml_vec3")
|
|
-- local new_vec3 = ffi.metatype("cpml_vec3", vec3) -- like setmetatable, but awesomer.
|
|
|
|
-- If new is called without specified n, we probably don't want an array.
|
|
function vec3.new(x, y, z)
|
|
return new_vec3(x, y, z)
|
|
end
|
|
|
|
-- results in out
|
|
function vec3.add(out, a, b)
|
|
out.x = a.x + b.x
|
|
out.y = a.y + b.y
|
|
out.z = a.z + b.z
|
|
end
|
|
|
|
-- results in out
|
|
function vec3.sub(out, a, b)
|
|
out.x = a.x - b.x
|
|
out.y = a.y - b.y
|
|
out.z = a.z - b.z
|
|
end
|
|
|
|
-- results in out
|
|
function vec3.mul(out, a, b)
|
|
out.x = a.x * b.x
|
|
out.y = a.y * b.y
|
|
out.z = a.z * b.z
|
|
end
|
|
|
|
-- results in out
|
|
function vec3.div(out, a, b)
|
|
out.x = a.x / b.x
|
|
out.y = a.y / b.y
|
|
out.z = a.z / b.z
|
|
end
|
|
|
|
-- results in out
|
|
function vec3.cross(out, a, b)
|
|
out.x = a.y*b.z - a.z*b.y
|
|
out.y = a.z*b.x - a.x*b.z
|
|
out.z = a.x*b.y - a.y*b.x
|
|
end
|
|
|
|
-- returns float
|
|
function vec3.dot(a, b)
|
|
return a.x * b.x + a.y * b.y + a.z * b.z
|
|
end
|
|
|
|
function vec3.clone(out, a)
|
|
ffi.copy(out, a, ffi.sizeof(out))
|
|
end
|
|
|
|
function vec3.unpack(a)
|
|
return a.x, a.y, a.z
|
|
end
|
|
|
|
function vec3.tostring(a)
|
|
return string.format("(%+0.3f,%+0.3f,%+0.3f)", a.x, a.y, a.z)
|
|
end
|
|
|
|
function vec3.len2(a)
|
|
return a.x * a.x + a.y * a.y + a.z * a.z
|
|
end
|
|
|
|
function vec3.len(a)
|
|
return sqrt(a.x * a.x + a.y * a.y + a.z * a.z)
|
|
end
|
|
|
|
function vec3.normalize(out, a)
|
|
local l = vec3.len(a)
|
|
if l > 0 then
|
|
out.x, out.y, out.z = a.x / l, a.y / l, a.z / l
|
|
end
|
|
end
|
|
|
|
function vec3.rotate(out, a, phi, axis)
|
|
local u = vec3.new(0, 0, 0)
|
|
vec3.normalize(u, axis)
|
|
local c, s = cos(phi), sin(phi)
|
|
|
|
-- Calculate generalized rotation matrix
|
|
local m1 = vec3.new((c + u.x * u.x * (1-c)), (u.x * u.y * (1-c) - u.z * s), (u.x * u.z * (1-c) + u.y * s))
|
|
local m2 = vec3.new((u.y * u.x * (1-c) + u.z * s), (c + u.y * u.y * (1-c)), (u.y * u.z * (1-c) - u.x * s))
|
|
local m3 = vec3.new((u.z * u.x * (1-c) - u.y * s), (u.z * u.y * (1-c) + u.x * s), (c + u.z * u.z * (1-c)))
|
|
|
|
-- Return rotated vector
|
|
out.x = vec3.dot(a, m1)
|
|
out.y = vec3.dot(a, m2)
|
|
out.z = vec3.dot(a, m3)
|
|
end
|
|
|
|
function vec3.dist(a, b)
|
|
local dx = a.x - b.x
|
|
local dy = a.y - b.y
|
|
local dz = a.z - b.z
|
|
return sqrt(dx * dx + dy * dy + dz * dz)
|
|
end
|
|
|
|
function vec3.dist2(a, b)
|
|
local dx = a.x - b.x
|
|
local dy = a.y - b.y
|
|
local dz = a.z - b.z
|
|
return (dx * dx + dy * dy + dz * dz)
|
|
end
|
|
|
|
--[[
|
|
local function isvector(v)
|
|
return type(v) == 'table' and type(v.x) == 'number' and type(v.y) == 'number' and type(v.z) == 'number'
|
|
end
|
|
|
|
function vector.__unm(a)
|
|
return new(-a.x, -a.y, -a.z)
|
|
end
|
|
|
|
function vector.__eq(a,b)
|
|
return a.x == b.x and a.y == b.y and a.z == b.z
|
|
end
|
|
|
|
function vector.__lt(a,b)
|
|
-- This is a lexicographical order.
|
|
return a.x < b.x or (a.x == b.x and a.y < b.y) or (a.x == b.x and a.y == b.y and a.z < b.z)
|
|
end
|
|
|
|
function vector.__le(a,b)
|
|
-- This is a lexicographical order.
|
|
return a.x <= b.x and a.y <= b.y and a.z <= b.z
|
|
end
|
|
|
|
function vector:project_on(v)
|
|
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
|
|
-- (self * v) * v / v:len2()
|
|
local s = (self.x * v.x + self.y * v.y + self.z * v.z) / (v.x * v.x + v.y * v.y + v.z * v.z)
|
|
return new(s * v.x, s * v.y, s * v.z)
|
|
end
|
|
|
|
function vector:project_from(v)
|
|
assert(isvector(v), "invalid argument: cannot project vector on " .. type(v))
|
|
-- Does the reverse of projectOn.
|
|
local s = (v.x * v.x + v.y * v.y + v.z * v.z) / (self.x * v.x + self.y * v.y + self.z * v.z)
|
|
return new(s * v.x, s * v.y, s * v.z)
|
|
end
|
|
|
|
function vector:mirror_on(v)
|
|
assert(isvector(v), "invalid argument: cannot mirror vector on " .. type(v))
|
|
-- 2 * self:projectOn(v) - self
|
|
local s = 2 * (self.x * v.x + self.y * v.y + self.z * v.z) / (v.x * v.x + v.y * v.y + v.z * v.z)
|
|
return new(s * v.x - self.x, s * v.y - self.y, s * v.z - self.z)
|
|
end
|
|
|
|
-- ref.: http://blog.signalsondisplay.com/?p=336
|
|
function vector:trim_inplace(maxLen)
|
|
local s = maxLen * maxLen / self:len2()
|
|
s = (s > 1 and 1) or math.sqrt(s)
|
|
self.x, self.y, self.z = self.x * s, self.y * s, self.z * s
|
|
return self
|
|
end
|
|
|
|
function vector:angle_to(other)
|
|
-- Only makes sense in 2D.
|
|
if other then
|
|
return atan2(self.y, self.x) - atan2(other.y, other.x)
|
|
end
|
|
return atan2(self.y, self.x)
|
|
end
|
|
|
|
function vector:angle_between(other)
|
|
if other then
|
|
return acos(self*other / (self:len() * other:len()))
|
|
end
|
|
return 0
|
|
end
|
|
|
|
function vector:orientation_to_direction(orientation)
|
|
orientation = orientation or new(0, 1, 0)
|
|
return orientation
|
|
:rotated(self.z, new(0, 0, 1))
|
|
:rotated(self.y, new(0, 1, 0))
|
|
:rotated(self.x, new(1, 0, 0))
|
|
end
|
|
|
|
-- http://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/
|
|
function vector.lerp(a, b, s)
|
|
return a + s * (b - a)
|
|
end
|
|
--]]
|
|
|
|
if ... then
|
|
return vec3
|
|
end
|
|
|
|
--------- bench/test
|
|
do
|
|
local vec3t = vec3
|
|
local vec3_slow = require "vec3"
|
|
local sin = math.sin
|
|
|
|
local result = 0
|
|
local t = os.clock()
|
|
|
|
for i = 1, 10000000 do
|
|
result = vec3_slow(0, 1, 0):rotate(sin(i), vec3_slow(0, 0, 1))
|
|
end
|
|
|
|
-- print(result)
|
|
print(vec3t.tostring(result))
|
|
print(string.format("Vec3: %0.8f", os.clock() - t))
|
|
|
|
result = 0
|
|
local t = os.clock()
|
|
|
|
for i = 1, 10000000 do
|
|
result = vec3t.new(0, 1, 0)
|
|
vec3t.rotate(result, result, sin(i), vec3t.new(0, 0, 1))
|
|
end
|
|
|
|
print(vec3t.tostring(result))
|
|
print(string.format("Turbo: %0.8f", os.clock() - t))
|
|
end
|