Browse Source

Make love.math a singleton. Remove code duplication. Add love.math.triangulate.

love::math::Math is now a singleton, so other modules can use it via
love::math::Math::instance.


Removed duplicate implementations of various rng helper functions.
ParticleSystem now uses love::math::Math's RNG.


New function: love.math.triangulate(vertices)

Accepts a table or list of x/y coordinate pairs and returns a table of tables.
The inner tables are the triangles the polygon is composed of.

Works on all *simple* polygons, i.e. a closed chain of vertices that does not
intersect itself. Attempting to triangulate non-simple polygons is undefined
behavior - in the best case it throws an error, in the worst case it returns
an invalid triangulation.

Polygons must be ordered in *clockwise order* with respect to the love
coordinate system. Attempting to triangulate a ccw polygon will throw an
error.

Examples:

  triangles = love.math.triangulate(0,0, 1,1, 2,0, 2,2, 0,2)
  triangles == {
      {1,1, 2,0, 2,2},
      {1,1, 2,2, 0,2},
      {1,1, 0,2, 0,0},
  }

  triangles = love.math.triangulate({0,0, 1,1, 2,0, 2,2, 0,2})
  -- same as above

  triangles = love.math.triangulate(0,2, 2,2, 2,0, 1,1, 0,0)
  -- error - polygons is in counterclockwise order

  triangles = love.math.triangulate(0,0, 1,3, 2,0, 2,2, 0,2)
  -- undefined behavior - polygon intersects itself (because of edge 1,3)
vrld 12 years ago
parent
commit
ece4ff6472

+ 0 - 33
src/common/math.cpp

