diff --git a/files/docs/delaunay/LICENSE b/files/docs/delaunay/LICENSE
new file mode 100644
index 0000000..4f69f6b
--- /dev/null
+++ b/files/docs/delaunay/LICENSE
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Roland Y.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/files/docs/delaunay/README.md b/files/docs/delaunay/README.md
new file mode 100644
index 0000000..7af5cdc
--- /dev/null
+++ b/files/docs/delaunay/README.md
@@ -0,0 +1,111 @@
+Delaunay
+=====
+
+[![Build Status](https://travis-ci.org/Yonaba/delaunay.png)](https://travis-ci.org/Yonaba/delaunay)
+[![Coverage Status](https://coveralls.io/repos/Yonaba/delaunay/badge.png?branch=master)](https://coveralls.io/r/Yonaba/delaunay?branch=master)
+[![License](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE)
+
+*delaunay* is a Lua module for [delaunay triangulation](http://en.wikipedia.org/wiki/Delaunay_triangulation) of a convex polygon.
+
+##Download
+
+###Git
+
+````
+git clone http://github.com/Yonaba/delaunay.git
+````
+
+###Archive
+
+* [zip](https://github.com/Yonaba/delaunay/archive/delaunay-0.1-1.zip) | [tar.gz](https://github.com/Yonaba/delaunay/archive/delaunay-0.1-1.tar.gz) | [all](http://github.com/Yonaba/delaunay/tags)
+
+###LuaRocks
+
+````
+luarocks install delaunay
+````
+
+###MoonRocks
+
+````
+luarocks install --server=http://rocks.moonscript.org/manifests/Yonaba delaunay
+````
+
+##Installation
+Copy the file [delaunay.lua](http://raw.github.com/Yonaba/delaunay/master/delaunay.lua) inside your project folder,
+call it with [require](http://pgl.yoyo.org/luai/i/require) function. It will return the `Delaunay` module, keeping safe the global environment.
+
+##Usage
+
+The module provides 3 classes:
+* `Point`
+* `Edge`
+* `Triangle`
+
+It also provides a single function named `triangulate`. This function accepts
+a variable list (*vararg* `...`) of instances of class `Point`. Assuming those
+points are the vertices of a convex polygon, it returns a table of instances of the class `Triangle` forming a *Delaunay triangulation* of the given polygon.
+
+A basic code example:
+```lua
+local Delaunay = require 'Delaunay'
+local Point = Delaunay.Point
+
+-- Creating 10 random points
+local points = {}
+for i = 1, 10 do
+ points[i] = Point(math.random() * 100, math.random() * 100)
+end
+
+-- Triangulating de convex polygon made by those points
+local triangles = Delaunay.triangulate(unpack(points))
+
+-- Printing the results
+for i, triangle in ipairs(triangles) do
+ print(triangle)
+end
+````
+
+See the [documentation](http://yonaba.github.io/delaunay/doc) for more details.
+
+##Testing
+###Specification
+
+This repository include unit tests. You can run them using [Telescope](https://github.com/norman/telescope) with the following command from the root foolder:
+
+```
+lua tsc -f specs/*
+```
+
+###Performance
+
+You can run the random performance tests included with the following command from the root folder:
+
+```lua
+lua performance/bench.lua
+````
+
+##License
+This work is under [MIT-LICENSE](http://www.opensource.org/licenses/mit-license.php).
+Copyright (c) 2013 Roland Yonaba
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/Yonaba/delaunay/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
\ No newline at end of file
diff --git a/files/docs/delaunay/index.html b/files/docs/delaunay/index.html
new file mode 100644
index 0000000..c171009
--- /dev/null
+++ b/files/docs/delaunay/index.html
@@ -0,0 +1,801 @@
+
+
+
+
+ local Delaunay = require'Delaunay'
+ local Edge = Delaunay.Edge
+ local Point = Delaunay.Point
+ local e = Edge:new(Point(1,1), Point(2,5))
+ local e = Edge(Point(1,1), Point(2,5)) -- Alias to Edge.new
+print(e) -- print the edge members p1 and p2
+
+
+
+
+
+
+ Edge:same (otherEdge)
+
+
+ Test if otherEdge is similar to self. It does not take into account the direction.
+
+
+ local Delaunay = require'Delaunay'
+ local Point = Delaunay.Point
+ local p = Point:new(1,1)
+ local p = Point(1,1) -- Alias to Point.new
+print(p) -- print the point members x and y
+
+
+
+
+
+
+ Point:dist2 (p)
+
+
+ Returns the square distance to another Point .
+
+
+ local Delaunay = require'Delaunay'
+ local Triangle = Delaunay.Triangle
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle:new(p1, p2, p3)
+ local t = Triangle(p1, p2, p3) -- Alias to Triangle.new
+print(t) -- print the triangle members p1, p2 and p3
+
+
+
+
+
+
+ Triangle:isCW ()
+
+
+ Checks if the triangle is defined clockwise (sequence p1-p2-p3)
+
+
+
Returns:
+
+
+ true or false
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(1,1), Point(2,0)
+ local t = Triangle(p1, p2, p3)
+ print(t:isCW()) --> true
+
+
+
+
+
+
+ Triangle:isCCW ()
+
+
+ Checks if the triangle is defined counter-clockwise (sequence p1-p2-p3)
+
+
+
Returns:
+
+
+ true or false
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:isCCW()) --> true
+
+
+
+
+
+
+ Triangle:getSidesLength ()
+
+
+ Returns the length of the edges
+
+
+
Returns:
+
+
+ the length of the edge p1-p2
+
+ the length of the edge p2-p3
+
+ the length of the edge p3-p1
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getSidesLength()) --> 2 1.4142135623731 1.4142135623731
+
+
+
+
+
+
+ Triangle:getCenter ()
+
+
+ Returns the coordinates of the center
+
+
+
Returns:
+
+
+ the x-coordinate of the center
+
+ the y-coordinate of the center
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCenter()) --> 1 0.33333333333333
+
+
+
+
+
+
+ Triangle:getCircumCircle ()
+
+
+ Returns the coordinates of the circumcircle center and its radius
+
+
+
Returns:
+
+
+ the x-coordinate of the circumcircle center
+
+ the y-coordinate of the circumcircle center
+
+ the radius of the circumcircle
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumCircle()) --> 1 0 1
+
+
+
+
+
+
+ Triangle:getCircumCenter ()
+
+
+ Returns the coordinates of the circumcircle center
+
+
+
Returns:
+
+
+ the x-coordinate of the circumcircle center
+
+ the y-coordinate of the circumcircle center
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumCenter()) --> 1 0
+
+
+
+
+
+
+ Triangle:getCircumRadius ()
+
+
+ Returns the radius of the circumcircle
+
+
+
Returns:
+
+
+ the radius of the circumcircle
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumRadius()) --> 1
+
+
+
+
+
+
+ Triangle:getArea ()
+
+
+ Returns the area
+
+
+
Returns:
+
+
+ the area
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getArea()) --> 1
+
+
+
+
+
+
+ Triangle:inCircumCircle (p)
+
+
+ Checks if a given point lies into the triangle circumcircle
+
+
+ local Delaunay = require'Delaunay'
+ local Point = Delaunay.Point
+ local p1, p2, p3, p4 = Point(), Point(2,0), Point(1,1), Point(1,-1)
+ local triangles = Delaunay.triangulate(p1, p2, p3, p4)
+ for i = 1, #triangles do
+ print(triangles[i])
+ end
+ local Delaunay = require'Delaunay'
+ local Edge = Delaunay.Edge
+ local Point = Delaunay.Point
+ local e = Edge:new(Point(1,1), Point(2,5))
+ local e = Edge(Point(1,1), Point(2,5)) -- Alias to Edge.new
+print(e) -- print the edge members p1 and p2
+
+
+
+
+
+
+ Edge:same (otherEdge)
+
+
+ Test if otherEdge is similar to self. It does not take into account the direction.
+
+
+ local Delaunay = require'Delaunay'
+ local Point = Delaunay.Point
+ local p = Point:new(1,1)
+ local p = Point(1,1) -- Alias to Point.new
+print(p) -- print the point members x and y
+
+
+
+
+
+
+ Point:dist2 (p)
+
+
+ Returns the square distance to another Point .
+
+
+ local Delaunay = require'Delaunay'
+ local Triangle = Delaunay.Triangle
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle:new(p1, p2, p3)
+ local t = Triangle(p1, p2, p3) -- Alias to Triangle.new
+print(t) -- print the triangle members p1, p2 and p3
+
+
+
+
+
+
+ Triangle:isCW ()
+
+
+ Checks if the triangle is defined clockwise (sequence p1-p2-p3)
+
+
+
Returns:
+
+
+ true or false
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(1,1), Point(2,0)
+ local t = Triangle(p1, p2, p3)
+ print(t:isCW()) --> true
+
+
+
+
+
+
+ Triangle:isCCW ()
+
+
+ Checks if the triangle is defined counter-clockwise (sequence p1-p2-p3)
+
+
+
Returns:
+
+
+ true or false
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:isCCW()) --> true
+
+
+
+
+
+
+ Triangle:getSidesLength ()
+
+
+ Returns the length of the edges
+
+
+
Returns:
+
+
+ the length of the edge p1-p2
+
+ the length of the edge p2-p3
+
+ the length of the edge p3-p1
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getSidesLength()) --> 2 1.4142135623731 1.4142135623731
+
+
+
+
+
+
+ Triangle:getCenter ()
+
+
+ Returns the coordinates of the center
+
+
+
Returns:
+
+
+ the x-coordinate of the center
+
+ the y-coordinate of the center
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCenter()) --> 1 0.33333333333333
+
+
+
+
+
+
+ Triangle:getCircumCircle ()
+
+
+ Returns the coordinates of the circumcircle center and its radius
+
+
+
Returns:
+
+
+ the x-coordinate of the circumcircle center
+
+ the y-coordinate of the circumcircle center
+
+ the radius of the circumcircle
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumCircle()) --> 1 0 1
+
+
+
+
+
+
+ Triangle:getCircumCenter ()
+
+
+ Returns the coordinates of the circumcircle center
+
+
+
Returns:
+
+
+ the x-coordinate of the circumcircle center
+
+ the y-coordinate of the circumcircle center
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumCenter()) --> 1 0
+
+
+
+
+
+
+ Triangle:getCircumRadius ()
+
+
+ Returns the radius of the circumcircle
+
+
+
Returns:
+
+
+ the radius of the circumcircle
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getCircumRadius()) --> 1
+
+
+
+
+
+
+ Triangle:getArea ()
+
+
+ Returns the area
+
+
+
Returns:
+
+
+ the area
+
+
+
+
+
Usage:
+
+
+ local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+ local t = Triangle(p1, p2, p3)
+ print(t:getArea()) --> 1
+
+
+
+
+
+
+ Triangle:inCircumCircle (p)
+
+
+ Checks if a given point lies into the triangle circumcircle
+
+
+ local Delaunay = require'Delaunay'
+ local Point = Delaunay.Point
+ local p1, p2, p3, p4 = Point(), Point(2,0), Point(1,1), Point(1,-1)
+ local triangles = Delaunay.triangulate(p1, p2, p3, p4)
+ for i = 1, #triangles do
+ print(triangles[i])
+ end
+
+
diff --git a/files/docs/delaunay/version_history.md b/files/docs/delaunay/version_history.md
new file mode 100644
index 0000000..26830ae
--- /dev/null
+++ b/files/docs/delaunay/version_history.md
@@ -0,0 +1,4 @@
+#Version history#
+
+###0.1 (12/26/2013)
+* Initial release
\ No newline at end of file
diff --git a/files/lua/delaunay.lua b/files/lua/delaunay.lua
new file mode 100644
index 0000000..b5eae5b
--- /dev/null
+++ b/files/lua/delaunay.lua
@@ -0,0 +1,448 @@
+#!/usr/bin/env lua
+---------------
+-- ## Delaunay, Lua module for convex polygon triangulation
+-- @author Roland Yonaba
+-- @copyright 2013
+-- @license MIT
+-- @script delaunay
+
+-- ================
+-- Private helpers
+-- ================
+
+local setmetatable = setmetatable
+local tostring = tostring
+local assert = assert
+local unpack = unpack
+local remove = table.remove
+local sqrt = math.sqrt
+local max = math.max
+
+-- Internal class constructor
+local class = function(...)
+ local klass = {}
+ klass.__index = klass
+ klass.__call = function(_,...) return klass:new(...) end
+ function klass:new(...)
+ local instance = setmetatable({}, klass)
+ klass.__init(instance, ...)
+ return instance
+ end
+ return setmetatable(klass,{__call = klass.__call})
+end
+
+-- Triangle semi-perimeter by Heron's formula
+local function quatCross(a, b, c)
+ local p = (a + b + c) * (a + b - c) * (a - b + c) * (-a + b + c)
+ return sqrt(p)
+end
+
+
+-- Cross product (p1-p2, p2-p3)
+local function crossProduct(p1, p2, p3)
+ local x1, x2 = p2.x - p1.x, p3.x - p2.x
+ local y1, y2 = p2.y - p1.y, p3.y - p2.y
+ return x1 * y2 - y1 * x2
+end
+
+-- Checks if angle (p1-p2-p3) is flat
+local function isFlatAngle(p1, p2, p3)
+ return (crossProduct(p1, p2, p3) == 0)
+end
+
+-- ================
+-- Module classes
+-- ================
+
+--- `Edge` class
+-- @type Edge
+local Edge = class()
+Edge.__eq = function(a, b) return (a.p1 == b.p1 and a.p2 == b.p2) end
+Edge.__tostring = function(e)
+ return (('Edge :\n %s\n %s'):format(tostring(e.p1), tostring(e.p2)))
+end
+
+--- Creates a new `Edge`
+-- @name Edge:new
+-- @param p1 a `Point`
+-- @param p2 a `Point`
+-- @return a new `Edge`
+-- @usage
+-- local Delaunay = require 'Delaunay'
+-- local Edge = Delaunay.Edge
+-- local Point = Delaunay.Point
+-- local e = Edge:new(Point(1,1), Point(2,5))
+-- local e = Edge(Point(1,1), Point(2,5)) -- Alias to Edge.new
+-- print(e) -- print the edge members p1 and p2
+--
+function Edge:__init(p1, p2)
+ self.p1, self.p2 = p1, p2
+end
+
+--- Test if `otherEdge` is similar to self. It does not take into account the direction.
+-- @param otherEdge an `Edge`
+-- @return `true` or `false`
+-- @usage
+-- local e1 = Edge(Point(1,1), Point(2,5))
+-- local e2 = Edge(Point(2,5), Point(1,1))
+-- print(e1:same(e2)) --> true
+-- print(e1 == e2)) --> false, == operator considers the direction
+--
+function Edge:same(otherEdge)
+ return ((self.p1 == otherEdge.p1) and (self.p2 == otherEdge.p2))
+ or ((self.p1 == otherEdge.p2) and (self.p2 == otherEdge.p1))
+end
+
+--- Returns the length.
+-- @return the length of self
+-- @usage
+-- local e = Edge(Point(), Point(10,0))
+-- print(e:length()) --> 10
+--
+function Edge:length()
+ return self.p1:dist(self.p2)
+end
+
+--- Returns the midpoint coordinates.
+-- @return the x-coordinate of self midpoint
+-- @return the y-coordinate of self midpoint
+-- @usage
+-- local e = Edge(Point(), Point(10,0))
+-- print(e:getMidPoint()) --> 5, 0
+--
+function Edge:getMidPoint()
+ local x = self.p1.x + (self.p2.x - self.p1.x) / 2
+ local y = self.p1.x + (self.p2.y - self.p1.y) / 2
+ return x, y
+end
+
+--- Point class
+-- @type Point
+local Point = class()
+Point.__eq = function(a,b) return (a.x == b.x and a.y == b.y) end
+Point.__tostring = function(p)
+ return ('Point (%s) x: %.2f y: %.2f'):format(p.id, p.x, p.y)
+end
+
+--- Creates a new `Point`
+-- @name Point:new
+-- @param x the x-coordinate
+-- @param y the y-coordinate
+-- @return a new `Point`
+-- @usage
+-- local Delaunay = require 'Delaunay'
+-- local Point = Delaunay.Point
+-- local p = Point:new(1,1)
+-- local p = Point(1,1) -- Alias to Point.new
+-- print(p) -- print the point members x and y
+--
+function Point:__init(x, y)
+ self.x, self.y, self.id = x or 0, y or 0, '?'
+end
+
+--- Returns the square distance to another `Point`.
+-- @param p a `Point`
+-- @return the square distance from self to `p`.
+-- @usage
+-- local p1, p2 = Point(), Point(1,1)
+-- print(p1:dist2(p2)) --> 2
+--
+function Point:dist2(p)
+ local dx, dy = (self.x - p.x), (self.y - p.y)
+ return dx * dx + dy * dy
+end
+
+--- Returns the distance to another `Point`.
+-- @param p a `Point`
+-- @return the distance from self to `p`.
+-- @usage
+-- local p1, p2 = Point(), Point(1,1)
+-- print(p1:dist2(p2)) --> 1.4142135623731
+--
+function Point:dist(p)
+ return sqrt(self:dist2(p))
+end
+
+--- Checks if self lies into the bounds of a circle
+-- @param cx the x-coordinate of the circle center
+-- @param cy the y-coordinate of the circle center
+-- @param r the radius of the circle
+-- @return `true` or `false`
+-- @usage
+-- local p = Point()
+-- print(p:isInCircle(0,0,1)) --> true
+--
+function Point:isInCircle(cx, cy, r)
+ local dx = (cx - self.x)
+ local dy = (cy - self.y)
+ return ((dx * dx + dy * dy) <= (r * r))
+end
+
+--- `Triangle` class
+-- @type Triangle
+
+local Triangle = class()
+Triangle.__tostring = function(t)
+ return (('Triangle: \n %s\n %s\n %s')
+ :format(tostring(t.p1), tostring(t.p2), tostring(t.p3)))
+end
+
+--- Creates a new `Triangle`
+-- @name Triangle:new
+-- @param p1 a `Point`
+-- @param p2 a `Point`
+-- @param p3 a `Point`
+-- @return a new `Triangle`
+-- @usage
+-- local Delaunay = require 'Delaunay'
+-- local Triangle = Delaunay.Triangle
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle:new(p1, p2, p3)
+-- local t = Triangle(p1, p2, p3) -- Alias to Triangle.new
+-- print(t) -- print the triangle members p1, p2 and p3
+--
+function Triangle:__init(p1, p2, p3)
+ assert(not isFlatAngle(p1, p2, p3), ("angle (p1, p2, p3) is flat:\n %s\n %s\n %s")
+ :format(tostring(p1), tostring(p2), tostring(p3)))
+ self.p1, self.p2, self.p3 = p1, p2, p3
+ self.e1, self.e2, self.e3 = Edge(p1, p2), Edge(p2, p3), Edge(p3, p1)
+end
+
+--- Checks if the triangle is defined clockwise (sequence p1-p2-p3)
+-- @return `true` or `false`
+-- @usage
+-- local p1, p2, p3 = Point(), Point(1,1), Point(2,0)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:isCW()) --> true
+--
+function Triangle:isCW()
+ return (crossProduct(self.p1, self.p2, self.p3) < 0)
+end
+
+--- Checks if the triangle is defined counter-clockwise (sequence p1-p2-p3)
+-- @return `true` or `false`
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:isCCW()) --> true
+--
+function Triangle:isCCW()
+ return (crossProduct(self.p1, self.p2, self.p3) > 0)
+end
+
+--- Returns the length of the edges
+-- @return the length of the edge p1-p2
+-- @return the length of the edge p2-p3
+-- @return the length of the edge p3-p1
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getSidesLength()) --> 2 1.4142135623731 1.4142135623731
+--
+function Triangle:getSidesLength()
+ return self.e1:length(), self.e2:length(), self.e3:length()
+end
+
+--- Returns the coordinates of the center
+-- @return the x-coordinate of the center
+-- @return the y-coordinate of the center
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getCenter()) --> 1 0.33333333333333
+--
+function Triangle:getCenter()
+ local x = (self.p1.x + self.p2.x + self.p3.x) / 3
+ local y = (self.p1.y + self.p2.y + self.p3.y) / 3
+ return x, y
+end
+
+--- Returns the coordinates of the circumcircle center and its radius
+-- @return the x-coordinate of the circumcircle center
+-- @return the y-coordinate of the circumcircle center
+-- @return the radius of the circumcircle
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getCircumCircle()) --> 1 0 1
+--
+function Triangle:getCircumCircle()
+ local x, y = self:getCircumCenter()
+ local r = self:getCircumRadius()
+ return x, y, r
+end
+
+--- Returns the coordinates of the circumcircle center
+-- @return the x-coordinate of the circumcircle center
+-- @return the y-coordinate of the circumcircle center
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getCircumCenter()) --> 1 0
+--
+function Triangle:getCircumCenter()
+ local p1, p2, p3 = self.p1, self.p2, self.p3
+ local D = ( p1.x * (p2.y - p3.y) +
+ p2.x * (p3.y - p1.y) +
+ p3.x * (p1.y - p2.y)) * 2
+ local x = (( p1.x * p1.x + p1.y * p1.y) * (p2.y - p3.y) +
+ ( p2.x * p2.x + p2.y * p2.y) * (p3.y - p1.y) +
+ ( p3.x * p3.x + p3.y * p3.y) * (p1.y - p2.y))
+ local y = (( p1.x * p1.x + p1.y * p1.y) * (p3.x - p2.x) +
+ ( p2.x * p2.x + p2.y * p2.y) * (p1.x - p3.x) +
+ ( p3.x * p3.x + p3.y * p3.y) * (p2.x - p1.x))
+ return (x / D), (y / D)
+end
+
+--- Returns the radius of the circumcircle
+-- @return the radius of the circumcircle
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getCircumRadius()) --> 1
+--
+function Triangle:getCircumRadius()
+ local a, b, c = self:getSidesLength()
+ return ((a * b * c) / quatCross(a, b, c))
+end
+
+--- Returns the area
+-- @return the area
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:getArea()) --> 1
+--
+function Triangle:getArea()
+ local a, b, c = self:getSidesLength()
+ return (quatCross(a, b, c) / 4)
+end
+
+--- Checks if a given point lies into the triangle circumcircle
+-- @param p a `Point`
+-- @return `true` or `false`
+-- @usage
+-- local p1, p2, p3 = Point(), Point(2,0), Point(1,1)
+-- local t = Triangle(p1, p2, p3)
+-- print(t:inCircumCircle(Point(1,-1))) --> true
+--
+function Triangle:inCircumCircle(p)
+ return p:isInCircle(self:getCircumCircle())
+end
+
+--- Delaunay module
+-- @section public
+
+--- Delaunay module
+-- @table Delaunay
+-- @field Point reference to the `Point` class
+-- @field Edge reference to the `Edge` class
+-- @field Triangle reference to the `Triangle` class
+-- @field _VERSION the version of the current module
+local Delaunay = {
+ Point = Point,
+ Edge = Edge,
+ Triangle = Triangle,
+ _VERSION = "0.1"
+}
+
+--- Triangulates a set of given vertices
+-- @param ... a `vargarg` list of objects of type `Point`
+-- @return a set of objects of type `Triangle`
+-- @usage
+-- local Delaunay = require 'Delaunay'
+-- local Point = Delaunay.Point
+-- local p1, p2, p3, p4 = Point(), Point(2,0), Point(1,1), Point(1,-1)
+-- local triangles = Delaunay.triangulate(p1, p2, p3, p4)
+-- for i = 1, #triangles do
+-- print(triangles[i])
+-- end
+--
+function Delaunay.triangulate(...)
+ local vertices = {...}
+ local nvertices = #vertices
+ assert(nvertices > 2, "Cannot triangulate, needs more than 3 vertices")
+ if nvertices == 3 then
+ return {Triangle(unpack(vertices))}
+ end
+
+ local trmax = nvertices * 4
+
+ local minX, minY = vertices[1].x, vertices[1].y
+ local maxX, maxY = minX, minY
+
+ for i = 1, #vertices do
+ local vertex = vertices[i]
+ vertex.id = i
+ if vertex.x < minX then minX = vertex.x end
+ if vertex.y < minY then minY = vertex.y end
+ if vertex.x > maxX then maxX = vertex.x end
+ if vertex.y > maxY then maxY = vertex.y end
+ end
+
+ local dx, dy = maxX - minX, maxY - minY
+ local deltaMax = max(dx, dy)
+ local midx, midy = (minX + maxX) * 0.5, (minY + maxY) * 0.5
+
+ local p1 = Point(midx - 2 * deltaMax, midy - deltaMax)
+ local p2 = Point(midx, midy + 2 * deltaMax)
+ local p3 = Point(midx + 2 * deltaMax, midy - deltaMax)
+ p1.id, p2.id, p3.id = nvertices + 1, nvertices + 2, nvertices + 3
+ vertices[p1.id] = p1
+ vertices[p2.id] = p2
+ vertices[p3.id] = p3
+
+ local triangles = {}
+ triangles[#triangles + 1] = Triangle(vertices[nvertices + 1],
+ vertices[nvertices + 2],
+ vertices[nvertices + 3]
+ )
+
+ for i = 1, nvertices do
+
+ local edges = {}
+ local ntriangles = #triangles
+
+ for j = #triangles, 1, -1 do
+ local curTriangle = triangles[j]
+ if curTriangle:inCircumCircle(vertices[i]) then
+ edges[#edges + 1] = curTriangle.e1
+ edges[#edges + 1] = curTriangle.e2
+ edges[#edges + 1] = curTriangle.e3
+ remove(triangles, j)
+ end
+ end
+
+ for j = #edges - 1, 1, -1 do
+ for k = #edges, j + 1, -1 do
+ if edges[j] and edges[k] and edges[j]:same(edges[k]) then
+ remove(edges, j)
+ remove(edges, k-1)
+ end
+ end
+ end
+
+ for j = 1, #edges do
+ local n = #triangles
+ assert(n <= trmax, "Generated more than needed triangles")
+ triangles[n + 1] = Triangle(edges[j].p1, edges[j].p2, vertices[i])
+ end
+
+ end
+
+ for i = #triangles, 1, -1 do
+ local triangle = triangles[i]
+ if (triangle.p1.id > nvertices or
+ triangle.p2.id > nvertices or
+ triangle.p3.id > nvertices) then
+ remove(triangles, i)
+ end
+ end
+
+ for _ = 1,3 do remove(vertices) end
+
+ return triangles
+
+end
+
+return Delaunay
\ No newline at end of file
diff --git a/files/utils/delaunay/bench.lua b/files/utils/delaunay/bench.lua
new file mode 100644
index 0000000..af2cd84
--- /dev/null
+++ b/files/utils/delaunay/bench.lua
@@ -0,0 +1,38 @@
+local Delaunay = require ('Delaunay')
+local Point = Delaunay.Point
+
+math.randomseed(os.time())
+
+local function newPoint()
+ local x, y = math.random(), math.random()
+ return Point(x * 1000, y * 1000)
+end
+
+local MAX_POINTS = arg[1] or 500
+local N_TESTS = arg[2] or 10
+
+local function genPoints(n)
+ local points = {}
+ for i = 1, n do
+ points[i] = newPoint()
+ end
+ return points
+end
+
+local function time(f, p)
+ local start_time = os.clock()
+ local result = f(unpack(p))
+ local duration = (os.clock() - start_time) * 1000
+ assert(result~=nil, 'Unexpected output, returned nil')
+ return duration
+end
+
+local function main()
+ for i = 1, N_TESTS do
+ local p = genPoints(MAX_POINTS)
+ local duration = time(Delaunay.triangulate, p)
+ print(('Test %02d: triangulating %04d points in %.2f ms'):format(i, MAX_POINTS, duration))
+ end
+end
+
+main()
\ No newline at end of file