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.
38static const char math_lua[] =
39#include "wrap_Math.lua"
40;
41
42namespace love
43{
44namespace math
45{
46
47#define instance() (Module::getInstance<Math>(Module::M_MATH))
48
49int w__getRandomGenerator(lua_State *L)
50{
51 RandomGenerator *t = instance()->getRandomGenerator();
52 luax_pushtype(L, t);
53 return 1;
54}
55
56int 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
88int 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
127int 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
152int 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
222int 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
259static 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
291int 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
307int 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
323int 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
353int 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
385int 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.
429struct 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
440static 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.
452static 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
473static const lua_CFunction types[] =
474{
475 luaopen_randomgenerator,
476 luaopen_beziercurve,
477 luaopen_transform,
478 0
479};
480
481extern "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