@@ -1,33 +0,0 @@
-#include "math.h"
-#include <limits>
-#include <cmath>
-
-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<float>::infinity();
-}
-
-namespace love
-{
-
-float random_normal(float o)
-{
-	// number in cache?
-	if (last_randnormal != std::numeric_limits<float>::infinity())
-	{
-		float r = last_randnormal;
-		last_randnormal = std::numeric_limits<float>::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

+ 8 - 36
src/common/math.h

@@ -68,6 +68,14 @@ struct vertex
 	float s, t;
 	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)
 inline int next_p2(int x)
 {
 {
 	x += (x == 0);
 	x += (x == 0);
@@ -81,42 +89,6 @@ inline float next_p2(float x)
 	return static_cast<float>(next_p2(static_cast<int>(x)));
 	return static_cast<float>(next_p2(static_cast<int>(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
 } // love
 
 
 #endif // LOVE_MATH_H
 #endif // LOVE_MATH_H

+ 17 - 14
src/modules/graphics/opengl/ParticleSystem.cpp

@@ -21,11 +21,14 @@
 #include "ParticleSystem.h"
 #include "ParticleSystem.h"
 
 
 #include "common/math.h"
 #include "common/math.h"
+#include "modules/math/Math.h"
 
 
 #include "OpenGL.h"
 #include "OpenGL.h"
 #include <cmath>
 #include <cmath>
 #include <cstdlib>
 #include <cstdlib>
 
 
+using love::math::Math;
+
 namespace love
 namespace love
 {
 {
 namespace graphics
 namespace graphics
@@ -45,7 +48,7 @@ float calculate_variation(float inner, float outer, float var)
 {
 {
 	float low = inner - (outer/2.0f)*var;
 	float low = inner - (outer/2.0f)*var;
 	float high = 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;
 	return low*(1-r)+high*r;
 }
 }
 
 
@@ -124,7 +127,7 @@ void ParticleSystem::add()
 	if (min == max)
 	if (min == max)
 		pLast->life = min;
 		pLast->life = min;
 	else
 	else
-		pLast->life = random(min, max);
+		pLast->life = Math::instance.random(min, max);
 	pLast->lifetime = pLast->life;
 	pLast->lifetime = pLast->life;
 
 
 	pLast->position[0] = position.getX();
 	pLast->position[0] = position.getX();
@@ -133,12 +136,12 @@ void ParticleSystem::add()
 	switch (areaSpreadDistribution)
 	switch (areaSpreadDistribution)
 	{
 	{
 		case DISTRIBUTION_UNIFORM:
 		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;
 			break;
 		case DISTRIBUTION_NORMAL:
 		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;
 			break;
 		case DISTRIBUTION_NONE:
 		case DISTRIBUTION_NONE:
 		default:
 		default:
@@ -147,37 +150,37 @@ void ParticleSystem::add()
 
 
 	min = direction - spread/2.0f;
 	min = direction - spread/2.0f;
 	max = direction + spread/2.0f;
 	max = direction + spread/2.0f;
-	pLast->direction = random(min, max);
+	pLast->direction = Math::instance.random(min, max);
 
 
 	pLast->origin = position;
 	pLast->origin = position;
 
 
 	min = speedMin;
 	min = speedMin;
 	max = speedMax;
 	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 = love::Vector(cos(pLast->direction), sin(pLast->direction));
 	pLast->speed *= speed;
 	pLast->speed *= speed;
 
 
 	min = gravityMin;
 	min = gravityMin;
 	max = gravityMax;
 	max = gravityMax;
-	pLast->gravity = random(min, max);
+	pLast->gravity = Math::instance.random(min, max);
 
 
 	min = radialAccelerationMin;
 	min = radialAccelerationMin;
 	max = radialAccelerationMax;
 	max = radialAccelerationMax;
-	pLast->radialAcceleration = random(min, max);
+	pLast->radialAcceleration = Math::instance.random(min, max);
 
 
 	min = tangentialAccelerationMin;
 	min = tangentialAccelerationMin;
 	max = tangentialAccelerationMax;
 	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)];
 	pLast->size = sizes[(size_t)(pLast->sizeOffset - .5f) * (sizes.size() - 1)];
 
 
 	min = rotationMin;
 	min = rotationMin;
 	max = rotationMax;
 	max = rotationMax;
 	pLast->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
 	pLast->spinStart = calculate_variation(spinStart, spinEnd, spinVariation);
 	pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
 	pLast->spinEnd = calculate_variation(spinEnd, spinStart, spinVariation);
-	pLast->rotation = random(min, max);
+	pLast->rotation = Math::instance.random(min, max);
 
 
 	pLast->color = colors[0];
 	pLast->color = colors[0];
 
 

+ 198 - 0
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 <cmath>
+#include <list>
+#include <iostream>
+
+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<const vertex *> &vertices, const vertex &a, const vertex &b, const vertex &c)
+	{
+		list<const vertex *>::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<const vertex *> &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<double>::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<double>::infinity())
+	{
+		double r = last_randnormal;
+		last_randnormal = numeric_limits<double>::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<Triangle> Math::triangulate(const vector<vertex> &polygon)
+{
+	if (polygon.size() < 3)
+		throw love::Exception("Not a ploygon");
+	else if (polygon.size() == 3)
+		return vector<Triangle>(1, Triangle(polygon[0], polygon[1], polygon[2]));
+
+	vector<size_t> 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<const vertex *> 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<Triangle> 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

+ 36 - 9
src/modules/math/ModMath.h → src/modules/math/Math.h

@@ -23,21 +23,23 @@
 
 
 // LOVE
 // LOVE
 #include "common/Module.h"
 #include "common/Module.h"
+#include "common/math.h"
 
 
 // STL
 // STL
 #include <limits>
 #include <limits>
 #include <stdint.h>
 #include <stdint.h>
+#include <vector>
 
 
 namespace love
 namespace love
 {
 {
 namespace math
 namespace math
 {
 {
 
 
-class ModMath : public Module
+class Math : public Module
 {
 {
 public:
 public:
-	ModMath();
-	virtual ~ModMath() {}
+	virtual ~Math()
+	{}
 
 
 	/** Set pseudo random seed.
 	/** Set pseudo random seed.
 	 *
 	 *
@@ -47,7 +49,7 @@ public:
 	 */
 	 */
 	inline void randomseed(uint64_t seed)
 	inline void randomseed(uint64_t seed)
 	{
 	{
-		RNGState.seed = seed;
+		rng_state = seed;
 	}
 	}
 
 
 	/** Return uniformly distributed pseudo random integer.
 	/** Return uniformly distributed pseudo random integer.
@@ -65,6 +67,24 @@ public:
 		return double(rand()) / (double(std::numeric_limits<uint32_t>::max()) + 1.0);
 		return double(rand()) / (double(std::numeric_limits<uint32_t>::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.
 	/** Get normally distributed pseudo random number.
 	 *
 	 *
 	 * @param stddev Standard deviation of the distribution.
 	 * @param stddev Standard deviation of the distribution.
@@ -77,12 +97,19 @@ public:
 		return "love.math";
 		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<Triangle> triangulate(const std::vector<vertex> &polygon);
+
+	static Math instance;
+
 private:
 private:
-	struct
-	{
-		uint64_t seed;
-		double last_randnormal;
-	} RNGState;
+	Math();
+
+	uint64_t rng_state;
+	double last_randnormal;
 };
 };
 
 
 } // math
 } // math

+ 0 - 66
src/modules/math/ModMath.cpp

@@ -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 <cmath>
-
-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<double>::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<double>::infinity())
-	{
-		double r = RNGState.last_randnormal;
-		RNGState.last_randnormal = std::numeric_limits<double>::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

+ 87 - 27
src/modules/math/wrap_Math.cpp

@@ -19,40 +19,32 @@
  **/
  **/
 
 
 #include "wrap_Math.h"
 #include "wrap_Math.h"
-#include "ModMath.h"
+#include "modules/math/Math.h"
 
 
 #include <cmath>
 #include <cmath>
 #include <iostream>
 #include <iostream>
 
 
-namespace
-{
-
-union SeedConverter
-{
-	double   seed_double;
-	uint64_t seed_uint;
-};
-
-} // anonymous namespace
-
 namespace love
 namespace love
 {
 {
 namespace math
 namespace math
 {
 {
 
 
-static ModMath *instance = 0;
-
 int w_randomseed(lua_State *L)
 int w_randomseed(lua_State *L)
 {
 {
-	SeedConverter s;
+	union
+	{
+		double   seed_double;
+		uint64_t seed_uint;
+	} s;
+
 	s.seed_double = luaL_checknumber(L, 1);
 	s.seed_double = luaL_checknumber(L, 1);
-	instance->randomseed(s.seed_uint);
+	Math::instance.randomseed(s.seed_uint);
 	return 0;
 	return 0;
 }
 }
 
 
 int w_random(lua_State *L)
 int w_random(lua_State *L)
 {
 {
-	double r = instance->random();
+	double r = Math::instance.random();
 	int l, u;
 	int l, u;
 	// verbatim from lua 5.1.4 source code: lmathlib.c:185 ff.
 	// verbatim from lua 5.1.4 source code: lmathlib.c:185 ff.
 	switch (lua_gettop(L))
 	switch (lua_gettop(L))
@@ -90,17 +82,92 @@ int w_randnormal(lua_State *L)
 		stddev = luaL_optnumber(L, 1, 1.);
 		stddev = luaL_optnumber(L, 1, 1.);
 	}
 	}
 
 
-	double r = instance->randnormal(stddev);
+	double r = Math::instance.randnormal(stddev);
 	lua_pushnumber(L, r + mean);
 	lua_pushnumber(L, r + mean);
 	return 1;
 	return 1;
 }
 }
 
 
+int w_triangulate(lua_State *L)
+{
+	std::vector<vertex> 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<Triangle> 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.
 // List of functions to wrap.
 static const luaL_Reg functions[] =
 static const luaL_Reg functions[] =
 {
 {
 	{ "randomseed", w_randomseed },
 	{ "randomseed", w_randomseed },
 	{ "random", w_random },
 	{ "random", w_random },
 	{ "randnormal", w_randnormal },
 	{ "randnormal", w_randnormal },
+	{ "triangulate", w_triangulate },
 	{ 0, 0 }
 	{ 0, 0 }
 };
 };
 
 
@@ -111,16 +178,9 @@ static const lua_CFunction types[] =
 
 
 extern "C" int luaopen_love_math(lua_State *L)
 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;
 	WrappedModule w;
-	w.module = instance;
+	w.module = &Math::instance;
 	w.name = "math";
 	w.name = "math";
 	w.flags = MODULE_T;
 	w.flags = MODULE_T;
 	w.functions = functions;
 	w.functions = functions;

+ 1 - 0
src/modules/math/wrap_Math.h

@@ -33,6 +33,7 @@ namespace math
 int w_randomseed(lua_State *L);
 int w_randomseed(lua_State *L);
 int w_random(lua_State *L);
 int w_random(lua_State *L);
 int w_randnormal(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);
 extern "C" LOVE_EXPORT int luaopen_love_math(lua_State *L);
 
 
 } // random
 } // random