Index: trunk/src/lua/lua_area.cc
===================================================================
--- trunk/src/lua/lua_area.cc	(revision 176)
+++ trunk/src/lua/lua_area.cc	(revision 176)
@@ -0,0 +1,489 @@
+// Copyright (C) 2012-2013 ChaosForge / Kornel Kisielewicz
+// http://chaosforge.org/
+//
+// This file is part of NV Libraries.
+// For conditions of distribution and use, see copyright notice in nv.hh
+
+#include "nv/lua/lua_area.hh"
+
+#include "nv/lua/lua_raw.hh"
+#include "nv/string.hh"
+#include "nv/random.hh"
+
+#define NLUA_AREA_METATABLE "area"
+
+bool nlua_is_area( lua_State* L, int index )
+{
+	return luaL_testudata( L, index, NLUA_AREA_METATABLE ) != 0;
+}
+
+nv::rectangle nlua_to_area( lua_State* L, int index )
+{
+	return *(nv::rectangle*)luaL_checkudata( L, index, NLUA_AREA_METATABLE );
+}
+
+nv::rectangle* nlua_to_parea( lua_State* L, int index )
+{
+	return (nv::rectangle*)luaL_checkudata( L, index, NLUA_AREA_METATABLE );
+}
+
+void nlua_push_area( lua_State* L, const nv::rectangle& v )
+{
+	new (lua_newuserdata(L, sizeof(nv::rectangle))) nv::rectangle(v);
+	luaL_setmetatable( L, NLUA_AREA_METATABLE );
+}
+
+void nlua_area_construct( lua_State* L, int sidx )
+{
+	if ( nlua_is_coord( L, sidx ) )
+	{
+		if ( nlua_is_coord( L, sidx+1 ) )
+		{
+			nv::rectangle a( nlua_to_coord( L, sidx ), nlua_to_coord( L, sidx+1 ) );
+			nlua_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 ) )
+		);
+	nlua_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, nlua_to_area( L, 1 ) == nlua_to_area( L, 2 ) );
+	return 1;
+}
+
+static int nlua_area_get( lua_State* L )
+{
+	nv::rectangle r( nlua_to_area( L, 1 ) );
+	nlua_push_coord( L, r.ul );
+	nlua_push_coord( L, r.lr );
+	return 2;
+}
+
+static int nlua_area_clone( lua_State* L )
+{
+	nlua_push_area( L, *nlua_to_parea( L, 1 ) );
+	return 1;
+}
+
+static int nlua_area_index( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	std::size_t l;
+	const char* index = lua_tolstring( L, 2, &l );
+	if ( l == 1 && index[0] == 'a' )
+	{
+		nlua_push_coord( L, a->ul );
+	}
+	else if ( l == 1 && index[0] == 'b' )
+	{
+		nlua_push_coord( L, a->lr );
+	}
+	else
+	{
+		luaL_getmetafield( L, 1, "__base" );
+		lua_pushvalue( L, 2 );
+		lua_rawget( L, -2 );
+	}
+	return 1;
+}
+
+static int nlua_area_newindex( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	std::size_t l;
+	const char* index = lua_tolstring( L, 2, &l );
+	nv::ivec2 value( nlua_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( nlua_to_parea( L, lua_upvalueindex(1) ) );
+	nv::ivec2*     c( nlua_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;
+		}
+	}
+
+	nlua_push_coord( L, *c );
+	return 1;
+}
+
+static int nlua_area_coords( lua_State* L )
+{
+	nv::rectangle* a( nlua_to_parea( L, lua_upvalueindex(1) ) );
+	nv::ivec2      c( a->ul );
+	c.x--;
+	nlua_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( nlua_to_parea( L, lua_upvalueindex(1) ) );
+	nv::ivec2*     c( nlua_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;
+
+
+	nlua_push_coord( L, *c );
+	return 1;
+}
+
+static int nlua_area_edges( lua_State* L )
+{
+	nv::rectangle*   a( nlua_to_parea( L, lua_upvalueindex(1) ) );
+	nv::ivec2 c( a->ul );
+	c.x--;
+	nlua_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 = 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 = nlua_to_parea( L, 1 );
+
+	lua_createtable(L, 4, 0);
+	nlua_push_coord( L, a->ul );
+	lua_rawseti( L, -2, 1 );
+	nlua_push_coord( L, a->ur() );
+	lua_rawseti( L, -2, 2 );
+	nlua_push_coord( L, a->ll() );
+	lua_rawseti( L, -2, 3 );
+	nlua_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 = nlua_to_parea( L, 1 );
+	a->shrink( lua_tointeger( L, 2 ) );
+	return 0;
+}
+
+static int nlua_area_shrinked( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	nlua_push_area( L, a->shrinked( lua_tointeger( L, 2 ) ) );
+	return 1;
+}
+
+static int nlua_area_expand( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	a->expand( lua_tointeger( L, 2 ) );
+	return 0;
+}
+
+static int nlua_area_expanded( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	nlua_push_area( L, a->expanded( lua_tointeger( L, 2 ) ) );
+	return 1;
+}
+
+static int nlua_area_clamp( lua_State* L )
+{
+	nv::rectangle* a1 = nlua_to_parea( L, 1 );
+	nv::rectangle* a2 = nlua_to_parea( L, 2 );
+	a1->clamp_to( *a2 );
+	return 0;
+}
+
+static int nlua_area_clamped( lua_State* L )
+{
+	nv::rectangle  a1 = nlua_to_area( L, 1 );
+	nv::rectangle* a2 = nlua_to_parea( L, 2 );
+	a1.clamp_to( *a2 );
+	nlua_push_area( L, a1 );
+	return 1;
+}
+
+static int nlua_area_clamp_coord( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	nv::ivec2*     c = nlua_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 = nlua_to_parea( L, 1 );
+	nv::ivec2*     c = nlua_to_pcoord( L, 2 );
+	nlua_push_coord( L, glm::clamp( *c, a->ul, a->lr ) );
+	return 0;
+}
+
+static int nlua_area_fix( lua_State* L )
+{
+	nv::rectangle* a = nlua_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 = nlua_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 = nlua_to_parea( L, 1 );
+	nlua_push_coord( L, a->ul - a->lr + nv::ivec2(1,1) );
+	return 1;
+}
+
+static int nlua_area_size( lua_State* L )
+{
+	nv::rectangle* a = nlua_to_parea( L, 1 );
+	lua_pushinteger( L, a->get_enclosed_area() );
+	return 1;
+}
+
+static int nlua_area_contains( lua_State* L )
+{
+	lua_pushboolean( L, nlua_to_parea( L, 1 )->contains( nlua_to_coord( L, 2 ) ) );
+	return 1;
+}
+
+static int nlua_area_is_edge( lua_State* L )
+{
+	nv::rectangle a = nlua_to_area( L, 1 );
+	nv::ivec2     c = nlua_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 = nlua_to_coord( L, 1 );
+	int amount = lua_tointeger( L, 1 );
+	nv::ivec2 shift( amount, amount );
+	nlua_push_area( L, nv::rectangle( c - shift, c + shift ) );
+	return 1;
+}
+
+static int nlua_area_tostring( lua_State* L )
+{
+	nv::rectangle a = nlua_to_area( L, 1 );
+	std::string s = "(";
+	s += nv::to_string(a.ul.x);
+	s += ",";
+	s += nv::to_string(a.ul.y);
+	s += "x";
+	s += nv::to_string(a.lr.x);
+	s += ",";
+	s += nv::to_string(a.lr.y);
+	s += ")";
+	lua_pushstring( L, s.c_str() );
+	return 1;
+}
+
+static int nlua_area_random_edge_coord( lua_State* L )
+{
+	nv::rectangle area = nlua_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);
+	}
+	nlua_push_coord( L, result );
+	return 1;
+}
+
+static int nlua_area_random_inner_edge_coord( lua_State* L )
+{
+	nv::rectangle area = nlua_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);
+	}
+	nlua_push_coord( L, result );
+	return 1;
+}
+
+static int nlua_area_random_coord( lua_State* L )
+{
+	nv::rectangle area = nlua_to_area( L, 1 );
+	nlua_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  = nlua_to_area( L, 1 );
+	nv::ivec2     dim   = nlua_to_coord( L, 2 );
+	nv::ivec2     start = nv::random::get().range( area.ul, area.lr - dim );
+	nlua_push_area( L, nv::rectangle( start, start + dim ) );
+	return 1;
+}
+
+
+int luaopen_area( lua_State * L )
+{
+	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, NLUA_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 nlua_register_area( lua_State* L )
+{
+	int stack = lua_gettop( L );
+	luaL_requiref(L, "area", luaopen_area, 1);
+	lua_settop( L, stack );
+}
+
