/* See license.txt in the root of this project. */ /*tex This is just an experiment by Mikael and Hans in the perspective of 3D experiments in \METAPOST\ with the \LUA\ interface. It is also a kind of follow up on the \type {matrix} module from which we took some of the code. So we have a mix of Jeong Dalyoung, Mikael Sundqvist & Hans Hagen code here. More methods might be added. This module is sort of generic but also geared at using on combination with \METAPOST, which is why we have a few public functions. For that we have extra helpers that work on meshes. */ # include # include "libraries/triangles/triangles.h" # define VECTOR_METATABLE "vector" # define MESH_METATABLE "mesh" /* todo: -- maybe also store size (more readable, less clutter) -- extra array for w (doubles) -- userdata lua upvalue for whatever extras */ /*tex We need to be way above EPSILON in triangles.c because otherwise we get too many false positives at that end! */ const double p_epsilon = 0.000001; const double m_epsilon = -0.000001; static inline int iszero(double d) { /* todo: do we need the same in triangles, currently a macro there */ return d > m_epsilon && d < p_epsilon; } static inline int vectorlib_aux_zero_in_column(const vector a, int c) { for (int r = 0; r < a->rows; r++) { if (iszero(a->data[r * a->columns + c])) { return 1; } } return 0; } static inline int vectorlib_aux_zero_in_row(const vector a, int r) { for (int c = 0; c < a->columns; c++) { if (iszero(a->data[r * a->columns + c])) { return 1; } } return 0; } /* let the compiled deal with this: */ /* memcpy(v->data, a->data,a->rows * a->columns * sizeof(double)); */ inline static void vectorlib_aux_copy_data(vector v, const vector a) { for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i]; } } /* We let \LUA\ manage the temporary vectors if needed which sometimes means that we push too many intermediate nil's too but it's harmless. */ static inline vector vectorlib_aux_push(lua_State *L, int r, int c, int s) { if (r >= max_vector_rows || c >= max_vector_columns || r*c > max_vector) { tex_formatted_error("vector lib", "you can have %i rows, %i columns and at most %i entries", max_vector_rows, max_vector_columns, max_vector); return NULL; } else { vector v = lua_newuserdatauv(L, 6 * sizeof(int) + r * c * sizeof(double), 0); if (v && r > 0 && c > 0) { v->rows = r; v->columns = c; v->type = generic_type; v->stacking = s; v->index = 0; v->padding = 0; lua_get_metatablelua(vector_instance); lua_setmetatable(L, -2); } return v; } } static vector vectorlib_aux_maybe_isvector(lua_State *L, int index) { vector v = lua_touserdata(L, index); if (v && lua_getmetatable(L, index)) { lua_get_metatablelua(vector_instance); if (! lua_rawequal(L, -1, -2)) { v = NULL; } lua_pop(L, 2); } return v; } static int vectorlib_isvector(lua_State *L) { lua_pushboolean(L, vectorlib_aux_maybe_isvector(L, 1) ? 1 : 0); return 1; } static int vectorlib_type(lua_State *L) { if (vectorlib_aux_maybe_isvector(L, 1)) { lua_push_key(vector); } else { lua_pushnil(L); } return 1; } /* tex Only |{ { 1,2,3 }, { 4,5,6 } }| here as we can mix. */ static int vectorlib_aux_be_nice(lua_State *L, int index) { /* We know that we have a table. */ int rows = (int) lua_rawlen(L, index); int columns = lua_rawgeti(L, index, 1) == LUA_TTABLE ? (int) lua_rawlen(L, -1) : 0; lua_pop(L, 1); if (rows && columns) { vector v = vectorlib_aux_push(L, rows, columns, 0); for (int r = 0; r < rows; r++) { long target = r * columns; if (lua_rawgeti(L, index, r + 1) == LUA_TTABLE) { for (int c = 0; c < columns; c++) { v->data[target++] = lua_rawgeti(L, -1, c + 1) == LUA_TNUMBER ? lua_tonumber(L, -1) : 0.0; lua_pop(L, 1); } } else { for (int c = 0; c < columns; c++) { v->data[target++] = 0.0; } } lua_pop(L, 1); } return 1; } lua_pushnil(L); return 1; } inline static vector vectorlib_aux_get(lua_State *L, int index) { switch (lua_type(L, index)) { case LUA_TUSERDATA: { vector v = lua_touserdata(L, index); if (v && lua_getmetatable(L, index)) { lua_get_metatablelua(vector_instance); if (! lua_rawequal(L, -1, -2)) { v = NULL; } lua_pop(L, 2); } return v; } case LUA_TTABLE: { vectorlib_aux_be_nice(L, index); if (lua_type(L, -1) == LUA_TUSERDATA) { vector v = lua_touserdata(L, -1); lua_replace(L, index); return v; } else { lua_pop(L, 1); break; } } default: break; } return NULL; } static int vectorlib_new(lua_State *L) { switch (lua_type(L, 1)) { case LUA_TNUMBER: { /* local v = vector.new( 2, 3 ) */ /* local v = vector.new( 2, 3, 1,2,3, 4,5,6 ) */ /* local v = vector.new( 2, 3, { 1,2,3 }, { 4,5,6 } ) */ /* maybe */ /* local v = vector.new( 2, 3, { 1,2,3 , 4,5,6 } ) */ /* maybe */ int t = lua_gettop(L) - 2; vector v = vectorlib_aux_push(L, lmt_optinteger(L, 1, 1), lmt_optinteger(L, 2, 1), 0); if (t) { if (t > v->rows * v->columns) { t = v->rows * v->columns; } for (int i = 0; i < t; i++) { v->data[i] = lua_type(L, i + 3) == LUA_TNUMBER ? lua_tonumber(L, i + 3) : 0.0; } } else { for (int i = 0; i < v->rows * v->columns; i++) { v->data[i] = 0.0; } } return 1; } case LUA_TTABLE: { int n = (int) lua_rawlen(L, 1); int t = lua_rawgeti(L, 1, 1); lua_pop(L, 1); switch (t) { case LUA_TNUMBER: /* local v = vector.new( { 1,2,3 }, { 4,5,6 } ) */ { int columns = n; int rows = lua_gettop(L); if (rows && columns) { vector v = vectorlib_aux_push(L, rows, columns, 0); for (int r = 0; r < rows; r++) { long index = r * columns; if (lua_type(L, r + 1) == LUA_TTABLE) { for (int c = 0; c < columns; c++) { if (lua_rawgeti(L, r + 1, c + 1) == LUA_TNUMBER) { v->data[index++] = lua_tonumber(L, -1); } else { v->data[index++] = 0.0; } lua_pop(L, 1); } } else { for (int c = 0; c < columns; c++) { v->data[index++] = 0.0; } } } return 1; } else { break; } } case LUA_TTABLE: { /* local v = vector.new( { { 1,2,3 }, { 4,5,6 } } ) */ int type = (int) lua_rawgeti(L, 1, 1); int rows = (int) lua_rawlen(L, 1); int columns = 0; switch (type) { case LUA_TTABLE: columns = (int) lua_rawlen(L, -1); break; case LUA_TUSERDATA: { vector a = vectorlib_aux_get(L, 1); if (a) { columns = a->columns; } break; } } lua_pop(L, 1); if (rows && columns) { vector v = vectorlib_aux_push(L, rows, columns, 0); long index = 0; for (int r = 1; r <= rows; r++) { switch (lua_rawgeti(L, 1, r)) { case LUA_TTABLE: { for (int c = 1; c <= columns; c++) { if (lua_rawgeti(L, -1, c) == LUA_TNUMBER) { v->data[index++] = lua_tonumber(L, -1); } else { v->data[index++] = 0.0; } lua_pop(L, 1); } break; } case LUA_TUSERDATA: { vector a = vectorlib_aux_get(L, 1); if (a && a->columns * a->rows == columns) { long source = 0; for (int c = 0; c < columns; c++) { v->data[index++] = a->data[source++]; } break; } else { /* fall through */ } } default: { for (int c = 0; c < columns; c++) { v->data[index++] = 0.0; } break; } } lua_pop(L, 1); } return 1; } else { break; } } default: break; } break; } } lua_pushnil(L); return 0; } static inline int vectorlib_onerow(lua_State *L) { int t = lua_gettop(L); if (t) { vector v = vectorlib_aux_push(L, 1, t, 0); for (int i = 0; i < t; i++) { v->data[i] = lua_tonumber(L, i + 1); } } else { lua_pushnil(L); } return 1; } static inline int vectorlib_onecolumn(lua_State *L) { int t = lua_gettop(L); if (t) { vector v = vectorlib_aux_push(L, t, 1, 0); for (int i = 0; i < t; i++) { v->data[i] = lua_tonumber(L, i + 1); } } else { lua_pushnil(L); } return 1; } vector vectorlib_get(lua_State *L, int index) { if (lua_type(L, index) == LUA_TUSERDATA) { return (vector) luaL_checkudata(L, index, VECTOR_METATABLE); } else { return NULL; } } static int vectorlib_tostring(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { lua_pushfstring(L, "", v->rows, v->columns, v); return 1; } else { return 0; } } static int vectorlib_totable(lua_State *L) { vector v = vectorlib_aux_maybe_isvector(L, 1); if (v) { long source = 0; if (lua_toboolean(L, 2)) { lua_createtable(L, v->rows * v->columns, 0); for (int i = 1; i <= v->rows * v->columns; i++) { lua_pushnumber(L, v->data[source++]); lua_rawseti(L, -2, i); } } else { lua_createtable(L, v->rows, 0); for (int r = 1; r <= v->rows; r++) { lua_createtable(L, v->columns, 0); for (int c = 1; c <= v->columns; c++) { lua_pushnumber(L, v->data[source++]); lua_rawseti(L, -2, c); } lua_rawseti(L, -2, r); } } return 1; } else { return 0; } } static int vectorlib_aux_copy(lua_State *L, int index) { vector a = vectorlib_aux_get(L, index); if (a) { /* could be a mem copy */ vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); v->type = a->type; vectorlib_aux_copy_data(v, a); } else { lua_pushnil(L); } return 1; } static int vectorlib_copy(lua_State *L) { return vectorlib_aux_copy(L, 1); } static int vectorlib_eq(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); int result = 1; if (a && b && a->rows == b->rows && a->columns == b->columns) { /* we can do a memcmp but we can have negative zero etc */ for (int i = 0; i < b->rows * b->columns; i++) { if (a->data[i] != b->data[i]) { result = 0; break; } } } else { result = 0; } lua_pushboolean(L, result); return 1; } static int vectorlib_add(lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) { vector b = vectorlib_aux_get(L, 2); if (b) { double d = lua_tonumber(L, 1); vector v = vectorlib_aux_push(L, b->rows, b->columns, b->stacking); for (int i = 0; i < b->rows * b->columns; i++) { v->data[i] = b->data[i] + d; } } else { lua_pushnil(L); } } else if (lua_type(L, 2) == LUA_TNUMBER) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = lua_tonumber(L, 2); vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] + d; } } else { lua_pushnil(L); } } else { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b && a->rows == b->rows && a->columns == b->columns) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] + b->data[i]; } } else { lua_pushnil(L); } } return 1; } static int vectorlib_sub(lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) { vector b = vectorlib_aux_get(L, 2); if (b) { double d = lua_tonumber(L, 1); vector v = vectorlib_aux_push(L, b->rows, b->columns, b->stacking); for (int i = 0; i < b->rows * b->columns; i++) { v->data[i] = b->data[i] - d; } } else { lua_pushnil(L); } } else if (lua_type(L, 2) == LUA_TNUMBER) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = lua_tonumber(L, 2); vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] - d; } } else { lua_pushnil(L); } } else { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b && a->rows == b->rows && a->columns == b->columns) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] - b->data[i]; } } else { lua_pushnil(L); } } return 1; } static int vectorlib_product(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b && a->columns == b->rows) { vector v = vectorlib_aux_push(L, a->rows, b->columns, b->stacking); /* not a->stacking */ for (int ra = 0; ra < a->rows; ra++) { /* loop over rows a : a[r] */ for (int cb = 0; cb < b->columns; cb++) { /* loop over columns b */ double d = 0; for (int i = 0; i < b->rows; i++) { /* loop over rows b */ d = d + a->data[ra * a->columns + i] * b->data[i * b->columns + cb]; } /* result [ra][cb] */ v->data[ra * b->columns + cb] = d; } } } else { lua_pushnil(L); } return 1; } static int vectorlib_mul(lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) { vector b = vectorlib_aux_get(L, 2); if (b) { double d = lua_tonumber(L, 1); if (d == 1.0) { vectorlib_aux_copy(L, 2); } else { vector v = vectorlib_aux_push(L, b->rows, b->columns, b->stacking); for (int i = 0; i < b->rows * b->columns; i++) { v->data[i] = b->data[i] * d; } } } else { lua_pushnil(L); } } else if (lua_type(L, 2) == LUA_TNUMBER) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = lua_tonumber(L, 2); if (d == 1.0) { vectorlib_aux_copy(L, 1); } else { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] * d; } } } else { lua_pushnil(L); } } else { return vectorlib_product(L); } return 1; } static int vectorlib_div(lua_State *L) { if (lua_type(L, 1) == LUA_TNUMBER) { lua_pushnil(L); } else if (lua_type(L, 2) == LUA_TNUMBER) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = lua_tonumber(L, 2); vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { /* Should we check for d == 0.0 or not here? */ v->data[i] = a->data[i] / d; } } else { lua_pushnil(L); } } else { lua_pushnil(L); } return 1; } static int vectorlib_neg(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = - a->data[i]; } } else { lua_pushnil(L); } return 1; } static int vectorlib_getrow(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { int row = lmt_tointeger(L, 2) - 1; if (row >= 0 && row < v->rows) { long source = row * v->columns; if (lua_toboolean(L, 3)) { long target = 1; lua_createtable(L, v->columns, 0); for (int c = 0; c < v->columns; c++) { lua_pushnumber(L, v->data[source++]); lua_rawseti(L, -2, target++); } return 1; } else { for (int c = 0; c < v->columns; c++) { lua_pushnumber(L, v->data[source++]); } return v->columns; } } } else { lua_pushnil(L); } return 1; } static int vectorlib_getvalue(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { long index = lmt_tolong(L, 2); lua_pushnumber(L, index > 0 && index <= v->rows * v->columns ? v->data[index-1] : 0); } else { lua_pushnil(L); } return 1; } static int vectorlib_setvalue(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { long index = lmt_tolong(L, 2); if (index > 0 && index <= v->rows * v->columns) { v->data[index-1] = lua_type(L, 3) == LUA_TNUMBER ? lua_tonumber(L, 3) : 0; } } else { lua_pushnil(L); } return 1; } static int vectorlib_getlength(lua_State *L) { vector v = vectorlib_aux_get(L, 1); lua_pushinteger(L, v ? v->rows : 0); return 1; } static int vectorlib_setnext(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a && a->index < (a->rows * a->columns)) { int value = 1; for (int c = 0; c < a->columns; c++) { a->data[a->index++] = lua_type(L, ++value) == LUA_TNUMBER ? lua_tonumber(L, value) : 0; } } else { lua_pushnil(L); } return 1; } static int vectorlib_getvaluerc(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int row = lmt_tointeger(L, 2) - 1; int column = lmt_tointeger(L, 3) - 1; if (row >= 0 && row < a->rows && column >= 0 && column < a->columns) { lua_pushnumber(L, a->data[row * a->columns + column]); return 1; } } lua_pushnil(L); return 1; } static int vectorlib_setvaluerc(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int row = lmt_tointeger(L, 2) - 1; int column = lmt_tointeger(L, 3) - 1; if (row >= 0 && row < a->rows && column >= 0 && column < a->columns) { a->data[row * a->columns] = lua_type(L, 4) == LUA_TNUMBER ? lua_tonumber(L, 4) : 0.0; } } else { lua_pushnil(L); } return 1; } /* round floor ceil */ static int vectorlib_identity(lua_State *L) { int n = lmt_tointeger(L, 1); if (n > 0) { vector v = vectorlib_aux_push(L, n, n, 0); v->type = identity_type; for (int i = 0; i < v->rows * v->columns; i++) { v->data[i] = 0.0; } for (int i = 0; i < n; i++) { v->data[i * v->columns + i] = 1.0; } } else { lua_pushnil(L); } return 1; } static int vectorlib_transpose(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { if (a->type == identity_type) { vectorlib_aux_copy(L, 1); } else { // if one column or one row just swap vector v = vectorlib_aux_push(L, a->columns, a->rows, a->stacking); for (int r = 0; r < a->rows; r++) { for (int c = 0; c < a->columns; c++) { v->data[c * a->rows + r] = a->data[r * a->columns + c]; } } } } else { lua_pushnil(L); } return 1; } static int vectorlib_inner(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b && a->rows == b->rows) { double d = 0.0; for (int i = 0; i < a->rows; i++) { d = d + a->data[i] * b->data[i]; } lua_pushnumber(L, d); } else { lua_pushnil(L); } return 1; } static int vectorlib_round(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = round(a->data[i]); } } else { lua_pushnil(L); } return 1; } static int vectorlib_floor(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = floor(a->data[i]); } } else { lua_pushnil(L); } return 1; } static int vectorlib_ceiling(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = ceil(a->data[i]); } } else { lua_pushnil(L); } return 1; } static int vectorlib_truncate(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { if (lua_toboolean(L, 2)) { /* in place */ for (int i = 0; i < a->rows * a->columns; i++) { if (iszero(a->data[i])) { a->data[i] = 0.0; } } } else { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = iszero(a->data[i]) ? 0.0 : a->data[i]; } } } else { lua_pushnil(L); } return 1; } static int vectorlib_aux_uppertriangle(vector v, int sign) { for (int i = 0; i < v->rows - 1; i++) { double pivot = v->data[i * v->columns + i]; if (iszero(pivot)) { int p = i + 1; // while (! (v->data[p * v->columns + i])) { while (iszero(v->data[p * v->columns + i])) { p++; if (p > v->rows) { return sign; } } // for (int k = 0; k < v->columns; k++) { // double d1 = v->data[p * v->columns + k]; // double d2 = v->data[i * v->columns + k]; // v->data[p * v->columns + k] = d2; // v->data[i * v->columns + k] = d1; // } p = p * v->columns; i = i * v->columns; for (int k = 0; k < v->columns; k++) { double d1 = v->data[p]; double d2 = v->data[i]; v->data[p++] = d2; v->data[i++] = d1; } sign = -sign; } for (int k = i + 1; k < v->rows; k++) { double factor = - v->data[k * v->columns + i] / v->data[i * v->columns + i]; long target = k * v->columns; if (iszero(factor)) { for (int l = i; l < v->columns; l++) { v->data[target++] += 0.0; } } else { long source = i * v->columns; for (int l = i; l < v->columns; l++) { v->data[target++] += factor * v->data[source++]; } } } } return sign; } static int vectorlib_aux_determinant(lua_State *L, int singular, double *d) { vector a = vectorlib_aux_get(L, 1); (void) singular; if (a && a->rows == a->columns) { if (a->type == identity_type) { *d = 1.0; return 1; } else { switch (a->columns) { case 1: /* { { a } } : a */ { *d = a->data[0]; } break; case 2: /* { { a, b }, { c, d } } : ad - bc */ { *d = a->data[0] * a->data[3] - a->data[1] * a->data[2]; } break; case 3: /* {{ a, b, c }, { d, e, f }. { g, g, i } } : a(ei-fh) - b(di-gf) + c(dh-eg) */ /* see gems article about pos / neg separation, appendix v2.5 */ { *d = a->data[0] * (a->data[4] * a->data[8] - a->data[5] * a->data[7]) - a->data[1] * (a->data[3] * a->data[8] - a->data[6] * a->data[5]) + a->data[2] * (a->data[3] * a->data[7] - a->data[4] * a->data[6]); } break; default: vectorlib_aux_copy(L, 1); { vector a = vectorlib_aux_get(L, -1); int sign = vectorlib_aux_uppertriangle(a, 1); *d = 1.0; for (int i = 0; i < a->rows; i++) { double dd = a->data[i * a->columns + i]; if (iszero(dd)) { /*tex This is also an attempt to avoid the -0.0 case. But even this doesn't catches all it seems. */ *d = 0.0; goto DONE; } else { *d = *d * a->data[i * a->columns + i]; } } *d = sign * *d; } break; } /*tex These -0.0 result in random tiny number, is this a compiler issue? The next test doesn't always catch is. We also have this issue in \METAPOST\ double mode. A |-d == 0.0| is more reliable than a |d == -0.0| test it seems. */ // if (-d == 0.0) { // d = 0.0; // } DONE: if (iszero(*d)) { *d = 0.0; } return 1; } } else { return 0; } } static int vectorlib_determinant(lua_State *L) { double d = 0.0; if (vectorlib_aux_determinant(L, 0, &d)) { if (iszero(d)) { lua_pushinteger(L, 0); } else { lua_pushnumber(L, d); } } else { lua_pushnil(L); } return 1; } static int vectorlib_issingular(lua_State *L) { double d = 0.0; if (vectorlib_aux_determinant(L, 0, &d)) { double e = lua_type(L, 2) == LUA_TNUMBER ? lua_tonumber(L, 2) : 0.001; lua_pushboolean(L, d <= e || d >= -e); } else { lua_pushnil(L); } return 1; } static int vectorlib_aux_rowechelon(vector v, int reduce, int augmented) { int pRow = 0; int pCol = 0; int nRow = v->rows; int nCol = v->columns; while (pRow < nRow) { double pivot = v->data[pRow * nCol + pCol]; if (iszero(pivot)) { int i = pRow; int n = nRow; while (iszero(v->data[i * nCol + pCol])) { i++; if (i > n) { pCol = pCol + 1; if (pCol >= nCol) { if (augmented) { /* we have a serious problem */ } return 1; } else { i = pRow; } } } for (int k = 0; k < v->columns; k++) { double d1 = v->data[pRow * v->columns + k]; double d2 = v->data[i * v->columns + k]; v->data[pRow * v->columns + k] = d2; v->data[i * v->columns + k] = d1; } } pivot = v->data[pRow * nCol + pCol]; for (int l = pCol; l < nCol; l++) { v->data[pRow * nCol + l] /= pivot; } if (reduce) { for (int k = 0; k < pRow; k++) { double factor = - v->data[k * nCol + pCol]; for (int l = pCol; l < nCol; l++) { v->data[k * nCol + l] += factor * v->data[pRow * nCol + l]; } } } for (int k = pRow + 1; k < nRow; k++) { double factor = - v->data[k * nCol + pCol]; for (int l = pCol; l < nCol; l++) { // + 1 v->data[k * nCol + l] += factor * v->data[pRow * nCol + l]; } } pRow = pRow + 1; pCol = pCol + 1; if (pRow > nRow || pCol > nCol) { pRow = nRow + 1; } } return 0; } static int vectorlib_inverse(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { switch (a->type) { case identity_type: vectorlib_aux_copy(L, 1); break; default: if (a->rows == a->columns) { switch (a->rows) { case 1: { vector w = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); if (iszero(a->data[0])) { goto BAD; } else { w->data[0] = 1.0 / a->data[0]; } } break; case 2: { double d = 0.0; if (vectorlib_aux_determinant(L, 0, &d)) { if (iszero(d)) { goto BAD; } else { vector w = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); w->data[0] = a->data[3] / d; w->data[1] = - a->data[1] / d; w->data[2] = - a->data[2] / d; w->data[3] = a->data[0] / d; } } } break; case 3: { double d = 0.0; if (vectorlib_aux_determinant(L, 0, &d)) { if (iszero(d)) { goto BAD; } else { vector w = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); w->data[0] = (a->data[4] * a->data[8] - a->data[5] * a->data[7]) / d; w->data[1] = (a->data[2] * a->data[7] - a->data[1] * a->data[8]) / d; w->data[2] = (a->data[1] * a->data[5] - a->data[2] * a->data[4]) / d; w->data[3] = (a->data[5] * a->data[6] - a->data[3] * a->data[8]) / d; w->data[4] = (a->data[0] * a->data[8] - a->data[2] * a->data[6]) / d; w->data[5] = (a->data[2] * a->data[3] - a->data[0] * a->data[5]) / d; w->data[6] = (a->data[3] * a->data[7] - a->data[4] * a->data[6]) / d; w->data[7] = (a->data[1] * a->data[6] - a->data[0] * a->data[7]) / d; w->data[8] = (a->data[0] * a->data[4] - a->data[1] * a->data[3]) / d; } } } break; default: { // vectorlib_copy(L); // vectorlib_identity(L); // vectorlib_concat(L); vector v = vectorlib_aux_push(L, a->rows, a->columns * 2, a->stacking); vector w = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int r = 0; r < a->rows; r++) { long target = r * v->columns; long source = r * a->columns; for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } // v->data[r * v->columns + a->columns + r] = 1.0; v->data[target + r] = 1.0; } vectorlib_aux_rowechelon(v, 1, 1); for (int r = 0; r < a->rows; r++) { if (iszero(v->data[r * v->columns + r])) { goto BAD; } } for (int r = 0; r < a->rows; r++) { long target = r * w->columns; long source = r * v->columns + a->columns; for (int c = 0; c < a->columns; c++) { w->data[target++] = v->data[source++]; } } } break; } } } return 1; } BAD: lua_pushnil(L); return 1; } static int vectorlib_rowechelon(lua_State *L) { vector a = vectorlib_aux_get(L, 1); int reduce = ! lua_toboolean(L, 2); if (a && a->rows == a->columns) { vectorlib_aux_copy(L, 1); switch (a->type) { case identity_type: break; default: a = vectorlib_aux_get(L, -1); vectorlib_aux_rowechelon(a, reduce, 0); } } else { lua_pushnil(L); } return 1; } static int vectorlib_crossproduct(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b) { if (a->columns == b->columns && a->rows == b->rows && (b->columns == 1 || b->rows == 1)) { switch (a->rows) { case 1: switch (a->columns) { case 1: lua_pushinteger(L, 0); return 1; case 2: lua_pushnumber(L, a->data[0] * b->data[1] - b->data[1] * a->data[0]); return 1; case 3: /*u \times v = (u2*v3 - u3*v2, u3*v1 - u1*v3, u1*v2 - u2*v1) */ { vector v = vectorlib_aux_push(L, 1, a->columns, a->stacking); v->data[0] = a->data[1] * b->data[2] - a->data[2] * b->data[1]; v->data[1] = a->data[2] * b->data[0] - a->data[0] * b->data[2]; v->data[2] = a->data[0] * b->data[1] - a->data[1] * b->data[0]; return 1; } default: break; /* maybe todo */ } break; case 2: /* u \times v = u1*v2 - u2*v1 */ lua_pushnumber(L, a->data[0] * b->data[1] - b->data[1] * a->data[0]); return 1; case 3: /*u \times v = (u2*v3 - u3*v2, u3*v1 - u1*v3, u1*v2 - u2*v1) */ { vector v = vectorlib_aux_push(L, a->rows, 1, a->stacking); v->data[0] = a->data[1] * b->data[2] - a->data[2] * b->data[1]; v->data[1] = a->data[2] * b->data[0] - a->data[0] * b->data[2]; v->data[2] = a->data[0] * b->data[1] - a->data[1] * b->data[0]; return 1; } default: break; /* maybe todo */ } } } lua_pushnil(L); return 1; } static int vectorlib_normalize(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = 0.0; for (int i = 0; i < a->rows * a->columns; i++) { d += a->data[i] * a->data[i]; } if (d > 0.0) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); d = sqrt(d); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i] / d; } return 1; } } lua_pushnil(L); return 1; } static int vectorlib_homogenize(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a && a->columns > 1 && ! vectorlib_aux_zero_in_column(a, a->columns - 1)) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); for (int r = 0; r < a->rows; r++) { double d = a->data[r * a->columns + a->columns - 1]; long target = r * a->columns; long source = r * a->columns; for (int c = 0; c < a->columns - 1; c++) { v->data[target++] = a->data[source++] / d; } v->data[target] = 1.0; } return 1; } lua_pushnil(L); return 1; } static int vectorlib_concat(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); int vertical = lua_toboolean(L, 3); if (a && b) { if (vertical) { if (b->columns == a->columns) { vector v = vectorlib_aux_push(L, a->rows + b->rows, a->columns, a->stacking); long index = a->rows * a->columns; vectorlib_aux_copy_data(v, a); for (int i = 0; i < b->rows * b->columns; i++) { v->data[index++] = b->data[i]; } return 1; } } else { if (b->rows == a->rows) { vector v = vectorlib_aux_push(L, a->rows, a->columns + b->columns, a->stacking); for (int r = 0; r < a->rows; r++) { long target = r * v->columns; long source = r * a->columns; for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } } for (int r = 0; r < b->rows; r++) { long target = r * v->columns + a->columns; long source = r * b->columns; for (int c = 0; c < b->columns; c++) { v->data[target++ ] = b->data[source++]; } } return 1; } } } lua_pushnil(L); return 1; } static int vectorlib_slice(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int fr = lmt_tointeger(L, 2); int fc = lmt_tointeger(L, 3); int nr = lmt_tointeger(L, 4); int nc = lmt_tointeger(L, 5); int tr = --fr + nr - 1; int tc = --fc + nc - 1; if ( (fr >= 0 && fr < a->rows) && (fc >= 0 && fc < a->columns) && (tr >= fr && tr < a->rows) && (tc >= fc && tc < a->columns)) { vector v = vectorlib_aux_push(L, nr, nc, a->stacking); long target = 0; for (int r = fr; r <= tr; r++) { long source = r * a->columns; for (int c = fc; c <= tc; c++) { v->data[target++] = a->data[source++]; } } return 1; } } lua_pushnil(L); return 1; } static int vectorlib_replace(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b) { int fr = lmt_tointeger(L, 3); int fc = lmt_tointeger(L, 4); int nr = b->rows; int nc = b->columns; int tr = --fr + nr - 1; int tc = --fc + nc - 1; if ( (fr >= 0 && fr < a->rows) && (fc >= 0 && fc < a->columns) && (tr >= fr && tr < a->rows) && (tc >= fc && tc < a->columns)) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); long source = 0; vectorlib_aux_copy_data(v, a); for (int r = fr; r <= tr; r++) { long target = r * v->columns + fc; for (int c = fc; c <= tc; c++) { v->data[target++] = b->data[source++]; } } return 1; } } lua_pushnil(L); return 1; } static int vectorlib_delete(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int f = lmt_tointeger(L, 2); int n = lmt_tointeger(L, 3); int t = --f + n - 1; int vertical = lua_toboolean(L, 4); if (vertical) { if ((n <= a->rows) && (f >= 0 && f < a->rows) && (t >= f && t < a->rows)) { vector v = vectorlib_aux_push(L, a->rows - n, a->columns, a->stacking); long target = 0; for (int r = 0; r < a->rows; r++) { if (r < f || r > t) { long source = r * a->columns; for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } } } return 1; } } else { if ((n <= a->columns) && (f >= 0 && f < a->columns) && (t >= f && t < a->columns)) { vector v = vectorlib_aux_push(L, a->rows, a->columns - n, a->stacking); long target = 0; for (int r = 0; r < a->rows; r++) { int base = r * a->columns; for (int c = 0; c < a->columns; c++) { if (c < f || c > t) { v->data[target++] = a->data[base + c]; } } } return 1; } } } lua_pushnil(L); return 1; } static int vectorlib_remove(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int f = lmt_tointeger(L, 2); int n = lmt_tointeger(L, 3); int t = --f + n - 1; int vertical = lua_toboolean(L, 4); if (vertical) { if ((n <= a->rows) && (f >= 0 && f < a->rows) && (t >= f && t < a->rows)) { vector v = vectorlib_aux_push(L, a->rows - n, a->columns, a->stacking); vector w = vectorlib_aux_push(L, n, a->columns, a->stacking); long vtarget = 0; long wtarget = 0; for (int r = 0; r < a->rows; r++) { long source = r * a->columns; if (r < f || r > t) { for (int c = 0; c < a->columns; c++) { v->data[vtarget++] = a->data[source++]; } } else { for (int c = 0; c < a->columns; c++) { w->data[wtarget++] = a->data[source++]; } } } return 2; } } else { if ((n <= a->columns) && (f >= 0 && f < a->columns) && (t >= f && t < a->columns)) { vector v = vectorlib_aux_push(L, a->rows, a->columns - n, a->stacking); vector w = vectorlib_aux_push(L, a->rows, n, a->stacking); long vtarget = 0; long wtarget = 0; for (int r = 0; r < a->rows; r++) { long source = r * a->columns; for (int c = 0; c < a->columns; c++) { if (c < f || c > t) { v->data[vtarget++] = a->data[source++]; } else { w->data[wtarget++] = a->data[source++]; } } } return 2; } } } lua_pushnil(L); lua_pushnil(L); return 2; } static int vectorlib_insert(lua_State *L) { vector a = vectorlib_aux_get(L, 1); vector b = vectorlib_aux_get(L, 2); if (a && b) { int p = lmt_tointeger(L, 3); int vertical = lua_toboolean(L, 4); if (vertical) { if (a->columns == b->columns) { vector v = vectorlib_aux_push(L, a->rows + b->rows, a->columns, a->stacking); long target = 0; for (int r = 0; r < p; r++) { long source = r * a->columns; for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } } for (int r = 0; r < b->rows; r++) { long source = r * b->columns; for (int c = 0; c < b->columns; c++) { v->data[target++] = b->data[source++]; } } for (int r = p; r < a->rows; r++) { long source = r * a->columns; for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } } return 1; } } else { if (a->rows == b->rows) { vector v = vectorlib_aux_push(L, a->rows, a->columns + b->columns, a->stacking); long target = 0; for (int r = 0; r < a->rows; r++) { long source = r * a->columns; for (int c = 0; c < p; c++) { v->data[target++] = a->data[source++]; } source = r * b->columns; for (int c = 0; c < b->columns; c++) { v->data[target++] = b->data[source++]; } source = r * a->columns + p; for (int c = p; c < a->columns; c++) { v->data[target++] = a->data[source++]; } } return 1; } } } lua_pushnil(L); return 1; } static int vectorlib_append(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { double d = lua_tonumber(L, 2); int vertical = lua_toboolean(L, 3); if (vertical) { int row = a->rows + 1; vector v = vectorlib_aux_push(L, row, a->columns, a->stacking); vectorlib_aux_copy_data(v, a); for (int c = 0; c < a->columns; c++) { v->data[row + c] = d; } return 1; } else { vector v = vectorlib_aux_push(L, a->rows, a->columns + 1, a->stacking); long target = 0; long source = 0; for (int r = 0; r < a->rows; r++) { for (int c = 0; c < a->columns; c++) { v->data[target++] = a->data[source++]; } v->data[target++] = d; } return 1; } } lua_pushnil(L); return 1; } static int vectorlib_exchange(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { int p = lmt_tointeger(L, 2) - 1; int q = lmt_tointeger(L, 3) - 1; int vertical = lua_toboolean(L, 4); if (vertical) { if (p >= 0 && p < a->rows && q >= 0 && q < a->rows) { vector v = vectorlib_aux_push(L, a->rows , a->columns, a->stacking); vectorlib_aux_copy_data(v, a); p *= a->columns; q *= a->columns; for (int c = 0; c < a->columns; c++) { v->data[p] = a->data[q]; v->data[q] = a->data[p]; p++; q++; } return 1; } } else { if (p >= 0 && p < a->columns && q >= 0 && q < a->columns) { vector v = vectorlib_aux_push(L, a->rows , a->columns, a->stacking); vectorlib_aux_copy_data(v, a); for (int i = 0; i < a->rows * a->columns; i++) { v->data[i] = a->data[i]; } for (int r = 0; r < a->rows; r++) { v->data[p] = a->data[q]; v->data[q] = a->data[p]; p += a->columns; q += a->columns; } return 1; } } } lua_pushnil(L); return 1; } static int vectorlib_swap(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a && (a->columns == 1 || a->rows == 1)) { vector v = vectorlib_aux_push(L, a->rows, a->columns, a->stacking); v->rows = a->columns; v->columns = a->rows; vectorlib_aux_copy_data(v, a); } else { lua_pushnil(L); } return 1; } static int vectorlib_gettype(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { lua_pushinteger(L, v->type); } else { lua_pushnil(L); } return 1; } static int vectorlib_getdimensions(lua_State *L) { vector v = vectorlib_aux_get(L, 1); if (v) { lua_pushinteger(L, v->rows); lua_pushinteger(L, v->columns); return 2; } else { return 0; } } static int vectorlib_getepsilon(lua_State *L) { lua_pushnumber(L, p_epsilon); return 1; } static int vectorlib_iszero(lua_State *L) { lua_pushboolean(L, lua_tonumber(L, 1)); return 1; } static int vectorlib_setstacking(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { a->stacking = lmt_tointeger(L, 2); } return 0; } static int vectorlib_getstacking(lua_State *L) { vector a = vectorlib_aux_get(L, 1); if (a) { lua_pushinteger(L, a->stacking); } else { lua_pushnil(L); } return 1; } /* Meshes: These we use for some experiments with alternative approaches to contour graphics in the \LUAMETAFUN\ subsystem. They are for now not official. */ static inline unsigned short vectorlib_valid_point(lua_State *L, int index) { int p = lmt_tointeger(L, index); return p < 0 ? 0 : p > max_mesh ? max_mesh : p; } static mesh vectorlib_aux_maybe_ismesh(lua_State *L, int index) { mesh m = lua_touserdata(L, index); if (m && lua_getmetatable(L, index)) { lua_get_metatablelua(mesh_instance); if (! lua_rawequal(L, -1, -2)) { m = NULL; } lua_pop(L, 2); } return m; } static int vectorlib_ismesh(lua_State *L) { lua_pushboolean(L, vectorlib_aux_maybe_ismesh(L, 1) ? 1 : 0); return 1; } static int vectorlib_mesh_type(lua_State *L) { if (vectorlib_aux_maybe_ismesh(L, 1)) { lua_push_key(mesh); } else { lua_pushnil(L); } return 1; } static inline void vectorlib_mesh_wipe_entry(meshentry *entry) { for (int i = 0; i < 4; i++) { entry->points[i] = 0; } entry->average = 0.0; } static inline mesh vectorlib_mesh_aux_push(lua_State *L, int r) { mesh m = lua_newuserdatauv(L, 2 * sizeof(int) + r * sizeof(meshentry), 0); if (m && r > 0) { m->rows = r; m->type = triangle_mesh_type; lua_get_metatablelua(mesh_instance); lua_setmetatable(L, -2); } return m; } static int vectorlib_mesh_new(lua_State *L) { switch (lua_type(L, 1)) { case LUA_TNUMBER: { mesh m = vectorlib_mesh_aux_push(L, lmt_optinteger(L, 1, 1)); for (int i = 0; i < m->rows; i++) { vectorlib_mesh_wipe_entry(&(m->data[i])); } return 1; } case LUA_TTABLE: { int t = lua_rawgeti(L, 1, 1); lua_pop(L, 1); switch (t) { case LUA_TNUMBER: /* local v = vector.new( { 1,2,3 }, { 4,5,6 } ) */ /* n can determine quads */ { int rows = lua_gettop(L); if (rows) { mesh m = vectorlib_mesh_aux_push(L, rows); for (int i = 0; i < rows; i++) { meshentry *entry = &(m->data[i]); if (lua_type(L, i + 1) == LUA_TTABLE) { for (int j = 0; j < 4; j++) { entry->points[j] = lua_rawgeti(L, i + 1, j + 1) == LUA_TNUMBER ? vectorlib_valid_point(L, -1) : 0; lua_pop(L, 1); } entry->average = 0.0; } else { vectorlib_mesh_wipe_entry(entry); } } return 1; } else { break; } } case LUA_TTABLE: { /* local v = vector.new( { { 1,2,3 }, { 4,5,6 } } ) */ int rows = (int) lua_rawlen(L, 1); if (rows) { mesh m = vectorlib_mesh_aux_push(L, rows); for (int i = 0; i < rows; i++) { meshentry *entry = &(m->data[i]); if (lua_rawgeti(L, 1, i + 1) == LUA_TTABLE) { for (int j = 0; j < 4; j++) { entry->points[j] = lua_rawgeti(L, -1, j + 1) == LUA_TNUMBER ? vectorlib_valid_point(L, -1) : 0; lua_pop(L, 1); } entry->average = 0.0; } else { vectorlib_mesh_wipe_entry(entry); } lua_pop(L, 1); } return 1; } else { break; } } default: break; } break; } } lua_pushnil(L); return 0; } inline static mesh vectorlib_mesh_aux_get(lua_State *L, int index) { mesh m = lua_touserdata(L, index); if (m && lua_getmetatable(L, index)) { lua_get_metatablelua(mesh_instance); if (! lua_rawequal(L, -1, -2)) { m = NULL; } lua_pop(L, 2); } return m; } mesh vectorlib_get_mesh(lua_State *L, int index) { if (lua_type(L, index) == LUA_TUSERDATA) { return (mesh) luaL_checkudata(L, index, MESH_METATABLE); } else { return NULL; } } static int vectorlib_mesh_tostring(lua_State *L) { mesh v = vectorlib_mesh_aux_get(L, 1); if (v) { lua_pushfstring(L, "", v->rows, v->type == quad_mesh_type ? "quads" : "triangles", v); return 1; } else { return 0; } } static int vectorlib_mesh_totable(lua_State *L) { mesh m = vectorlib_aux_maybe_ismesh(L, 1); if (m) { long target = 1; int size = m->type == quad_mesh_type ? 4 : 3; if (lua_toboolean(L, 2)) { lua_createtable(L, m->rows * (size + 1), 0); for (int r = 0; r < m->rows; r++) { meshentry *triangle = &(m->data[r]); for (int i = 0; i < size; i++) { lua_pushinteger(L, triangle->points[i]); lua_rawseti(L, -2, target++); } lua_pushnumber(L, triangle->average); lua_rawseti(L, -2, target++); } } else { lua_createtable(L, m->rows, 0); for (int r = 0; r < m->rows; r++) { meshentry *triangle = &(m->data[r]); long index = 1; lua_createtable(L, size + 1, 0); for (int i = 0; i < size; i++) { lua_pushinteger(L, triangle->points[i]); lua_rawseti(L, -2, index++); } lua_pushnumber(L, triangle->average); lua_rawseti(L, -2, index); lua_rawseti(L, -2, target++); } } return 1; } else { return 0; } } static int vectorlib_mesh_index(lua_State *L) { mesh m = vectorlib_mesh_aux_get(L, 1); if (m) { int index = lmt_tointeger(L, 2); if (index > 0 && index <= m->rows) { lua_pushnumber(L, m->data[index - 1].average); return 1; } } return 0; } static int vectorlib_mesh_getvalue(lua_State *L) { mesh m = vectorlib_mesh_aux_get(L, 1); if (m) { int index = lmt_tointeger(L, 2); if (index > 0 && index <= m->rows) { meshentry *triangle = &(m->data[index-1]); int size = m->type == quad_mesh_type ? 4 : 3; if (lua_toboolean(L, 3)) { long target = 1; lua_createtable(L, size + 1, 0); for (int i = 0; i < size; i++) { lua_pushinteger(L, triangle->points[i]); lua_rawseti(L, -2, target++); } lua_pushnumber(L, triangle->average); lua_rawseti(L, -2, target); return 1; } else { for (int i = 0; i < size; i++) { lua_pushinteger(L, triangle->points[i]); } lua_pushnumber(L, triangle->average); return size + 1; } } } return 0; } static int vectorlib_mesh_setvalue(lua_State *L) { mesh m = vectorlib_mesh_aux_get(L, 1); if (m) { int index = lmt_tointeger(L, 2); if (index > 0 && index <= m->rows) { meshentry *triangle = &(m->data[index-1]); switch (lua_type(L, 3)) { case LUA_TNUMBER: { triangle->average = lua_tonumber(L, 3); break; } case LUA_TTABLE: { int size = m->type == quad_mesh_type ? 4 : 3; for (int i = 1; i <= size; i++) { if (lua_rawgeti(L, 3, i) == LUA_TNUMBER) { if (i < size) { triangle->points[i-1] = vectorlib_valid_point(L, -1); } else { triangle->average = lua_tonumber(L, -1); } } lua_pop(L, 1); } break; } } } } return 0; } static int vectorlib_mesh_getlength(lua_State *L) { mesh m = vectorlib_mesh_aux_get(L, 1); lua_pushinteger(L, m ? m->rows : 0); return 1; } static int vectorlib_mesh_getdimensions(lua_State *L) { mesh m = vectorlib_mesh_aux_get(L, 1); if (m) { lua_pushinteger(L, m->rows); } else { lua_pushnil(L); } return 1; } /* Contours: These we use for some experiments with alternative approaches to contour graphics in the \LUAMETAFUN\ subsystem. They are for now not official. */ static int vectorlib_contour_aux_okay(int condition, const char *where, const char *message) { if (! condition) { tex_formatted_error("vector lib", "error in vector.%s: %s\n", where, message); } return condition; } static int vectorlib_contour_aux_is_valid(lua_State *L, vector *v, mesh *l, const char *what) { *v = vectorlib_get(L, 1); if (vectorlib_contour_aux_okay(*v && (*v)->rows > 1 && (*v)->columns > 2, what, "point list expected (x,y,z,...)")) { *l = vectorlib_get_mesh(L, 2); if (vectorlib_contour_aux_okay(*l && (*l)->type == triangle_mesh_type, what, "triangle list expected ((p1,p2,p3),average)")) { return 1; } } return 0; } static int vectorlib_contour_getmesh(lua_State *L) /* see mplib */ { vector v = NULL; mesh l = NULL; if (vectorlib_contour_aux_is_valid(L, &v, &l, "getmesh")) { int i = lmt_tointeger(L, 3) - 1; /* triangle index */ if (i >= 0 && i < l->rows) { int done = 0; int okay = 0; int size = l->type == quad_mesh_type ? 4 : 3; meshentry *triangle = &(l->data[i]); for (int j = 0; j < size; j++) { int r = triangle->points[j] - 1; if (r >= 0 && r < v->rows) { int k = r * v->columns; if (! okay) { lua_createtable(L, 2 * size, 0); okay = 1; } lua_pushnumber(L, v->data[k++]); lua_rawseti(L, -2, ++done); lua_pushnumber(L, v->data[k]); lua_rawseti(L, -2, ++done); } else { break; /* error */ } } if (okay) { lua_pushnumber(L, triangle->average); return 2; } else { /*tex We signal that we have a zero entry. */ lua_pushboolean(L, 0); return 1; } } } return 0; } static int vectorlib_contour_getarea(lua_State *L) /* see mplib */ { vector v = NULL; mesh l = NULL; if (vectorlib_contour_aux_is_valid(L, &v, &l, "getarea")) { int i = lmt_tointeger(L, 3) - 1; /* triangle index */ if (i >= 0 && i < l->rows) { meshentry *entry = &(l->data[i]); switch (l->type) { case triangle_mesh_type: { int p1 = entry->points[0] - 1; int p2 = entry->points[1] - 1; int p3 = entry->points[2] - 1; if (p1 >= 0 && p1 < v->rows && p2 >= 0 && p2 < v->rows && p3 >= 0 && p3 < v->rows) { double x1 = v->data[p1 * v->columns]; double y1 = v->data[p1 * v->columns + 1]; double x2 = v->data[p2 * v->columns]; double y2 = v->data[p2 * v->columns + 1]; double x3 = v->data[p3 * v->columns]; double y3 = v->data[p3 * v->columns + 1]; double ab = sqrt(pow(x1-x2,2) + pow(y1-y2,2)); double bc = sqrt(pow(x2-x3,2) + pow(y2-y3,2)); double ca = sqrt(pow(x3-x1,2) + pow(y3-y1,2)); lua_pushnumber(L, sqrt ( ( ab + bc + ca) * (- ab + bc + ca) * ( ab - bc + ca) * ( ab + bc - ca) ) / 4); return 1; } break; } case quad_mesh_type: break; } } } return 0; } /* see below, todo: optimize */ static int vectorlib_contour_aux_checkoverlap(vector v, mesh l, int U, int V, int method) { int u0 = l->data[U].points[0] - 1; int u1 = l->data[U].points[1] - 1; int u2 = l->data[U].points[2] - 1; int v0 = l->data[V].points[0] - 1; int v1 = l->data[V].points[1] - 1; int v2 = l->data[V].points[2] - 1; if ( u0 >= 0 && u1 >= 0 && u2 >= 0 && v0 >= 0 && v1 >= 0 && v2 >= 0 && u0 < v->rows && u1 < v->rows && u2 < v->rows && v0 < v->rows && v1 < v->rows && v2 < v->rows ) { if ( u0 == v0 || u1 == v0 || u2 == v0 || u0 == v1 || u1 == v1 || u2 == v1 || u0 == v2 || u1 == v2 || u2 == v2 ) { return triangles_intersection_nop_same_points; } else { double *U0 = &(v->data[u0 * v->columns]); double *U1 = &(v->data[u1 * v->columns]); double *U2 = &(v->data[u2 * v->columns]); double *V0 = &(v->data[v0 * v->columns]); double *V1 = &(v->data[v1 * v->columns]); double *V2 = &(v->data[v2 * v->columns]); if ( /* we can't memcmp due to small differences (epsilon) */ (iszero(fabs(U0[0] - V0[0])) && iszero(fabs(U0[1] - V0[1])) && iszero(fabs(U0[2] - V0[2]))) || (iszero(fabs(U1[0] - V0[0])) && iszero(fabs(U1[1] - V0[1])) && iszero(fabs(U1[2] - V0[2]))) || (iszero(fabs(U2[0] - V0[0])) && iszero(fabs(U2[1] - V0[1])) && iszero(fabs(U2[2] - V0[2]))) || (iszero(fabs(U0[0] - V1[0])) && iszero(fabs(U0[1] - V1[1])) && iszero(fabs(U0[2] - V1[2]))) || (iszero(fabs(U1[0] - V1[0])) && iszero(fabs(U1[1] - V1[1])) && iszero(fabs(U1[2] - V1[2]))) || (iszero(fabs(U2[0] - V1[0])) && iszero(fabs(U2[1] - V1[1])) && iszero(fabs(U2[2] - V1[2]))) || (iszero(fabs(U0[0] - V2[0])) && iszero(fabs(U0[1] - V2[1])) && iszero(fabs(U0[2] - V2[2]))) || (iszero(fabs(U1[0] - V2[0])) && iszero(fabs(U1[1] - V2[1])) && iszero(fabs(U1[2] - V2[2]))) || (iszero(fabs(U2[0] - V2[0])) && iszero(fabs(U2[1] - V2[1])) && iszero(fabs(U2[2] - V2[2]))) ) { return triangles_intersection_nop_same_values; } else if (method == 2) { return triangles_intersect_gd(U0, U1, U2, V0, V1, V2); } else { return triangles_intersect(U0, U1, U2, V0, V1, V2); } } } return -1; } static int vectorlib_contour_checkoverlap(lua_State *L) /* see mplib */ { vector v = NULL; mesh l = NULL; if (vectorlib_contour_aux_is_valid(L, &v, &l, "checkoverlap")) { int U = lmt_tointeger(L, 3) - 1; int V = lmt_tointeger(L, 4) - 1; if (U >= 0 && U < l->rows && V >= 0 && V < l->rows) { int method = lmt_optinteger(L, 5, 1); int overlapping = vectorlib_contour_aux_checkoverlap(v, l, U, V, method); if (overlapping >= 0) { lua_pushinteger(L, overlapping); return 1; } } } return 0; } static int vectorlib_contour_collectoverlap(lua_State *L) /* see mplib */ { vector v = NULL; mesh l = NULL; if (vectorlib_contour_aux_is_valid(L, &v, &l, "collectoverlap")) { int details = lua_toboolean(L, 3); int method = lmt_optinteger(L, 4, 1); int vr = v->rows; int vc = v->columns; int lr = l->rows; int m = 0; for (int i = 0; i < lr; i++) { meshentry *triangle = &(l->data[i]); int u0 = triangle->points[0]; int u1 = triangle->points[1]; int u2 = triangle->points[2]; if ( u0 >= 1 && u1 >= 1 && u2 >= 1 && u0 <= vr && u1 <= vr && u2 <= vr ) { } else { vectorlib_contour_aux_okay(0, "collectoverlap", "triangle list entries has invalid point references"); return 0; } } for (int i = 0; i < lr - 1; i++) { meshentry *triangle = &(l->data[i]); int u0 = triangle->points[0] - 1; int u1 = triangle->points[1] - 1; int u2 = triangle->points[2] - 1; double *U0 = &(v->data[u0 * vc]); double *U1 = &(v->data[u1 * vc]); double *U2 = &(v->data[u2 * vc]); for (int j = i + 1; j < lr; j++) { meshentry *triangle = &(l->data[j]); int v0 = triangle->points[0] - 1; if ( u0 == v0 || u1 == v0 || u2 == v0 ) { continue; /* triangles_intersection_nop_same_points */ } int v1 = triangle->points[1] - 1; if ( u0 == v1 || u1 == v1 || u2 == v1 ) { continue; /* triangles_intersection_nop_same_points */ } int v2 = triangle->points[2] - 1; if ( u0 == v2 || u1 == v2 || u2 == v2 ) { continue; /* triangles_intersection_nop_same_points */ } double *V0 = &(v->data[v0 * vc]); if ( // test with fabs(a-b) < EPSILON (iszero(fabs(U0[0] - V0[0])) && iszero(fabs(U0[1] - V0[1])) && iszero(fabs(U0[2] - V0[2]))) || (iszero(fabs(U1[0] - V0[0])) && iszero(fabs(U1[1] - V0[1])) && iszero(fabs(U1[2] - V0[2]))) || (iszero(fabs(U2[0] - V0[0])) && iszero(fabs(U2[1] - V0[1])) && iszero(fabs(U2[2] - V0[2]))) ) { continue; /* triangles_intersection_nop_same_values */ } double *V1 = &(v->data[v1 * vc]); if ( (iszero(fabs(U0[0] - V1[0])) && iszero(fabs(U0[1] - V1[1])) && iszero(fabs(U0[2] - V1[2]))) || (iszero(fabs(U1[0] - V1[0])) && iszero(fabs(U1[1] - V1[1])) && iszero(fabs(U1[2] - V1[2]))) || (iszero(fabs(U2[0] - V1[0])) && iszero(fabs(U2[1] - V1[1])) && iszero(fabs(U2[2] - V1[2]))) ) { continue; /* triangles_intersection_nop_same_values */ } double *V2 = &(v->data[v2 * vc]); if ( (iszero(fabs(U0[0] - V2[0])) && iszero(fabs(U0[1] - V2[1])) && iszero(fabs(U0[2] - V2[2]))) || (iszero(fabs(U1[0] - V2[0])) && iszero(fabs(U1[1] - V2[1])) && iszero(fabs(U1[2] - V2[2]))) || (iszero(fabs(U2[0] - V2[0])) && iszero(fabs(U2[1] - V2[1])) && iszero(fabs(U2[2] - V2[2]))) ) { continue; /* triangles_intersection_nop_same_values */ } if (details) { triangles_three Utimes, Vtimes; int state = method == 2 ? triangles_intersect_with_line_gd(U0, U1, U2, V0, V1, V2, Utimes, Vtimes) : triangles_intersect_with_line (U0, U1, U2, V0, V1, V2, Utimes, Vtimes); if (state > triangles_intersection_yes_bound) { long index = 1; if (! m) { lua_createtable(L, 8, 0); /* main table, how many makes sense */ } lua_createtable(L, 8, 0); // lua_createtable(L, 9, 0); lua_pushinteger(L, i + 1); lua_rawseti(L, -2, index++); lua_pushinteger(L, j + 1); lua_rawseti(L, -2, index++); for (int k = 0; k < 3; k++) { lua_pushnumber(L, Utimes[k]); lua_rawseti(L, -2, index++); } for (int k = 0; k < 3; k++) { lua_pushnumber(L, Vtimes[k]); lua_rawseti(L, -2, index++); } // lua_pushinteger(L, state); // lua_rawseti(L, -2, index++); lua_rawseti(L, -2, ++m); } } else { int state = method == 2 ? triangles_intersect_gd(U0, U1, U2, V0, V1, V2) : triangles_intersect (U0, U1, U2, V0, V1, V2); if (state > triangles_intersection_yes_bound) { if (! m) { lua_createtable(L, 8, 0); } lua_createtable(L, 2, 0); lua_pushinteger(L, i + 1); lua_rawseti(L, -2, 1); lua_pushinteger(L, j + 1); lua_rawseti(L, -2, 2); lua_rawseti(L, -2, ++m); /* maybe also store state */ } } } } return m ? 1 : 0; } return 0; } static int vectorlib_contour_average(lua_State *L) /* see mplib */ { vector v = NULL; mesh l = NULL; if (vectorlib_contour_aux_is_valid(L, &v, &l, "average")) { double minx = 0.0; double maxx = 0.0; double miny = 0.0; double maxy = 0.0; double minz = 0.0; double maxz = 0.0; int tolerant = lua_toboolean(L, 3); int method = lmt_optinteger(L, 4, 1); int size = l->type == quad_mesh_type ? 4 : 3; int okay = 0; int done = 0; for (int i = 0; i < l->rows; i++) { meshentry *entry = &(l->data[i]); double average = 0.0; double amin = 0; int a = 0; for (int j = 0; j < size; j++) { int r = entry->points[j]; if (r > 0 && r <= v->rows) { int k = (r - 1) * v->columns; double x = v->data[k++]; double y = v->data[k++]; double z = v->data[k]; if (! a || z < amin) { amin = z; } average += z; if (done) { if (x < minx) { minx = x; } else if (x > maxx) { maxx = x; } if (y < miny) { miny = y; } else if (y > maxy) { maxy = y; } } else { minx = x; miny = y; maxx = x; maxy = y; done = 1; } a++; } else if (! tolerant) { tex_formatted_error("vector lib", "error in vector.average, invalid point index"); a = 0; break; } } if (method == 2) { average = amin; } else { average = a > 0 ? (average / a) : 0.0; if (method == 3) { average += amin; } } entry->average = average; if (okay) { if (average < minz) { minz = average; } if (average > maxz) { maxz = average; } } else { minz = average; maxz = average; } okay++; } lua_createtable(L, 0, 7); lua_set_integer_by_key(L, "okay", okay); lua_set_number_by_key (L, "minx", minx); lua_set_number_by_key (L, "miny", miny); lua_set_number_by_key (L, "maxx", maxx); lua_set_number_by_key (L, "maxy", maxy); lua_set_number_by_key (L, "minz", minz); lua_set_number_by_key (L, "maxz", maxz); return 1; } else { return 0; } } // static int vectorlib_contour_bounds(lua_State *L) /* see mplib */ // { // vector v = NULL; // mesh l = NULL; // if (vectorlib_contour_aux_is_valid(L, &v, &l, "bounds")) { // double minx = 0.0; // double maxx = 0.0; // double miny = 0.0; // double maxy = 0.0; // int okay = 0; // for (int i = 0; i < l->rows; i++) { // int a = 0; // double average = 0.0; // meshentry *triangle = &(l->data[i]); // int size = l->type == quad_mesh_type ? 4 : 3; // for (int j = 0; j < size; j++) { // int r = triangle->points[j]; // if (r > 0 && r <= v->rows) { // int k = (r - 1) * v->columns; // double x = v->data[k]; // double y = v->data[k + 1]; // if (okay) { // if (x < minx) { minx = x; } else if (x > maxx) { maxx = x; } // if (y < miny) { miny = y; } else if (y > maxy) { maxy = y; } // } else { // minx = x; miny = y; // maxx = x; maxy = y; // } // } else { // break; // } // } // okay++; // } // lua_createtable(L, 0, 5); // lua_set_integer_by_key(L, "okay", okay); // lua_set_number_by_key (L, "minx", minx); // lua_set_number_by_key (L, "miny", miny); // lua_set_number_by_key (L, "maxx", maxx); // lua_set_number_by_key (L, "maxy", maxy); // return 1; // } // return 0; // } int vectorlib_contour_compare_mesh_average(const void *entry_1, const void *entry_2) { const meshentry *value_1 = (const meshentry *) entry_1; const meshentry *value_2 = (const meshentry *) entry_2; if (value_1->average > value_2->average) { return 1; } else if (value_1->average < value_2->average) { return -1; } else { return 0; } } static int vectorlib_contour_sort(lua_State *L) /* see mplib */ { mesh m = vectorlib_get_mesh(L, 1); if (m && m->rows > 1) { qsort(m->data, m->rows, sizeof(meshentry), vectorlib_contour_compare_mesh_average); } return 0; } static int vectorlib_contour_triangulate(lua_State *L) /* see mplib */ { int columns = lmt_tointeger(L, 1); int rows = lmt_tointeger(L, 2); if (rows > 0 && columns > 0 && columns * rows <= max_mesh) { mesh m = vectorlib_mesh_aux_push(L, rows * columns * 2); int index = 0; int offset = columns + 1; for (int r = 1; r <= rows; r++) { for (int c = 1; c <= columns; c++) { int p1 = (r - 1) * offset + c; // left point of current row int p2 = r * offset + c; // left point of next row int p11 = p1 + 1; int p21 = p2 + 1; if (p11 > max_mesh || p21 > max_mesh) { /* we clip */ } else { /* first */ m->data[index ].points[0] = (unsigned short) p1; m->data[index ].points[1] = (unsigned short) p11; m->data[index ].points[2] = (unsigned short) p21; m->data[index ].points[3] = 0; m->data[index++].average = 0.0; /* second */ m->data[index ].points[0] = (unsigned short) p1; m->data[index ].points[1] = (unsigned short) p21; m->data[index ].points[2] = (unsigned short) p2; m->data[index ].points[3] = 0; m->data[index++].average = 0.0; } } } } else { /* message */ lua_pushnil(L); } return 1; } /* */ static const luaL_Reg vectorlib_vector_function_list[] = { /* management */ { "new", vectorlib_new }, { "isvector", vectorlib_isvector }, { "ismesh", vectorlib_ismesh }, { "onerow", vectorlib_onerow }, { "onecolumn", vectorlib_onecolumn }, { "copy", vectorlib_copy }, { "type", vectorlib_type }, { "tostring", vectorlib_tostring }, { "totable", vectorlib_totable }, { "get", vectorlib_getvalue }, { "set", vectorlib_setvalue }, { "getrow", vectorlib_getrow }, /* table */ { "getrc", vectorlib_getvaluerc }, { "setrc", vectorlib_setvaluerc }, { "setnext", vectorlib_setnext }, /* info */ { "gettype", vectorlib_gettype }, { "setstacking", vectorlib_setstacking }, { "getstacking", vectorlib_getstacking }, { "getdimensions", vectorlib_getdimensions }, { "getepsilon", vectorlib_getepsilon }, { "iszero", vectorlib_iszero }, /* { "isidentity", vectorlib_isidentity }, */ /* todo */ /* operators */ { "add", vectorlib_add }, { "div", vectorlib_div }, { "mul", vectorlib_mul }, { "sub", vectorlib_sub }, { "negate", vectorlib_neg }, { "equal", vectorlib_eq }, /* functions */ { "round", vectorlib_round }, { "floor", vectorlib_floor }, { "ceiling", vectorlib_ceiling }, { "truncate", vectorlib_truncate }, { "inverse", vectorlib_inverse }, { "rowechelon", vectorlib_rowechelon }, { "identity", vectorlib_identity }, { "transpose", vectorlib_transpose }, { "normalize", vectorlib_normalize }, { "homogenize", vectorlib_homogenize }, { "determinant", vectorlib_determinant }, { "issingular", vectorlib_issingular }, { "inner", vectorlib_inner }, /* maybe: innerproduct */ { "product", vectorlib_product }, { "crossproduct", vectorlib_crossproduct }, /* manipulations */ { "concat", vectorlib_concat }, { "slice", vectorlib_slice }, { "delete", vectorlib_delete }, { "remove", vectorlib_remove }, { "insert", vectorlib_insert }, { "replace", vectorlib_replace }, { "swap", vectorlib_swap }, { "exchange", vectorlib_exchange }, { "append", vectorlib_append }, /* nothing more */ { NULL, NULL }, }; static const luaL_Reg vectorlib_mesh_function_list[] = { { "new", vectorlib_mesh_new }, { "ismesh", vectorlib_ismesh }, { "type", vectorlib_mesh_type }, { "tostring", vectorlib_mesh_tostring }, { "totable", vectorlib_mesh_totable }, { "getdimensions", vectorlib_mesh_getdimensions }, { "get", vectorlib_mesh_getvalue }, /* table when third is true */ { "set", vectorlib_mesh_setvalue }, /* nothing more */ { NULL, NULL }, }; static const luaL_Reg vectorlib_contour_function_list[] = { /* for experiments with mp */ { "average", vectorlib_contour_average }, // { "bounds", vectorlib_contour_bounds }, { "triangulate", vectorlib_contour_triangulate }, { "sort", vectorlib_contour_sort }, { "getmesh", vectorlib_contour_getmesh }, { "getarea", vectorlib_contour_getarea }, { "checkoverlap", vectorlib_contour_checkoverlap }, { "collectoverlap", vectorlib_contour_collectoverlap }, /* nothing more */ { NULL, NULL }, }; static const luaL_Reg vectorlib_vector_metatable[] = { { "__len", vectorlib_getlength }, { "__index", vectorlib_getvalue }, { "__newindex", vectorlib_setvalue }, { "__tostring", vectorlib_tostring }, { "__add", vectorlib_add }, { "__div", vectorlib_div }, { "__mul", vectorlib_mul }, { "__sub", vectorlib_sub }, { "__unm", vectorlib_neg }, { "__eq", vectorlib_eq }, { "__concat", vectorlib_concat }, { "__call", vectorlib_setnext }, { NULL, NULL }, }; static const luaL_Reg vectorlib_mesh_metatable[] = { { "__len", vectorlib_mesh_getlength }, { "__index", vectorlib_mesh_index }, { "__newindex", vectorlib_mesh_setvalue }, { "__tostring", vectorlib_mesh_tostring }, { NULL, NULL }, }; int luaopen_vector(lua_State *L) { luaL_newmetatable(L, VECTOR_METATABLE_INSTANCE); luaL_setfuncs(L, vectorlib_vector_metatable, 0); luaL_newmetatable(L, MESH_METATABLE_INSTANCE); luaL_setfuncs(L, vectorlib_mesh_metatable, 0); /* vector.* */ lua_newtable(L); luaL_setfuncs(L, vectorlib_vector_function_list, 0); /* vector.mesh */ // lua_pushliteral(L, "mesh"); lua_pushstring(L, lua_key(mesh)); lua_newtable(L); luaL_setfuncs(L, vectorlib_mesh_function_list, 0); lua_rawset(L, -3); /* vector.contour */ // lua_pushstring(L, lua_key(contour)); lua_pushliteral(L, "contour"); lua_newtable(L); luaL_setfuncs(L, vectorlib_contour_function_list, 0); lua_rawset(L, -3); return 1; }