diff --git a/polymer/eduke32/Makefile b/polymer/eduke32/Makefile
index 4c3f3a4cf..1448f7f2a 100644
--- a/polymer/eduke32/Makefile
+++ b/polymer/eduke32/Makefile
@@ -165,7 +165,8 @@ ifneq (0,$(LUNATIC))
GAMEOBJS+= $(OBJ)/../lpeg.$o # TEMP
GAMEOBJS+= $(OBJ)/luaJIT_BC_con_lang.$o \
- $(OBJ)/luaJIT_BC_lunacon.$o
+ $(OBJ)/luaJIT_BC_lunacon.$o \
+ $(OBJ)/luaJIT_BC_geom.$o
# now, take care of having the necessary symbols (sector, wall, etc.) in the
# executable no matter what the debugging level
diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua
index 6efe7f825..515565b43 100644
--- a/polymer/eduke32/source/lunatic/defs.ilua
+++ b/polymer/eduke32/source/lunatic/defs.ilua
@@ -525,6 +525,12 @@ string.dump = nil
local allowed_modules = {
coroutine=coroutine, bit=bit, table=table, math=math, string=string,
+
+ os = {
+ clock = function() return gv_.gethitickms()/1000 end,
+ },
+
+ geom = require("geom"),
}
local package_loaded = {}
diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist
index 0825a7140..27d4ce937 100644
--- a/polymer/eduke32/source/lunatic/dynsymlist
+++ b/polymer/eduke32/source/lunatic/dynsymlist
@@ -40,6 +40,7 @@ g_player;
luaJIT_BC_lunacon;
luaJIT_BC_con_lang;
+luaJIT_BC_geom;
gethitickms;
};
diff --git a/polymer/eduke32/source/lunatic/geom.lua b/polymer/eduke32/source/lunatic/geom.lua
new file mode 100644
index 000000000..92a605804
--- /dev/null
+++ b/polymer/eduke32/source/lunatic/geom.lua
@@ -0,0 +1,94 @@
+-- Geometry module for Lunatic.
+
+local ffi = require("ffi")
+local math = require("math")
+
+local type = type
+local assert = assert
+
+
+module(...)
+
+
+ffi.cdef[[
+typedef struct { double x, y; } dvec2_t;
+]]
+
+local vec2_
+local mt = {
+ __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,
+ __unm = function(a) return vec2_(-a.x, -b.x) end,
+
+ __mul = function(a,b)
+ if (type(a)=="number") then
+ return vec2_(a*b.x, a*b.y)
+ end
+
+ assert(type(b)=="number")
+ return vec2_(a.x*b, a.y*b)
+ end,
+
+ __div = function(a,b)
+ assert(type(b)=="number")
+ return vec2_(a.x/b, a.y/b)
+ end,
+
+ __eq = function(a,b)
+ return (a.x==b.x and a.y==b.y)
+ end,
+
+ __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,
+ },
+}
+
+-- VEC2 user data constructor.
+-- * vec2(
), the arg should be indexable with "x" and "y"
+-- * vec2(x, y), assuming that x and y are numbers
+vec2_ = ffi.metatype("dvec2_t", mt)
+vec2 = vec2_
+
+-- Returns a vec2 from anything indexable with "x" and "y"
+-- (vec2(t) works if t is such a table, but not if it's a vec2)
+function tovec2(t)
+ return vec2(t.x, t.y)
+end
+
+-- 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 , nil
+-- if retpoint_p evaluates to a non-true value, coefficients cv and cw such that
+-- 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
diff --git a/polymer/eduke32/source/lunatic/test.elua b/polymer/eduke32/source/lunatic/test.elua
index cfa2823a2..730d3db6b 100644
--- a/polymer/eduke32/source/lunatic/test.elua
+++ b/polymer/eduke32/source/lunatic/test.elua
@@ -249,6 +249,8 @@ printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines
local bittest = require "bittest"
bittest.sieve()
+require("test/test_geom")
+
print('---=== END TEST SCRIPT ===---')
-- This will complain about wrong usage of 'error'. In particular,
diff --git a/polymer/eduke32/source/lunatic/test/test_geom.lua b/polymer/eduke32/source/lunatic/test/test_geom.lua
new file mode 100755
index 000000000..9d577ab2b
--- /dev/null
+++ b/polymer/eduke32/source/lunatic/test/test_geom.lua
@@ -0,0 +1,41 @@
+#!/usr/bin/env luajit
+
+local math = require("math")
+local os = require("os")
+
+local geom = require("geom")
+
+
+local N = os.exit and tostring(arg[1]) or 1e6
+
+local A,B = {}, {}
+local V,W = {}, {}
+
+local function randvec()
+ return geom.vec2(math.random(), math.random())
+end
+
+local t1 = os.clock()
+
+-- init random points and vectors
+for i=1,N do
+ A[i] = randvec()
+ B[i] = randvec()
+ V[i] = randvec()
+ W[i] = randvec()
+end
+
+local t2 = os.clock()
+
+local v = geom.vec2(0, 0)
+for i=1,N do
+ v = v + geom.intersect(A[i],V[i], B[i],W[i], true)
+end
+
+local t3 = os.clock()
+
+print(1000*(t2-t1))
+print(1000*(t3-t2))
+print(v)
+
+return {} -- appease Lunatic's require