// Copyright (C) 2012-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/lua/lua_area.hh" #include "nv/lua/lua_raw.hh" #include "nv/core/random.hh" const char* nv::lua::detail::AREA_METATABLE = "area"; using nv::lua::detail::is_coord; using nv::lua::detail::to_coord; using nv::lua::detail::to_pcoord; using nv::lua::detail::push_coord; using nv::lua::detail::is_area; using nv::lua::detail::to_area; using nv::lua::detail::to_parea; using nv::lua::detail::push_area; static void nlua_area_construct( lua_State* L, int sidx ) { if ( is_coord( L, sidx ) ) { if ( is_coord( L, sidx+1 ) ) { nv::rectangle a( to_coord( L, sidx ), to_coord( L, sidx+1 ) ); push_area( L, a ); return; } // TODO: coord, width, height } nv::rectangle a( nv::ivec2( lua_tointeger( L, sidx ), lua_tointeger( L, sidx+1 ) ), nv::ivec2( lua_tointeger( L, sidx+2 ), lua_tointeger( L, sidx+3 ) ) ); push_area( L, a ); } static int nlua_area_new( lua_State* L ) { nlua_area_construct( L, 1 ); return 1; } static int nlua_area_call( lua_State* L ) { nlua_area_construct( L, 2 ); return 1; } static int nlua_area_eq( lua_State* L ) { lua_pushboolean( L, to_area( L, 1 ) == to_area( L, 2 ) ); return 1; } static int nlua_area_get( lua_State* L ) { nv::rectangle r( to_area( L, 1 ) ); push_coord( L, r.ul ); push_coord( L, r.lr ); return 2; } static int nlua_area_clone( lua_State* L ) { push_area( L, *to_parea( L, 1 ) ); return 1; } static int nlua_area_index( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); nv::size_t l; const char* index = lua_tolstring( L, 2, &l ); if ( l == 1 && index[0] == 'a' ) { push_coord( L, a->ul ); } else if ( l == 1 && index[0] == 'b' ) { push_coord( L, a->lr ); } else { luaL_getmetafield( L, 1, "__functions" ); lua_pushvalue( L, 2 ); lua_rawget( L, -2 ); } return 1; } static int nlua_area_newindex( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); nv::size_t l; const char* index = lua_tolstring( L, 2, &l ); nv::ivec2 value( to_coord( L, 3 ) ); if ( l == 1 && index[0] == 'a' ) { a->ul = value; } else if ( l == 1 && index[0] == 'b' ) { a->lr = value; } return 0; } static int lua_area_coords_closure( lua_State* L ) { nv::rectangle* a( to_parea( L, lua_upvalueindex(1) ) ); nv::ivec2* c( to_pcoord( L, lua_upvalueindex(2) ) ); c->x++; if ( c->x > a->lr.x ) { c->x = a->ul.x; c->y++; if (c->y > a->lr.y) { lua_pushnil( L ); return 1; } } push_coord( L, *c ); return 1; } static int nlua_area_coords( lua_State* L ) { nv::rectangle* a( to_parea( L, 1 ) ); nv::ivec2 c( a->ul ); c.x--; push_coord( L, c ); lua_pushcclosure( L, lua_area_coords_closure, 2 ); return 1; } static int nlua_area_edges_closure( lua_State* L ) { nv::rectangle* a( to_parea( L, lua_upvalueindex(1) ) ); nv::ivec2* c( to_pcoord( L, lua_upvalueindex(2) ) ); c->x++; if ( c->x > a->lr.x ) { c->x = a->ul.x; c->y++; if (c->y > a->lr.y) { lua_pushnil( L ); return 1; } } if (c->y != a->ul.y && c->y != a->lr.y && c->x == a->ul.x + 1 ) c->x = a->ul.x; push_coord( L, *c ); return 1; } static int nlua_area_edges( lua_State* L ) { nv::rectangle* a( to_parea( L, 1 ) ); nv::ivec2 c( a->ul ); c.x--; push_coord( L, c ); lua_pushcclosure( L, nlua_area_edges_closure, 2 ); return 1; } static int nlua_area_corners_closure( lua_State* L ) { int index = static_cast< int >( lua_tointeger( L, lua_upvalueindex(2) ) + 1 ); lua_pushinteger( L, index ); lua_replace( L, lua_upvalueindex(2) ); // update lua_rawgeti( L, lua_upvalueindex(1), index ); // get value return 1; } static int nlua_area_corners( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); lua_createtable(L, 4, 0); push_coord( L, a->ul ); lua_rawseti( L, -2, 1 ); push_coord( L, a->ur() ); lua_rawseti( L, -2, 2 ); push_coord( L, a->ll() ); lua_rawseti( L, -2, 3 ); push_coord( L, a->lr ); lua_rawseti( L, -2, 4 ); lua_pushinteger(L, 0); lua_pushcclosure( L, nlua_area_corners_closure, 2 ); return 1; } static int nlua_area_shrink( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); a->shrink( static_cast< int >( lua_tointeger( L, 2 ) ) ); return 0; } static int nlua_area_shrinked( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); push_area( L, a->shrinked( static_cast< int >( lua_tointeger( L, 2 ) ) ) ); return 1; } static int nlua_area_expand( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); a->expand( static_cast< int >( lua_tointeger( L, 2 ) ) ); return 0; } static int nlua_area_expanded( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); push_area( L, a->expanded( static_cast< int >( lua_tointeger( L, 2 ) ) ) ); return 1; } static int nlua_area_clamp( lua_State* L ) { nv::rectangle* a1 = to_parea( L, 1 ); nv::rectangle* a2 = to_parea( L, 2 ); a1->clamp_to( *a2 ); return 0; } static int nlua_area_clamped( lua_State* L ) { nv::rectangle a1 = to_area( L, 1 ); nv::rectangle* a2 = to_parea( L, 2 ); a1.clamp_to( *a2 ); push_area( L, a1 ); return 1; } static int nlua_area_clamp_coord( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); nv::ivec2* c = to_pcoord( L, 2 ); *c = glm::clamp( *c, a->ul, a->lr ); return 0; } static int nlua_area_clamped_coord( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); nv::ivec2* c = to_pcoord( L, 2 ); push_coord( L, glm::clamp( *c, a->ul, a->lr ) ); return 0; } static int nlua_area_fix( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); if ( a->ul.x > a->lr.x ) a->ul.x = a->lr.x; if ( a->ul.y > a->lr.y ) a->ul.y = a->lr.y; return 0; } static int nlua_area_proper( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); lua_pushboolean( L, a->ul.x <= a->lr.x && a->ul.y <= a->lr.y ); return 1; } static int nlua_area_dim( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); push_coord( L, a->ul - a->lr + nv::ivec2(1,1) ); return 1; } static int nlua_area_size( lua_State* L ) { nv::rectangle* a = to_parea( L, 1 ); lua_pushinteger( L, a->get_enclosed_area() ); return 1; } static int nlua_area_contains( lua_State* L ) { lua_pushboolean( L, to_parea( L, 1 )->contains( to_coord( L, 2 ) ) ); return 1; } static int nlua_area_is_edge( lua_State* L ) { nv::rectangle a = to_area( L, 1 ); nv::ivec2 c = to_coord( L, 2 ); lua_pushboolean( L, a.contains( c ) && ( c.x == a.ul.x || c.x == a.lr.x || c.y == a.ul.y || c.y == a.lr.y ) ); return 1; } static int nlua_area_around( lua_State* L ) { nv::ivec2 c = to_coord( L, 1 ); int amount = static_cast< int >( lua_tointeger( L, 1 ) ); nv::ivec2 shift( amount, amount ); push_area( L, nv::rectangle( c - shift, c + shift ) ); return 1; } static int nlua_area_tostring( lua_State* L ) { nv::rectangle a = to_area( L, 1 ); lua_pushfstring( L, "(%d,%dx%d,%d)", a.ul.x, a.ul.y, a.lr.x, a.lr.y ); return 1; } static int nlua_area_random_edge_coord( lua_State* L ) { nv::rectangle area = to_area( L, 1 ); nv::ivec2 a = area.ul; nv::ivec2 b = area.lr; nv::sint32 xs = ( b.x - a.x ) + 1; nv::sint32 ys = ( b.y - a.y ) - 1; nv::sint32 roll = nv::random::get().srand( 2 * xs + 2 * ys ); nv::ivec2 result; if ( roll < 2 * xs ) { result = ( roll < xs ) ? nv::ivec2( a.x + roll, a.y ) : nv::ivec2( a.x + roll - xs, b.y ); } else { roll -= 2 * xs; result = ( roll < ys ) ? nv::ivec2( a.x, a.y + roll + 1 ) : nv::ivec2( b.x, a.y + roll - ys + 1); } push_coord( L, result ); return 1; } static int nlua_area_random_inner_edge_coord( lua_State* L ) { nv::rectangle area = to_area( L, 1 ); nv::ivec2 a = area.ul; nv::ivec2 b = area.lr; nv::sint32 xs = ( b.x - a.x ) - 1; nv::sint32 ys = ( b.y - a.y ) - 1; nv::sint32 roll = nv::random::get().srand( 2 * xs + 2 * ys ); nv::ivec2 result; if ( roll < 2 * xs ) { result = ( roll < xs ) ? nv::ivec2( a.x + roll + 1, a.y ) : nv::ivec2( a.x + roll - xs + 1, b.y ); } else { roll -= 2 * xs; result = ( roll < ys ) ? nv::ivec2( a.x, a.y + roll + 1 ) : nv::ivec2( b.x, a.y + roll - ys + 1); } push_coord( L, result ); return 1; } static int nlua_area_random_coord( lua_State* L ) { nv::rectangle area = to_area( L, 1 ); push_coord( L, nv::random::get().range( area.ul, area.lr ) ); return 1; } static int nlua_area_random_subarea( lua_State* L ) { nv::rectangle area = to_area( L, 1 ); nv::ivec2 dim = to_coord( L, 2 ); nv::ivec2 start = nv::random::get().range( area.ul, area.lr - dim ); push_area( L, nv::rectangle( start, start + dim ) ); return 1; } static int luaopen_area( lua_State * L ) { NV_LUA_STACK_ASSERT( L, 1 ); static const struct luaL_Reg nlua_area_sf [] = { { "new", nlua_area_new }, { "around", nlua_area_around }, {NULL, NULL} }; static const struct luaL_Reg nlua_area_f [] = { { "get", nlua_area_get }, { "clone", nlua_area_clone }, { "coords", nlua_area_coords }, { "edges", nlua_area_edges }, { "corners", nlua_area_corners }, { "tostring", nlua_area_tostring }, { "shrink", nlua_area_shrink }, { "shrinked", nlua_area_shrinked }, { "expand", nlua_area_expand }, { "expanded", nlua_area_expanded }, { "clamp", nlua_area_clamp }, { "clamped", nlua_area_clamped }, { "clamp_coord", nlua_area_clamp_coord }, { "clamped_coord", nlua_area_clamped_coord }, { "fix", nlua_area_fix }, { "proper", nlua_area_proper }, { "dim", nlua_area_dim }, { "size", nlua_area_size }, { "contains", nlua_area_contains }, { "is_edge", nlua_area_is_edge }, { "random_subarea", nlua_area_random_subarea }, { "random_coord", nlua_area_random_coord }, { "random_edge_coord", nlua_area_random_edge_coord }, { "random_inner_edge_coord", nlua_area_random_inner_edge_coord }, {NULL, NULL} }; static const struct luaL_Reg nlua_area_sm [] = { { "__call", nlua_area_call }, {NULL, NULL} }; static const struct luaL_Reg nlua_area_m [] = { { "__eq", nlua_area_eq }, { "__call", nlua_area_coords }, { "__index", nlua_area_index }, { "__newindex", nlua_area_newindex }, { "__tostring", nlua_area_tostring }, {NULL, NULL} }; luaL_newmetatable( L, nv::lua::detail::AREA_METATABLE ); nlua_register( L, nlua_area_m, -1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_area_f, -1 ); lua_setfield(L, -2, "__functions" ); lua_pop( L, 1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_area_sf, -1 ); lua_createtable( L, 0, 0 ); nlua_register( L, nlua_area_sm, -1 ); lua_setmetatable( L, -2 ); return 1; } void nv::lua::register_area( lua_State* L ) { int stack = lua_gettop( L ); luaL_requiref(L, "area", luaopen_area, 1); lua_settop( L, stack ); }