diff --git a/src/common/math.cpp b/src/common/math.cpp deleted file mode 100644 index 41c881518..000000000 --- a/src/common/math.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "math.h" -#include -#include - -namespace -{ - // The Box–Muller transform generates two random numbers, one of which we - // cache here. A value of +infinity is used to signal the cache is invalid - // and that new numbers have to be generated. - float last_randnormal = std::numeric_limits::infinity(); -} - -namespace love -{ - -float random_normal(float o) -{ - // number in cache? - if (last_randnormal != std::numeric_limits::infinity()) - { - float r = last_randnormal; - last_randnormal = std::numeric_limits::infinity(); - return r * o; - } - - // else: generate numbers using the Box-Muller transform - float a = sqrt(-2.0f * log(1. - random())); - float b = float(LOVE_M_PI) * 2.0f * (1. - random()); - last_randnormal = a * cos(b); - return a * sin(b) * o; -} - -} // namespace love diff --git a/src/common/math.h b/src/common/math.h index 611aba010..b7457bf43 100644 --- a/src/common/math.h +++ b/src/common/math.h @@ -68,6 +68,14 @@ struct vertex float s, t; }; +struct Triangle +{ + Triangle(const vertex &x, const vertex &y, const vertex &z) + : a(x), b(y), c(z) + {} + vertex a, b, c; +}; + inline int next_p2(int x) { x += (x == 0); @@ -81,42 +89,6 @@ inline float next_p2(float x) return static_cast(next_p2(static_cast(x))); } -/** - * Draws a random number from a uniform distribution. - * @returns Uniformly distributed random number in [0:1). - */ -inline float random() -{ - // to satisfy picky compilers... - return float(double(rand() % RAND_MAX) / double(RAND_MAX)); -} - -/** - * Draws a random number from a uniform distribution. - * @return Uniformly distributed random number in [0:max). - */ -inline float random(float max) -{ - return random() * max; -} - -/** - * Draws a random number from a uniform distribution. - * @return Uniformly distributed random number in [min:max). - */ -inline float random(float min, float max) -{ - return random(max - min) + min; -} - -/** - * Draws a random number from a normal/gaussian distribution. - * @param o Standard deviation of the distribution. - * @returns Normal distributed random number with mean 0 and variance o^2. - */ -float random_normal(float o = 1.); -#define random_gaussian random_normal - } // love #endif // LOVE_MATH_H diff --git a/src/modules/graphics/opengl/ParticleSystem.cpp b/src/modules/graphics/opengl/ParticleSystem.cpp index ac739e817..4ab0fdc53 100644 --- a/src/modules/graphics/opengl/ParticleSystem.cpp +++ b/src/modules/graphics/opengl/ParticleSystem.cpp @@ -21,11 +21,14 @@ #include "ParticleSystem.h" #include "common/math.h" +#include "modules/math/Math.h" #include "OpenGL.h" #include #include +using love::math::Math; + namespace love { namespace graphics @@ -45,7 +48,7 @@ float calculate_variation(float inner, float outer, float var) { float low = inner - (outer/2.0f)*var; float high = inner + (outer/2.0f)*var; - float r = random(); + float r = Math::instance.random(); return low*(1-r)+high*r; } @@ -124,7 +127,7 @@ void ParticleSystem::add() if (min == max) pLast->life = min; else - pLast->life = random(min, max); + pLast->life = Math::instance.random(min, max); pLast->lifetime = pLast->life; pLast->position[0] = position.getX(); @@ -133,12 +136,12 @@ void ParticleSystem::add() switch (areaSpreadDistribution) { case DISTRIBUTION_UNIFORM: - pLast->position[0] += random(-areaSpread.getX(), areaSpread.getX()); - pLast->position[1] += random(-areaSpread.getY(), areaSpread.getY()); + pLast->position[0] += Math::instance.random(-areaSpread.getX(), areaSpread.getX()); + pLast->position[1] += Math::instance.random(-areaSpread.getY(), areaSpread.getY()); break; case DISTRIBUTION_NORMAL: - pLast->position[0] += random_normal(areaSpread.getX()); - pLast->position[1] += random_normal(areaSpread.getY()); + pLast->position[0] += Math::instance.randnormal(areaSpread.getX()); + pLast->position[1] += Math::instance.randnormal(areaSpread.getY()); break; case DISTRIBUTION_NONE: default: @@ -147,37 +150,37 @@ void ParticleSystem::add() min = direction - spread/2.0f; max = direction + spread/2.0f; - pLast->direction = random(min, max); + pLast->direction = Math::instance.random(min, max); pLast->origin = position; min = speedMin; max = speedMax; - float speed = random(min, max); + float speed = Math::instance.random(min, max); pLast->speed = love::Vector(cos(pLast->direction), sin(pLast->direction)); pLast->speed *= speed; min = gravityMin; max = gravityMax; - pLast->gravity = random(min, max); + pLast->gravity = Math::instance.random(min, max); min = radialAccelerationMin; max = radialAccelerationMax; - pLast->radialAcceleration = random(min, max); + pLast->radialAcceleration = Math::instance.random(min, max); min = tangentialAccelerationMin; max = tangentialAccelerationMax; - pLast->tangentialAcceleration = random(min, max); + pLast->tangentialAcceleration = Math::instance.random(min, max); - pLast->sizeOffset = random(sizeVariation); // time offset for size change - pLast->sizeIntervalSize = (1.0f - random(sizeVariation)) - pLast->sizeOffset; + pLast->sizeOffset = Math::instance.random(sizeVariation); // time offset for size change + pLast->sizeIntervalSize = (1.0f - Math::instance.random(sizeVariation)) - pLast->sizeOffset; pLast->size = sizes[(size_t)(pLast->sizeOffset - .5f) * (sizes.size() - 1)]; min = rotationMin; max = rotationMax; pLast->spinStart = calculate_variation(spinStart, spinEnd, spinVariation); pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation); - pLast->rotation = random(min, max); + pLast->rotation = Math::instance.random(min, max); pLast->color = colors[0]; diff --git a/src/modules/math/Math.cpp b/src/modules/math/Math.cpp new file mode 100644 index 000000000..c836cb0be --- /dev/null +++ b/src/modules/math/Math.cpp @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2006-2013 LOVE Development Team + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + **/ + +#include "modules/math/Math.h" +#include "common/math.h" + +#include +#include +#include + +using namespace std; +using love::vertex; + +namespace +{ + + // check if an angle is oriented counter clockwise + inline bool is_oriented_ccw(const vertex &a, const vertex &b, const vertex &c) + { + // return det(b-a, c-a) >= 0 + return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) >= 0; + } + + // check if a and b are on the same side of the line c->d + bool on_same_side(const vertex &a, const vertex &b, const vertex &c, const vertex &d) + { + float px = d.x - c.x, py = d.y - c.y; + // return det(p, a-c) * det(p, b-c) >= 0 + float l = px * (a.y - c.y) - py * (a.x - c.x); + float m = px * (b.y - c.y) - py * (b.x - c.x); + return l * m >= 0; + } + + // checks is p is contained in the triangle abc + inline bool point_in_triangle(const vertex &p, const vertex &a, const vertex &b, const vertex &c) + { + return on_same_side(p,a, b,c) && on_same_side(p,b, a,c) && on_same_side(p,c, a,b); + } + + // checks if any vertex in `vertices' is in the triangle abc. + bool any_point_in_triangle(const list &vertices, const vertex &a, const vertex &b, const vertex &c) + { + list::const_iterator it, end = vertices.end(); + for (it = vertices.begin(); it != end; ++it) + { + const vertex *p = *it; + if ((p != &a) && (p != &b) && (p != &c) && point_in_triangle(*p, a,b,c)) // oh god... + return true; + } + + return false; + } + + inline bool is_ear(const vertex &a, const vertex &b, const vertex &c, const list &vertices) + { + return is_oriented_ccw(a,b,c) && !any_point_in_triangle(vertices, a,b,c); + } +} + +namespace love +{ +namespace math +{ + +Math Math::instance; + +// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in +// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003 +Math::Math() + : last_randnormal(numeric_limits::infinity()) +{ + // because it is too big for some compilers to handle ... if you know what + // i mean + union + { + uint64_t b64; + struct + { + uint32_t a; + uint32_t b; + } b32; + } converter; + +#ifdef LOVE_BIG_ENDIAN + converter.b32.a = 0x0139408D; + converter.b32.b = 0xCBBF7A44; +#else + converter.b32.b = 0x0139408D; + converter.b32.a = 0xCBBF7A44; +#endif + rng_state = converter.b64; + + // prevent the runtime from free()-ing this + retain(); +} + +uint32_t Math::rand() +{ + rng_state ^= (rng_state << 13); + rng_state ^= (rng_state >> 7); + rng_state ^= (rng_state << 17); + return rng_state; +} + +// Box–Muller transform +double Math::randnormal(double stddev) +{ + // use cached number if possible + if (last_randnormal != numeric_limits::infinity()) + { + double r = last_randnormal; + last_randnormal = numeric_limits::infinity(); + return r * stddev; + } + + double r = sqrt(-2.0 * log(1. - random())); + double phi = 2.0 * LOVE_M_PI * (1. - random()); + + last_randnormal = r * cos(phi); + return r * sin(phi) * stddev; +} + +vector Math::triangulate(const vector &polygon) +{ + if (polygon.size() < 3) + throw love::Exception("Not a ploygon"); + else if (polygon.size() == 3) + return vector(1, Triangle(polygon[0], polygon[1], polygon[2])); + + vector next_vertex(polygon.size()), prev_vertex(polygon.size()); + + // collect list of connections + for (size_t i = 0; i < polygon.size(); ++i) + { + next_vertex[i] = i+1; + prev_vertex[i] = i-1; + } + next_vertex[next_vertex.size()-1] = 0; + prev_vertex[0] = prev_vertex.size()-1; + + // collect list of concave polygons + list concave_vertices; + for (size_t i = 0; i < polygon.size(); ++i) + { + if (!is_oriented_ccw(polygon[prev_vertex[i]], polygon[i], polygon[next_vertex[i]])) + concave_vertices.push_back(&polygon[i]); + } + + // triangulation according to kong + vector triangles; + size_t n_vertices = polygon.size(); + size_t current = 1, skipped = 0, next, prev; + while (n_vertices > 3) + { + next = next_vertex[current]; + prev = prev_vertex[current]; + const vertex &a = polygon[prev], &b = polygon[current], &c = polygon[next]; + if (is_ear(a,b,c, concave_vertices)) + { + triangles.push_back(Triangle(a,b,c)); + next_vertex[prev] = next; + prev_vertex[next] = prev; + concave_vertices.remove(&b); + --n_vertices; + skipped = 0; + } + else if (++skipped > n_vertices) + { + throw love::Exception("Cannot triangulate polygon."); + } + current = next; + } + next = next_vertex[current]; + prev = prev_vertex[current]; + triangles.push_back(Triangle(polygon[prev], polygon[current], polygon[next])); + + return triangles; +} + +} // math +} // love diff --git a/src/modules/math/ModMath.h b/src/modules/math/Math.h similarity index 70% rename from src/modules/math/ModMath.h rename to src/modules/math/Math.h index f5486ccf0..fcecf21c8 100644 --- a/src/modules/math/ModMath.h +++ b/src/modules/math/Math.h @@ -23,21 +23,23 @@ // LOVE #include "common/Module.h" +#include "common/math.h" // STL #include #include +#include namespace love { namespace math { -class ModMath : public Module +class Math : public Module { public: - ModMath(); - virtual ~ModMath() {} + virtual ~Math() + {} /** Set pseudo random seed. * @@ -47,7 +49,7 @@ public: */ inline void randomseed(uint64_t seed) { - RNGState.seed = seed; + rng_state = seed; } /** Return uniformly distributed pseudo random integer. @@ -65,6 +67,24 @@ public: return double(rand()) / (double(std::numeric_limits::max()) + 1.0); } + /** Get uniformly distributed pseudo random number in [0,max). + * + * @returns Pseudo random number in [0,max). + */ + inline double random(double max) + { + return random() * max; + } + + /** Get uniformly distributed pseudo random number in [min, max). + * + * @returns Pseudo random number in [min, max). + */ + inline double random(double min, double max) + { + return random() * (max - min) + min; + } + /** Get normally distributed pseudo random number. * * @param stddev Standard deviation of the distribution. @@ -77,12 +97,19 @@ public: return "love.math"; } + /** Triangulate a simple polygon. + * @param polygon Polygon to triangulate. Must not intersect itself. + * @returns List of triangles the polygon is composed of. + */ + std::vector triangulate(const std::vector &polygon); + + static Math instance; + private: - struct - { - uint64_t seed; - double last_randnormal; - } RNGState; + Math(); + + uint64_t rng_state; + double last_randnormal; }; } // math diff --git a/src/modules/math/ModMath.cpp b/src/modules/math/ModMath.cpp deleted file mode 100644 index 6e873d7d6..000000000 --- a/src/modules/math/ModMath.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright (c) 2006-2013 LOVE Development Team - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - **/ - -#include "ModMath.h" -#include "common/math.h" - -#include - -namespace love -{ -namespace math -{ - -// 64 bit Xorshift implementation taken from the end of Sec. 3 (page 4) in -// George Marsaglia, "Xorshift RNGs", Journal of Statistical Software, Vol.8 (Issue 14), 2003 -ModMath::ModMath() -{ - RNGState.seed = 0x0139408DCBBF7A44; - RNGState.last_randnormal = std::numeric_limits::infinity(); -} - -uint32_t ModMath::rand() -{ - uint64_t &x = RNGState.seed; - x ^= (x << 13); - x ^= (x >> 7); - x ^= (x << 17); - return x; -} - -// Box–Muller transform -double ModMath::randnormal(double stddev) -{ - if (RNGState.last_randnormal != std::numeric_limits::infinity()) - { - double r = RNGState.last_randnormal; - RNGState.last_randnormal = std::numeric_limits::infinity(); - return r * stddev; - } - - double r = sqrt(-2.0 * log(1. - random())); - double phi = 2.0 * LOVE_M_PI * (1. - random()); - - RNGState.last_randnormal = r * cos(phi); - return r * sin(phi) * stddev; -} - -} // math -} // love diff --git a/src/modules/math/wrap_Math.cpp b/src/modules/math/wrap_Math.cpp index 0959f2910..1ba2cc5e5 100644 --- a/src/modules/math/wrap_Math.cpp +++ b/src/modules/math/wrap_Math.cpp @@ -19,40 +19,32 @@ **/ #include "wrap_Math.h" -#include "ModMath.h" +#include "modules/math/Math.h" #include #include -namespace -{ - -union SeedConverter -{ - double seed_double; - uint64_t seed_uint; -}; - -} // anonymous namespace - namespace love { namespace math { -static ModMath *instance = 0; - int w_randomseed(lua_State *L) { - SeedConverter s; + union + { + double seed_double; + uint64_t seed_uint; + } s; + s.seed_double = luaL_checknumber(L, 1); - instance->randomseed(s.seed_uint); + Math::instance.randomseed(s.seed_uint); return 0; } int w_random(lua_State *L) { - double r = instance->random(); + double r = Math::instance.random(); int l, u; // verbatim from lua 5.1.4 source code: lmathlib.c:185 ff. switch (lua_gettop(L)) @@ -90,17 +82,92 @@ int w_randnormal(lua_State *L) stddev = luaL_optnumber(L, 1, 1.); } - double r = instance->randnormal(stddev); + double r = Math::instance.randnormal(stddev); lua_pushnumber(L, r + mean); return 1; } +int w_triangulate(lua_State *L) +{ + std::vector vertices; + if (lua_istable(L, 1)) + { + size_t top = lua_objlen(L, 1); + vertices.reserve(top / 2); + for (size_t i = 1; i <= top; i += 2) + { + lua_rawgeti(L, 1, i); + lua_rawgeti(L, 1, i+1); + + vertex v; + v.x = luaL_checknumber(L, -2); + v.y = luaL_checknumber(L, -1); + vertices.push_back(v); + + lua_pop(L, 2); + } + } + else + { + size_t top = lua_gettop(L); + vertices.reserve(top / 2); + for (size_t i = 1; i <= top; i += 2) + { + vertex v; + v.x = luaL_checknumber(L, i); + v.y = luaL_checknumber(L, i+1); + vertices.push_back(v); + } + } + + if (vertices.size() < 3) + return luaL_error(L, "Need at least 3 vertices to triangulate"); + + std::vector triangles; + try + { + if (vertices.size() == 3) + triangles.push_back(Triangle(vertices[0], vertices[1], vertices[2])); + else + triangles = Math::instance.triangulate(vertices); + } + catch (love::Exception &e) + { + return luaL_error(L, e.what()); + } + + lua_createtable(L, triangles.size(), 0); + for (size_t i = 0; i < triangles.size(); ++i) + { + Triangle &tri = triangles[i]; + + lua_createtable(L, 6, 0); + lua_pushnumber(L, tri.a.x); + lua_rawseti(L, -2, 1); + lua_pushnumber(L, tri.a.y); + lua_rawseti(L, -2, 2); + lua_pushnumber(L, tri.b.x); + lua_rawseti(L, -2, 3); + lua_pushnumber(L, tri.b.y); + lua_rawseti(L, -2, 4); + lua_pushnumber(L, tri.c.x); + lua_rawseti(L, -2, 5); + lua_pushnumber(L, tri.c.y); + lua_rawseti(L, -2, 6); + + lua_rawseti(L, -2, i+1); + } + + return 1; +} + // List of functions to wrap. static const luaL_Reg functions[] = { { "randomseed", w_randomseed }, { "random", w_random }, { "randnormal", w_randnormal }, + { "triangulate", w_triangulate }, { 0, 0 } }; @@ -111,16 +178,9 @@ static const lua_CFunction types[] = extern "C" int luaopen_love_math(lua_State *L) { - if (instance == 0) - instance = new love::math::ModMath(); - else - instance->retain(); - - if (instance == 0) - return luaL_error(L, "Could not open module math."); - + Math::instance.retain(); WrappedModule w; - w.module = instance; + w.module = &Math::instance; w.name = "math"; w.flags = MODULE_T; w.functions = functions; diff --git a/src/modules/math/wrap_Math.h b/src/modules/math/wrap_Math.h index b907013ee..f9cecb93f 100644 --- a/src/modules/math/wrap_Math.h +++ b/src/modules/math/wrap_Math.h @@ -33,6 +33,7 @@ namespace math int w_randomseed(lua_State *L); int w_random(lua_State *L); int w_randnormal(lua_State *L); +int w_triangulate(lua_State *L); extern "C" LOVE_EXPORT int luaopen_love_math(lua_State *L); } // random