lua-users home
lua-l archive

Re: Two indexing methods of a vector. How to ?

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


It was thus said that the Great Charles Melice once stated:
> I defined (with the C API) a Vector object based on full user data.
> 
> 	static Vec4 *Vget(lua_State *L, int i)
> 	{
> 		if (luaL_checkudata(L,i,MYTYPE)==NULL) ERROR(MYTYPE);
> 		return (Vec4*)lua_touserdata(L,i);
> 	}
> 
> Now, I'm interested to be able to index the 'x, y, z, t' coordinates with
> numeric indexes (1,2,3,4). For instance :
> 
> 	local v = Vec4(11,22,33,44)
> 	print(v.y) -- 22
> 	v:normalize()
> 	print(v) -- Vec4(0.27, 0.53, 0.80, 1.07)
> 	print(v[2]) -- 0.53 ???
> 
> 	v[2]=22.22 -- ???
> 	print(v.y) -- 22.22
> 
> I don't see how to do that ?
 First, you need to create a metatable to associate with your userdata. 
When you register the module, you create the metatable:
	#define MY_VEC4_TYPE	"Some String to denote a Vec4"
	
	/*----------------------------------------------------------------
	; I'm not sure how you actually define this, but this is a sample
	; anyway.
	;-----------------------------------------------------------------*/
	
	typedef struct
	{
	 double data[4];
	} Vec4;
	
	/*--------------------------------------------------------------------
	; This function will handle references in an expression. The naming
	; convention I use is <modname>meta_<method>, so for instance, the
	; Lua method __index, for this example, will end up being named
	; vec4meta___index(). You don't have to follow this convention, but
	; I find it nice.
	;
	; Also, luaL_checkudata() will throw an error if the type is
	; incorrect, so you don't need to check the return code.
	;---------------------------------------------------------------------*/
	
	static int vec4meta___index(lua_State *L)
	{
	 Vec4 *vec = luaL_checkudata(L,1,MY_VEC_TYPE);
	 int idx;
	 
	 if (lua_type(L,2) == LUA_TSTRING)
	 {
	 const char *name = lua_tostring(L,2);
	 if (strcmp(name,"x") == 0)
	 idx = 0;
	 else if (strcmp(name,"y") == 0)
	 idx = 1;
	 else if (strcmp(name,"z") == 0)
	 idx = 2;
	 else if (strcmp(name,"t") == 0)
	 idx = 3;
	 else
	 idx = -1;
	 }
	 else if (lua_type(L,2) == LUA_TNUMBER)
	 idx = lua_tointeger(L,2);
	 else
	 idx = -1;
	 
	 if ((idx < 0) || (idx > 3))
	 lua_pushnil(L);
	 else
	 lua_pushnumber(L,vec->data[idx]);
	 return 1;
	}
	
	/*------------------------------------------------------------------
	; This function will handle assignments. It looks quite a bit like
	; the __index function.
	;-----------------------------------------------------------------*/
	static int vec4meta___newindex(lua_State *L)
	{
	 Vec4 *vec = luaL_checkudata(L,1,MY_VEC_TYPE);
	 int idx;
	 
	 if (lua_type(L,2) == LUA_TSTRING)
	 {
	 const char *name = lua_tostring(L,2);
	 if (strcmp(name,"x") == 0)
	 idx = 0;
	 else if (strcmp(name,"y") == 0)
	 idx = 1;
	 else if (strcmp(name,"z") == 0)
	 idx = 2;
	 else if (strcmp(name,"t") == 0)
	 idx = 3;
	 else
	 idx = -1;
	 }
	 else if (lua_type(L,2) == LUA_TNUMBER)
	 idx = lua_tointeger(L,2);
	 else
	 idx = -1;
	 
	 if ((idx >= 0) && (idx <= 3))
	 vec->data[idx] = luaL_checknumber(L,3);
	 return 0;
	}
	
	static const luaL_Reg vec4meta[] =
	{
	 { "__index"		, vec4meta___index	},
	 { "__newindex"	, vec4meta___newindex	},
	 { NULL		, NULL			}
	};
	
	int luaopen_vec4(lua_State *L)	/* rename as appropriate */
	{
	 luaL_newmetatable(L,MY_VEC4_TYPE);
	#if LUA_VERSION_NUM == 501
	 /* Lua 5.1 */	
	 luaL_register(L,NULL,vec4meta);
	#else
	 /* Lua 5.2 or 5.3 */
	 luaL_setfuncs(L,vec4meta,0);
	#endif
	 /* rest of initialization */
	}
 Then, when you create a new userdata, you associate the metatable you
created with it:	
	Vec4* vec = lua_newuserdata(L,sizeof(Vec4));
	luaL_getmetatable(L,MY_VEC4_TYPE);
	lua_setmetatable(L,-2);
	
 And that's pretty much it.
 -spc

AltStyle によって変換されたページ (->オリジナル) /