1 /**
2  * Copyright (c) 2006-2016 LOVE Development Team
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty.  In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  *    claim that you wrote the original software. If you use this software
14  *    in a product, an acknowledgment in the product documentation would be
15  *    appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  *    misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  **/
20 
21 #include "wrap_Math.h"
22 #include "wrap_RandomGenerator.h"
23 #include "wrap_BezierCurve.h"
24 #include "wrap_CompressedData.h"
25 #include "MathModule.h"
26 #include "BezierCurve.h"
27 
28 #include <cmath>
29 #include <iostream>
30 #include <algorithm>
31 
32 // Put the Lua code directly into a raw string literal.
33 static const char math_lua[] =
34 #include "wrap_Math.lua"
35 ;
36 
37 namespace love
38 {
39 namespace math
40 {
41 
w__getRandomGenerator(lua_State * L)42 int w__getRandomGenerator(lua_State *L)
43 {
44 	RandomGenerator *t = Math::instance.getRandomGenerator();
45 	luax_pushtype(L, MATH_RANDOM_GENERATOR_ID, t);
46 	return 1;
47 }
48 
w_newRandomGenerator(lua_State * L)49 int w_newRandomGenerator(lua_State *L)
50 {
51 	RandomGenerator::Seed s;
52 	if (lua_gettop(L) > 0)
53 		s = luax_checkrandomseed(L, 1);
54 
55 	RandomGenerator *t = Math::instance.newRandomGenerator();
56 
57 	if (lua_gettop(L) > 0)
58 	{
59 		bool should_error = false;
60 
61 		try
62 		{
63 			t->setSeed(s);
64 		}
65 		catch (love::Exception &e)
66 		{
67 			t->release();
68 			should_error = true;
69 			lua_pushstring(L, e.what());
70 		}
71 
72 		if (should_error)
73 			return luaL_error(L, "%s", lua_tostring(L, -1));
74 	}
75 
76 	luax_pushtype(L, MATH_RANDOM_GENERATOR_ID, t);
77 	t->release();
78 	return 1;
79 }
80 
w_newBezierCurve(lua_State * L)81 int w_newBezierCurve(lua_State *L)
82 {
83 	std::vector<Vector> points;
84 	if (lua_istable(L, 1))
85 	{
86 		int top = (int) luax_objlen(L, 1);
87 		points.reserve(top / 2);
88 		for (int i = 1; i <= top; i += 2)
89 		{
90 			lua_rawgeti(L, 1, i);
91 			lua_rawgeti(L, 1, i+1);
92 
93 			Vector v;
94 			v.x = (float) luaL_checknumber(L, -2);
95 			v.y = (float) luaL_checknumber(L, -1);
96 			points.push_back(v);
97 
98 			lua_pop(L, 2);
99 		}
100 	}
101 	else
102 	{
103 		int top = (int) lua_gettop(L);
104 		points.reserve(top / 2);
105 		for (int i = 1; i <= top; i += 2)
106 		{
107 			Vector v;
108 			v.x = (float) luaL_checknumber(L, i);
109 			v.y = (float) luaL_checknumber(L, i+1);
110 			points.push_back(v);
111 		}
112 	}
113 
114 	BezierCurve *curve = Math::instance.newBezierCurve(points);
115 	luax_pushtype(L, MATH_BEZIER_CURVE_ID, curve);
116 	curve->release();
117 	return 1;
118 }
119 
w_triangulate(lua_State * L)120 int w_triangulate(lua_State *L)
121 {
122 	std::vector<love::Vector> vertices;
123 	if (lua_istable(L, 1))
124 	{
125 		int top = (int) luax_objlen(L, 1);
126 		vertices.reserve(top / 2);
127 		for (int i = 1; i <= top; i += 2)
128 		{
129 			lua_rawgeti(L, 1, i);
130 			lua_rawgeti(L, 1, i+1);
131 
132 			Vector v;
133 			v.x = (float) luaL_checknumber(L, -2);
134 			v.y = (float) luaL_checknumber(L, -1);
135 			vertices.push_back(v);
136 
137 			lua_pop(L, 2);
138 		}
139 	}
140 	else
141 	{
142 		int top = (int) lua_gettop(L);
143 		vertices.reserve(top / 2);
144 		for (int i = 1; i <= top; i += 2)
145 		{
146 			Vector v;
147 			v.x = (float) luaL_checknumber(L, i);
148 			v.y = (float) luaL_checknumber(L, i+1);
149 			vertices.push_back(v);
150 		}
151 	}
152 
153 	if (vertices.size() < 3)
154 		return luaL_error(L, "Need at least 3 vertices to triangulate");
155 
156 	std::vector<Triangle> triangles;
157 
158 	luax_catchexcept(L, [&]() {
159 		if (vertices.size() == 3)
160 			triangles.push_back(Triangle(vertices[0], vertices[1], vertices[2]));
161 		else
162 			triangles = Math::instance.triangulate(vertices);
163 	});
164 
165 	lua_createtable(L, (int) triangles.size(), 0);
166 	for (int i = 0; i < (int) triangles.size(); ++i)
167 	{
168 		const Triangle &tri = triangles[i];
169 
170 		lua_createtable(L, 6, 0);
171 		lua_pushnumber(L, tri.a.x);
172 		lua_rawseti(L, -2, 1);
173 		lua_pushnumber(L, tri.a.y);
174 		lua_rawseti(L, -2, 2);
175 		lua_pushnumber(L, tri.b.x);
176 		lua_rawseti(L, -2, 3);
177 		lua_pushnumber(L, tri.b.y);
178 		lua_rawseti(L, -2, 4);
179 		lua_pushnumber(L, tri.c.x);
180 		lua_rawseti(L, -2, 5);
181 		lua_pushnumber(L, tri.c.y);
182 		lua_rawseti(L, -2, 6);
183 
184 		lua_rawseti(L, -2, i+1);
185 	}
186 
187 	return 1;
188 }
189 
w_isConvex(lua_State * L)190 int w_isConvex(lua_State *L)
191 {
192 	std::vector<love::Vector> vertices;
193 	if (lua_istable(L, 1))
194 	{
195 		int top = (int) luax_objlen(L, 1);
196 		vertices.reserve(top / 2);
197 		for (int i = 1; i <= top; i += 2)
198 		{
199 			lua_rawgeti(L, 1, i);
200 			lua_rawgeti(L, 1, i+1);
201 
202 			love::Vector v;
203 			v.x = (float) luaL_checknumber(L, -2);
204 			v.y = (float) luaL_checknumber(L, -1);
205 			vertices.push_back(v);
206 
207 			lua_pop(L, 2);
208 		}
209 	}
210 	else
211 	{
212 		int top = lua_gettop(L);
213 		vertices.reserve(top / 2);
214 		for (int i = 1; i <= top; i += 2)
215 		{
216 			love::Vector v;
217 			v.x = (float) luaL_checknumber(L, i);
218 			v.y = (float) luaL_checknumber(L, i+1);
219 			vertices.push_back(v);
220 		}
221 	}
222 
223 	luax_pushboolean(L, Math::instance.isConvex(vertices));
224 	return 1;
225 }
226 
getGammaArgs(lua_State * L,float color[4])227 static int getGammaArgs(lua_State *L, float color[4])
228 {
229 	int numcomponents = 0;
230 
231 	if (lua_istable(L, 1))
232 	{
233 		int n = (int) luax_objlen(L, 1);
234 		for (int i = 1; i <= n && i <= 4; i++)
235 		{
236 			lua_rawgeti(L, 1, i);
237 			color[i - 1] = (float) luaL_checknumber(L, -1) / 255.0f;
238 			numcomponents++;
239 		}
240 
241 		lua_pop(L, numcomponents);
242 	}
243 	else
244 	{
245 		int n = lua_gettop(L);
246 		for (int i = 1; i <= n && i <= 4; i++)
247 		{
248 			color[i - 1] = (float) luaL_checknumber(L, i) / 255.0f;
249 			numcomponents++;
250 		}
251 	}
252 
253 	if (numcomponents == 0)
254 		luaL_checknumber(L, 1);
255 
256 	return numcomponents;
257 }
258 
w_gammaToLinear(lua_State * L)259 int w_gammaToLinear(lua_State *L)
260 {
261 	float color[4];
262 	int numcomponents = getGammaArgs(L, color);
263 
264 	for (int i = 0; i < numcomponents; i++)
265 	{
266 		// Alpha should always be linear.
267 		if (i < 3)
268 			color[i] = Math::instance.gammaToLinear(color[i]);
269 		lua_pushnumber(L, color[i] * 255);
270 	}
271 
272 	return numcomponents;
273 }
274 
w_linearToGamma(lua_State * L)275 int w_linearToGamma(lua_State *L)
276 {
277 	float color[4];
278 	int numcomponents = getGammaArgs(L, color);
279 
280 	for (int i = 0; i < numcomponents; i++)
281 	{
282 		// Alpha should always be linear.
283 		if (i < 3)
284 			color[i] = Math::instance.linearToGamma(color[i]);
285 		lua_pushnumber(L, color[i] * 255);
286 	}
287 
288 	return numcomponents;
289 }
290 
w_noise(lua_State * L)291 int w_noise(lua_State *L)
292 {
293 	int nargs = std::min(std::max(lua_gettop(L), 1), 4);
294 	float args[4];
295 
296 	for (int i = 0; i < nargs; i++)
297 		args[i] = (float) luaL_checknumber(L, i + 1);
298 
299 	float val = 0.0f;
300 
301 	switch (nargs)
302 	{
303 	case 1:
304 		val = Math::instance.noise(args[0]);
305 		break;
306 	case 2:
307 		val = Math::instance.noise(args[0], args[1]);
308 		break;
309 	case 3:
310 		val = Math::instance.noise(args[0], args[1], args[2]);
311 		break;
312 	case 4:
313 		val = Math::instance.noise(args[0], args[1], args[2], args[3]);
314 		break;
315 	}
316 
317 	lua_pushnumber(L, (lua_Number) val);
318 	return 1;
319 }
320 
w_compress(lua_State * L)321 int w_compress(lua_State *L)
322 {
323 	const char *fstr = lua_isnoneornil(L, 2) ? nullptr : luaL_checkstring(L, 2);
324 	Compressor::Format format = Compressor::FORMAT_LZ4;
325 
326 	if (fstr && !Compressor::getConstant(fstr, format))
327 		return luaL_error(L, "Invalid compressed data format: %s", fstr);
328 
329 	int level = (int) luaL_optnumber(L, 3, -1);
330 
331 	CompressedData *cdata = nullptr;
332 	if (lua_isstring(L, 1))
333 	{
334 		size_t rawsize = 0;
335 		const char *rawbytes = luaL_checklstring(L, 1, &rawsize);
336 		luax_catchexcept(L, [&](){ cdata = Math::instance.compress(format, rawbytes, rawsize, level); });
337 	}
338 	else
339 	{
340 		Data *rawdata = luax_checktype<Data>(L, 1, DATA_ID);
341 		luax_catchexcept(L, [&](){ cdata = Math::instance.compress(format, rawdata, level); });
342 	}
343 
344 	luax_pushtype(L, MATH_COMPRESSED_DATA_ID, cdata);
345 	return 1;
346 }
347 
w_decompress(lua_State * L)348 int w_decompress(lua_State *L)
349 {
350 	char *rawbytes = nullptr;
351 	size_t rawsize = 0;
352 
353 	if (luax_istype(L, 1, MATH_COMPRESSED_DATA_ID))
354 	{
355 		CompressedData *data = luax_checkcompresseddata(L, 1);
356 		rawsize = data->getDecompressedSize();
357 		luax_catchexcept(L, [&](){ rawbytes = Math::instance.decompress(data, rawsize); });
358 	}
359 	else
360 	{
361 		Compressor::Format format = Compressor::FORMAT_LZ4;
362 		const char *fstr = luaL_checkstring(L, 2);
363 
364 		if (!Compressor::getConstant(fstr, format))
365 			return luaL_error(L, "Invalid compressed data format: %s", fstr);
366 
367 		size_t compressedsize = 0;
368 		const char *cbytes = nullptr;
369 
370 		if (luax_istype(L, 1, DATA_ID))
371 		{
372 			Data *data = luax_checktype<Data>(L, 1, DATA_ID);
373 			cbytes = (const char *) data->getData();
374 			compressedsize = data->getSize();
375 		}
376 		else
377 			cbytes = luaL_checklstring(L, 1, &compressedsize);
378 
379 		luax_catchexcept(L, [&](){ rawbytes = Math::instance.decompress(format, cbytes, compressedsize, rawsize); });
380 	}
381 
382 	lua_pushlstring(L, rawbytes, rawsize);
383 	delete[] rawbytes;
384 
385 	return 1;
386 }
387 
388 // C functions in a struct, necessary for the FFI versions of math functions.
389 struct FFI_Math
390 {
391 	float (*noise1)(float x);
392 	float (*noise2)(float x, float y);
393 	float (*noise3)(float x, float y, float z);
394 	float (*noise4)(float x, float y, float z, float w);
395 
396 	float (*gammaToLinear)(float c);
397 	float (*linearToGamma)(float c);
398 };
399 
400 static FFI_Math ffifuncs =
401 {
402 	[](float x) -> float // noise1
__anon1ca6daec0602() 403 	{
404 		return Math::instance.noise(x);
405 	},
406 	[](float x, float y) -> float // noise2
__anon1ca6daec0702() 407 	{
408 		return Math::instance.noise(x, y);
409 	},
410 	[](float x, float y, float z) -> float // noise3
__anon1ca6daec0802() 411 	{
412 		return Math::instance.noise(x, y, z);
413 	},
414 	[](float x, float y, float z, float w) -> float // noise4
__anon1ca6daec0902() 415 	{
416 		return Math::instance.noise(x, y, z, w);
417 	},
418 
419 	[](float c) -> float // gammaToLinear
__anon1ca6daec0a02() 420 	{
421 		return Math::instance.gammaToLinear(c);
422 	},
423 	[](float c) -> float // linearToGamma
__anon1ca6daec0b02() 424 	{
425 		return Math::instance.linearToGamma(c);
426 	}
427 };
428 
429 // List of functions to wrap.
430 static const luaL_Reg functions[] =
431 {
432 	// love.math.random etc. are defined in wrap_Math.lua.
433 
434 	{ "_getRandomGenerator", w__getRandomGenerator },
435 	{ "newRandomGenerator", w_newRandomGenerator },
436 	{ "newBezierCurve", w_newBezierCurve },
437 	{ "triangulate", w_triangulate },
438 	{ "isConvex", w_isConvex },
439 	{ "gammaToLinear", w_gammaToLinear },
440 	{ "linearToGamma", w_linearToGamma },
441 	{ "noise", w_noise },
442 	{ "compress", w_compress },
443 	{ "decompress", w_decompress },
444 	{ 0, 0 }
445 };
446 
447 static const lua_CFunction types[] =
448 {
449 	luaopen_randomgenerator,
450 	luaopen_beziercurve,
451 	luaopen_compresseddata,
452 	0
453 };
454 
luaopen_love_math(lua_State * L)455 extern "C" int luaopen_love_math(lua_State *L)
456 {
457 	Math::instance.retain();
458 
459 	WrappedModule w;
460 	w.module = &Math::instance;
461 	w.name = "math";
462 	w.type = MODULE_ID;
463 	w.functions = functions;
464 	w.types = types;
465 
466 	int n = luax_register_module(L, w);
467 
468 	// Execute wrap_Math.lua, sending the math table and ffifuncs pointer as args.
469 	luaL_loadbuffer(L, math_lua, sizeof(math_lua), "wrap_Math.lua");
470 	lua_pushvalue(L, -2);
471 	lua_pushlightuserdata(L, &ffifuncs);
472 	lua_call(L, 2, 0);
473 
474 	return n;
475 }
476 
477 } // math
478 } // love
479