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