Recently I have learnt about the Delaunay Triangulation and using the Bowyer-Watson algorithm to solve it. When implementing, I followed along from the wikipedia pseudocode and other sources to create my own solution in lua. However, I noticed that when changing the order of the points it sometimes changes the output - resulting in the incorrect outcome but works perfectly other times. The code below is the main triangulation function.
function polygon:Triangulate(superTriangle)
local vertices = {}
for _, v in ipairs(self.nodes) do
table.insert(vertices, v)
end
for _, v in ipairs(superTriangle:GetConnectedNodes()) do
table.insert(vertices, v)
end
table.insert(self.triangles, superTriangle)
for _, v in ipairs(vertices) do
local edgeBuffer = {}
local badTriangles = {}
for i = #self.triangles, 1, - 1 do
local tri = self.triangles[i]
local circle = circumcircle.fromTriangle(tri)
if circle:ContainsNode(v) then
table.insert(badTriangles, tri)
end
end
for _, tri1 in ipairs(badTriangles) do
local edges1 = tri1:GetEdges()
for j1 = 1, 3 do
for _, tri2 in ipairs(badTriangles) do
if tri1 == tri2 then goto continue end
local edges2 = tri2:GetEdges()
for j2 = 1, 3 do
if edges1[j1]:CompareTo(edges2[j2]) then
goto ignore
end
end
::continue::
end
table.insert(edgeBuffer, edges1[j1])
::ignore::
end
end
for _, tri1 in pairs(badTriangles) do
for i, tri2 in pairs(self.triangles) do
if tri2 == tri1 then
table.remove(self.triangles, i)
break
end
end
end
for _, edge in ipairs(edgeBuffer) do
local newTriangle = triangle.new(edge.node1, edge.node2, v)
table.insert(self.triangles, newTriangle)
end
end
for i = #self.triangles, 1, -1 do
local nodes = self.triangles[i]:GetConnectedNodes()
for j = 1, 3 do
local current = nodes[j]
if superTriangle:SharesNode(current) then
table.remove(self.triangles, i)
goto skip
end
end
::skip::
end
The attributes nodes
contains the input points, and triangles
is an initially empty list which stores the output triangles.
The node
class just contains an x and y position, whilst the edge
class contains two nodes. The triangle is therefore made up of 3 nodes (which also has all 3 edges as an attribute).
The ContainsNode
method for circumcircle, is below
function circumcircle:ContainsNode(node)
local newRadius = math.sqrt((node.x - self.x)^2 + (node.y - self.y)^2)
return newRadius <= self.radius
end
The CompareTo
function checks to see if the x and y attributes are equal - and for the edge check the possible nodes and compares those.
When testing, I used the following points
{x = 30, y = 45},
{x = 50, y = 65},
{x = 0, y = 0},
{x = 0, y = 100},
{x = 100, y = 100},
{x = 100, y = 0}
Which produces the following output:
(The blue lines are the calculated triangles, where the red triangle is the super triangle)
When changing the order of the points to
{x = 0, y = 0},
{x = 0, y = 100},
{x = 100, y = 100},
{x = 100, y = 0}
{x = 30, y = 45},
{x = 50, y = 65},
It breaks the output, producing:
I have looked into the functions used to validate whether a node falls into the circumcircle, and don't think it is incorrect but I am not too versed on triangulation and am new to the subject. It would be great if anyone could help. I think I may just be missing something here so poking me in the right direction would be excellent.
return newRadius <= self.radius
-->return newRadius < self.radius
. Nodes on the circumference of a circle are not contained within the circle.polygon:Triangulate
function into several smaller functions so that the mainTriangulate
function can be expressed more clearly. In the process you may find your issue.