2012-08-02 10:52:21 +00:00
|
|
|
-- Geometry module for Lunatic.
|
|
|
|
|
|
|
|
local ffi = require("ffi")
|
|
|
|
local math = require("math")
|
|
|
|
|
|
|
|
local type = type
|
2012-12-03 18:24:25 +00:00
|
|
|
local error = error
|
2012-08-02 10:52:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
module(...)
|
|
|
|
|
|
|
|
|
2013-01-26 17:07:53 +00:00
|
|
|
local dvec2_t = ffi.typeof("struct { double x, y; }")
|
|
|
|
local dvec3_t = ffi.typeof("struct { double x, y, z; }")
|
|
|
|
|
2012-08-02 10:52:21 +00:00
|
|
|
|
|
|
|
local vec2_
|
2012-10-07 15:26:24 +00:00
|
|
|
local vec2_mt = {
|
2012-08-02 10:52:21 +00:00
|
|
|
__add = function(a, b) return vec2_(a.x+b.x, a.y+b.y) end,
|
|
|
|
__sub = function(a, b) return vec2_(a.x-b.x, a.y-b.y) end,
|
2012-12-03 18:24:25 +00:00
|
|
|
__unm = function(a) return vec2_(-a.x, -a.y) end,
|
2012-08-02 10:52:21 +00:00
|
|
|
|
|
|
|
__mul = function(a,b)
|
|
|
|
if (type(a)=="number") then
|
|
|
|
return vec2_(a*b.x, a*b.y)
|
|
|
|
end
|
|
|
|
|
2012-12-03 18:24:25 +00:00
|
|
|
if (type(b)~="number") then
|
|
|
|
error("number expected in vec2 multiplication", 2)
|
|
|
|
end
|
2012-08-02 10:52:21 +00:00
|
|
|
return vec2_(a.x*b, a.y*b)
|
|
|
|
end,
|
|
|
|
|
|
|
|
__div = function(a,b)
|
2012-12-03 18:24:25 +00:00
|
|
|
if (type(b)~="number") then
|
|
|
|
error("number expected in vec2 division", 2)
|
|
|
|
end
|
2012-08-02 10:52:21 +00:00
|
|
|
return vec2_(a.x/b, a.y/b)
|
|
|
|
end,
|
|
|
|
|
|
|
|
__eq = function(a,b)
|
2013-01-26 17:07:53 +00:00
|
|
|
-- XXX: will error if <a> is not a ctype (can only happen if __eq was
|
|
|
|
-- called by <b>)
|
2012-11-03 19:32:35 +00:00
|
|
|
return (ffi.istype(a,b) and a.x==b.x and a.y==b.y)
|
2012-08-02 10:52:21 +00:00
|
|
|
end,
|
2012-11-03 19:32:35 +00:00
|
|
|
|
2012-08-02 10:52:21 +00:00
|
|
|
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
|
|
|
|
|
|
|
|
__tostring = function(a) return "vec2("..a.x..", "..a.y..")" end,
|
|
|
|
|
|
|
|
__index = {
|
|
|
|
lensq = function(a) return a.x*a.x + a.y*a.y end,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2012-10-07 15:26:24 +00:00
|
|
|
local vec3_
|
|
|
|
local vec3_mt = {
|
|
|
|
__add = function(a, b) return vec3_(a.x+b.x, a.y+b.y, a.z+b.z) end,
|
|
|
|
__sub = function(a, b) return vec3_(a.x-b.x, a.y-b.y, a.z-b.z) end,
|
2012-12-03 18:24:25 +00:00
|
|
|
__unm = function(a) return vec3_(-a.x, -a.y, -a.z) end,
|
2012-10-07 15:26:24 +00:00
|
|
|
|
|
|
|
__mul = function(a,b)
|
|
|
|
if (type(a)=="number") then
|
|
|
|
return vec3_(a*b.x, a*b.y, a*b.z)
|
|
|
|
end
|
|
|
|
|
2012-12-03 18:24:25 +00:00
|
|
|
if (type(b)~="number") then
|
|
|
|
error("number expected in vec3 multiplication", 2)
|
|
|
|
end
|
2012-10-07 15:26:24 +00:00
|
|
|
return vec2_(a.x*b, a.y*b, a.z*b)
|
|
|
|
end,
|
|
|
|
|
|
|
|
__div = function(a,b)
|
2012-12-03 18:24:25 +00:00
|
|
|
if (type(b)~="number") then
|
|
|
|
error("number expected in vec3 division", 2)
|
|
|
|
end
|
2012-10-07 15:26:24 +00:00
|
|
|
return vec2_(a.x/b, a.y/b, a.z/b)
|
|
|
|
end,
|
2012-11-03 19:32:35 +00:00
|
|
|
|
2012-10-07 15:26:24 +00:00
|
|
|
__eq = function(a,b)
|
2013-01-26 17:07:53 +00:00
|
|
|
-- XXX: see vec2
|
2012-11-03 19:32:35 +00:00
|
|
|
return (ffi.istype(a,b) and a.x==b.x and a.y==b.y and a.z==b.z)
|
2012-10-07 15:26:24 +00:00
|
|
|
end,
|
2012-11-03 19:32:35 +00:00
|
|
|
|
2012-10-07 15:26:24 +00:00
|
|
|
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y + a.z*a.z) end,
|
|
|
|
|
|
|
|
__tostring = function(a) return "vec3("..a.x..", "..a.y..", "..a.z..")" end,
|
|
|
|
|
|
|
|
__index = {
|
|
|
|
lensq = function(a) return a.x*a.x + a.y*a.y + a.z*a.z end,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2012-08-02 10:52:21 +00:00
|
|
|
-- VEC2 user data constructor.
|
2012-10-07 15:26:24 +00:00
|
|
|
-- * vec2(<table>), <table> should be indexable with "x" and "y"
|
2012-08-02 10:52:21 +00:00
|
|
|
-- * vec2(x, y), assuming that x and y are numbers
|
2013-01-26 17:07:53 +00:00
|
|
|
vec2_ = ffi.metatype(dvec2_t, vec2_mt)
|
2012-08-02 10:52:21 +00:00
|
|
|
vec2 = vec2_
|
|
|
|
|
|
|
|
-- Returns a vec2 from anything indexable with "x" and "y"
|
2012-10-07 15:26:24 +00:00
|
|
|
-- (vec2(t) works if t is such a table, but not if it's a vec2 or other cdata)
|
|
|
|
function tovec2(t) return vec2(t.x, t.y) end
|
|
|
|
|
|
|
|
-- Same for vec3
|
2013-01-26 17:07:53 +00:00
|
|
|
vec3_ = ffi.metatype(dvec3_t, vec3_mt)
|
2012-10-07 15:26:24 +00:00
|
|
|
vec3 = vec3_
|
|
|
|
function tovec3(t) return vec3(t.x, t.y, t.z) end
|
|
|
|
|
|
|
|
-- This has no metamethods, but can be useful for calculations expecting
|
|
|
|
-- integer values, e.g. geom.ivec3(x, y, z) is a reasonable way to round
|
|
|
|
-- a vec3. It can be also used as the RHS to the vec2/vec3 arithmetic
|
|
|
|
-- methods.
|
2013-01-26 17:07:53 +00:00
|
|
|
-- NOTE: We must have a typedef with that exact name, because for
|
|
|
|
-- Lunatic (i.e. not stand-alone), it is a duplicate (and ignored)
|
|
|
|
-- declaration for an already metatype'd type.
|
|
|
|
ffi.cdef "typedef struct { int32_t x, y, z; } vec3_t;"
|
2012-10-07 15:26:24 +00:00
|
|
|
ivec3 = ffi.typeof("vec3_t")
|
|
|
|
|
2012-08-02 10:52:21 +00:00
|
|
|
|
|
|
|
-- Two-element vector cross product.
|
|
|
|
-- Anti-commutative, distributive.
|
|
|
|
function cross2(v, w)
|
|
|
|
return v.y*w.x - v.x*w.y
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
-- Finds the intersection point of two lines given by
|
|
|
|
-- point a and vector v
|
|
|
|
-- and
|
|
|
|
-- point b and vector w
|
|
|
|
--
|
|
|
|
-- Returns:
|
|
|
|
-- if <TODO>, nil
|
|
|
|
-- if retpoint_p evaluates to a non-true value, coefficients cv and cw such that <TODO>
|
|
|
|
-- else, the intersection point
|
|
|
|
function intersect(a,v, b,w, retpoint_p)
|
|
|
|
local vxw = cross2(v,w)
|
|
|
|
|
|
|
|
if (vxw ~= 0) then
|
|
|
|
local btoa = tovec2(a)-tovec2(b)
|
|
|
|
local cv, cw = cross2(w, btoa)/vxw, cross2(v, btoa)/vxw
|
|
|
|
|
|
|
|
if (retpoint_p) then
|
|
|
|
return tovec2(a)+cv*tovec2(v)
|
|
|
|
else
|
|
|
|
return cv, cw
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- return nil if v and w parallel (or either of them is a point), or if
|
|
|
|
-- they contain NaNs
|
|
|
|
end
|