Does anyone have Lua code for Delauney triangulation or Voronoi diagram generation?

I have a few ideas for games that can only be made with this kind of code. Normally, I would use a C++ library for some optimized code, but we don’t have access to external libraries with Corona. Does anyone have something like Fortune’s Algorithm for Voronoi decomposition in Lua? [import]uid: 5652 topic_id: 2678 reply_id: 302678[/import]

Your problem would probably be that there aren’t many primitives for visualising such stuff in Corona. Wireframe is probably about it, as you can’t set quad vertices for those nice image warping effects, if that’s what you’re after.

Anyway, others have done it in ActionScript, so that should give a clue on how to go about it in script.

It would be very nice to be able to set quad vertices in Corona. [import]uid: 3953 topic_id: 2678 reply_id: 7815[/import]

That scripts looks way to hard to me to implement, and I don’t know if hard-core number crunching in pure LUA is the best idea. Thanks anyway.

There are many implementations in C or C++. I wish we could use this kind of code :frowning: [import]uid: 5652 topic_id: 2678 reply_id: 8255[/import]

OK, after a week or hard work, I got the code ported :slight_smile:

But I can only handle a small number of points! maybe 20-30

I really want something like this:
http://wonderfl.net/c/iNy0/fullscreen

If the Lua scripts get compiled to Obj-C, why is Corona so slow? Pure Obj-C apps seem much much faster.

How can we get more speeeeeeeed?? [import]uid: 5652 topic_id: 2678 reply_id: 9278[/import]

I don’t think the Lua code is converted to native code. But I would have expected that, given a similar runtime environment, Corona would be as fast as Flash (nice demo your link points to). How about posting your code so maybe other more experienced Corona developers (that’s not me) can offer some optimisation tips? [import]uid: 3953 topic_id: 2678 reply_id: 9313[/import]

Sure. Have a poke around if you like :slight_smile:

[code]
–[[

Copyright © 2010 David Ng

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.

Based of the work of Steve J. Fortune (1987) A Sweepline Algorithm for Voronoi Diagrams,
Algorithmica 2, 153-174, and its translation to C++ by Matt Brubeck,
http://www.cs.hmc.edu/~mbrubeck/voronoi.html
–]]
math.randomseed(os.time())

Heap = {}
function Heap:new()
o = {heap = {}, nodes = {}}
setmetatable(o, self)
self.__index = self
return o
end

function Heap:push(k, v)
assert(v ~= nil, “cannot push nil”)
local t = self.nodes
local h = self.heap
local n = #h + 1 – node position in heap array (leaf)
local p = (n - n % 2) / 2 – parent position in heap array
h[n] = k – insert at a leaf
t[k] = v
while n > 1 and t[h[p]] > v do – climb heap?
h[p], h[n] = h[n], h[p]
n = p
p = (n - n % 2) / 2
end
end

function Heap:pop()
local t = self.nodes
local h = self.heap
local s = #h
assert(s > 0, “cannot pop from empty heap”)
local e = h[1] – min (heap root)
local r = t[e]
local v = t[h[s]]
h[1] = h[s] – move leaf to root
h[s] = nil – remove leaf
t[e] = nil
s = s - 1
local n = 1 – node position in heap array
local p = 2 * n – left sibling position
if s > p and t[h[p]] > t[h[p + 1]] then
p = 2 * n + 1 – right sibling position
end
while s >= p and t[h[p]] < v do – descend heap?
h[p], h[n] = h[n], h[p]
n = p
p = 2 * n
if s > p and t[h[p]] > t[h[p + 1]] then
p = 2 * n + 1
end
end
return e, r
end

function Heap:isEmpty()
return self.heap[1] == nil
end

DoubleLinkedList = {}
function DoubleLinkedList:new()
o = {first = nil, last = nil} – empty list head
setmetatable(o, self)
self.__index = self
return o
end

function DoubleLinkedList:insertAfter(node, data)
local new = {prev = node, next = node.next, x = data.x, y = data.y } – creates a new node
node.next = new – the node after node is the new node
if node == self.last then – check if the old node is the last node…
self.last = new – …and set the new node as last node
else
–otherwise set the next nodes previous node to the new one
new.next.prev = new
end
return new – return the new node
end

function DoubleLinkedList:insertAtStart(data)
local new = {prev = nil, next = self.first, x = data.x, y = data.y} – create the new node
if not self.first then – check if the list is empty
self.first = new – the new node is the first and the last in this case
self.last = new
else
– the node before the old first node is the new first node
self.first.prev = new
self.first = new – update the list’s first field
end
return new
end

function DoubleLinkedList:delete(node)
if node == self.first then – check if the node is the first one…
– …and set the new first node if we remove the first
self.first = node.next
else
– set the previous node’s next node to the next node
node.prev.next = node.next
end

if node == self.last then – check if the node is the last one…
– …the new last node is the node before the deleted node
self.last = node.prev
else
node.next.prev = node.prev – update the next node’s prev field
end
end

function DoubleLinkedList:nextNode(node)
return (not node and self.first) or node.next
end

function processEvent(event)
if event.valid then
segment = {startPoint = {x = event.x, y = event.y}, endPoint = {x = 0, y = 0}, done = false, type = 1}
table.insert(segments, segment)
–Remove the associated arc from the front, and update segment info

beachline:delete(event.arc)

if event.arc.prev then
event.arc.prev.seg1 = segment
end

if event.arc.next then
event.arc.next.seg0 = segment
end

–Finish the edges before and after arc.
if event.arc.seg0 then
event.arc.seg0.endPoint = {x = event.x, y = event.y}
event.arc.seg0.done = true
end

if event.arc.seg1 then
event.arc.seg1.endPoint = {x = event.x, y = event.y}
event.arc.seg1.done = true
end

– debugging vertix list!!!
table.insert(vertex, {x = event.x, y = event.y})

–Recheck circle events on either side of p:
if (event.arc.prev) then
check_circle_event(event.arc.prev, event.x)
end
if (event.arc.next) then
check_circle_event(event.arc.next, event.x)
end
event.arc = nil
end
event = nil
end
function processPoint(point)
–Adds a point to the beachline
local intersect = intersect
if (not beachline.first) then
beachline:insertAtStart(point)
return
end

–Find the current arc(s) at height p.y (if there are any).
for arc in beachline.nextNode, beachline do
z = (intersect(point,arc))
if z then
–New parabola intersects arc i. If necessary, duplicate i.
– ie if there is a next node, but there is not interation, then creat a duplicate
if not (arc.next and (intersect(point,arc.next))) then
beachline:insertAfter(arc, arc)
else
–print(“test”, arc.next,intersect(point,arc.next).x,intersect(point,arc.next).y, z.x,z.y )
return
end
arc.next.seg1 = arc.seg1

–Add p between i and i->next.
beachline:insertAfter(arc, point)

segment = {startPoint = {x = z.x, y = z.y}, endPoint = {x = 0, y = 0}, done = false, type = 2}
segment2 = {startPoint = {x = z.x, y = z.y}, endPoint = {x = 0, y = 0}, done = false, type = 2}

– debugging segment list!!!
table.insert(segments, segment)
table.insert(segments, segment2)

–Add new half-edges connected to i’s endpoints.
arc.next.seg0 = segment
arc.seg1 = segment
arc.next.seg1 = segment2
arc.next.next.seg0 = segment2

–Check for new circle events around the new arc:
check_circle_event(arc, point.x)
check_circle_event(arc.next, point.x)
check_circle_event(arc.next.next, point.x)

return

end
end
–Special case: If p never intersects an arc, append it to the list.
– Find the last node.

beachline:insertAtStart(point)

segment = {startPoint = {x = X0, y = (beachline.last.y + beachline.last.prev.y) / 2}, endPoint = {x = 0, y = 0}, done = false, type = 3}

table.insert(segments, segment)

beachline.last.seg0 = segment
beachline.last.prev.seg1 = segment
end

function check_circle_event(arc, x0)
–Look for a new circle event for arc i.
–Invalidate any old event.

if (arc.event and arc.event.x ~= x0) then
arc.event.valid = false
end
arc.event = nil

if ( not arc.prev or not arc.next) then
return
end

local a = arc.prev
local b = arc
local c = arc.next

if ((b.x-a.x)*(c.y-a.y) - (c.x-a.x)*(b.y-a.y) >= 0) then
return false
end

–Algorithm from O’Rourke 2ed p. 189.
local A = b.x - a.x
local B = b.y - a.y
local C = c.x - a.x
local D = c.y - a.y
local E = A*(a.x+b.x) + B*(a.y+b.y)
local F = C*(a.x+c.x) + D*(a.y+c.y)
local G = 2*(A*(c.y-b.y) - B*(c.x-b.x))

if (G == 0) then
–return false --Points are co-linear
print(“g is 0”)
end

–Point o is the center of the circle.
local o = {}
o.x = (D*E-B*F)/G
o.y = (A*F-C*E)/G

–o.x plus radius equals max x coordinate.
local x = o.x + math.sqrt( math.pow(a.x - o.x, 2) + math.pow(a.y - o.y, 2) )

if x and x > x0 then
–Create new event.
arc.event = {x = o.x, y = o.y, arc = arc, valid = true, event = true}
events:push(arc.event, x)
end
end

function intersect(point, arc)
–Will a new parabola at point p intersect with arc i?
local res = {}
if (arc.x == point.x) then
return false
end

if (arc.prev) then
–Get the intersection of i->prev, i.
a = intersection(arc.prev, arc, point.x).y
end
if (arc.next) then
–Get the intersection of i->next, i.
b = intersection(arc, arc.next, point.x).y
end
–print(“intersect”, a,b,p.y)
if (( not arc.prev or a <= point.y) and (not arc.next or point.y <= b)) then
res.y = point.y
– Plug it back into the parabola equation.
res.x = (arc.x*arc.x + (arc.y-res.y)*(arc.y-res.y) - point.x*point.x) / (2*arc.x - 2*point.x)
return res
end
return false
end
function intersection(p0, p1, l)
– Where do two parabolas intersect?

local res = {}
local p = {x = p0.x, y = p0.y}

if (p0.x == p1.x) then
res.y = (p0.y + p1.y) / 2
elseif (p1.x == l) then
res.y = p1.y
elseif (p0.x == l) then
res.y = p0.y
p = p1
else
– Use the quadratic formula.
local z0 = 2*(p0.x - l);
local z1 = 2*(p1.x - l);

local a = 1/z0 - 1/z1;
local b = -2*(p0.y/z0 - p1.y/z1);
local c = (p0.y*p0.y + p0.x*p0.x - l*l)/z0 - (p1.y*p1.y + p1.x*p1.x - l*l)/z1
res.y = ( -b - math.sqrt(b*b - 4*a*c) ) / (2*a)
end

– Plug back into one of the parabola equations.
res.x = (p.x*p.x + (p.y-res.y)*(p.y-res.y) - l*l)/(2*p.x-2*l)
return res
end

function finishEdges()
–Advance the sweep line so no parabolas can cross the bounding box.
l = X1 + (X1-X0) + (Y1-Y0)

–Extend each remaining segment to the new parabola intersections.
for arc in beachline.nextNode, beachline do
if arc.seg1 then
p = intersection(arc, arc.next, l*2)
arc.seg1.endPoint = {x = p.x, y = p.y}
arc.seg1.done = true
end
end
end

points_list = {}
graphics = {}

X0 = 50
X1 = 700
Y0 = 50
Y1 = 970
NUMPOINT = 100

–Point that share the exact same coordinates can cause problems; adding a random decimal is a quick fix.
for i = 1,NUMPOINT do
points_list[i] = {x = math.random(X0,X1) + math.random(),y = math.random(Y0,Y1) + math.random(),dx = math.random(-2,2), dy = math.random(-2,2)}
end

voronoi = {}
function voronoi:enterFrame( event )

–Clear and reset tables
for i = #graphics,1,-1 do
graphics[i]:removeSelf()
graphics[i] = nil
end
graphics = {}
beachline = DoubleLinkedList:new()
events = Heap:new()
vertex = {}
segments = {}

–Update point positions
for i = 1,#points_list do
points_list[i].x = points_list[i].x + points_list[i].dx
if points_list[i].x < X0 then
points_list[i].x = X0
points_list[i].dx = -1*points_list[i].dx
end
if points_list[i].x > X1 then
points_list[i].x = X1
points_list[i].dx = -1*points_list[i].dx
end

points_list[i].y = points_list[i].y + points_list[i].dy
if points_list[i].y < Y0 then
points_list[i].y = Y0
points_list[i].dy = -1*points_list[i].dy
end
if points_list[i].y > Y1 then
points_list[i].y = Y1
points_list[i].dy = -1*points_list[i].dy
end

local x = display.newCircle(points_list[i].x,points_list[i].y, 5)
x:setFillColor(0,255,0,255)
table.insert(graphics,x)
events:push(points_list[i], points_list[i].x)
end

while not events:isEmpty() do
e, x = events:pop()
if e.event then
processEvent(e)
else
processPoint(e)
end
end
finishEdges()

–Draw the vertex and line segments
for i = 1, #vertex do
local x = display.newCircle(vertex[i].x,vertex[i].y, 5)
x:setFillColor(0,0,255,255)
table.insert(graphics,x)
end
for i = 1,#segments do
if segments[i].done then
x = display.newLine(segments[i].startPoint.x,segments[i].startPoint.y,segments[i].endPoint.x,segments[i].endPoint.y)
x:setColor( 255, 0, 0, 255 )
x.width = 1
table.insert(graphics, x)
end
end
end

Runtime:addEventListener( “enterFrame”, voronoi )
[/code] [import]uid: 5652 topic_id: 2678 reply_id: 9314[/import]

That’s quite a bit of work you’ve done there. And you’re right about the speed. Totally unusable on the device.

I might just have that poke around a little later. [import]uid: 3953 topic_id: 2678 reply_id: 9319[/import]

From a cursory glance, without diving into the code… can you make a one line width rectangle instead of a line? and measure any difference?

I did run the code in the Corona sim but not on my device.

c [import]uid: 24 topic_id: 2678 reply_id: 9327[/import]

Way too much stuff happening in enterFrame.

It needs to be reworked so that tables and DisplayObjects are not created or destroyed within it for starters. [import]uid: 3953 topic_id: 2678 reply_id: 9328[/import]

specially at the enter frame when you have to delete all the objects…every time through the event loop.

c. [import]uid: 24 topic_id: 2678 reply_id: 9329[/import]

I’ve had a bit of a play with it but don’t have time right now to follow it through, but some useful optimisations would be:

Create tables before start and fill them with nil entries (or circles in the case of the graphics table).
graphics table needs exactly NUMPOINT entries
vertex about 200 max
segment 400 max etc

Instead of creating and deleting, update them using an index:

graphics[graphicsIndex] = value; graphicsIndex = graphicsIndex + 1

This is where I really miss C notation: graphics[graphicsIndex++] = value

There are only circles in the graphics table, so fill it up before start then just change xy and size values in enterFrame

Basically only do per-frame calculations in enterFrame and reuse tables and graphic items within. Later on, dereference table items:

local ipl = points\_list[i]

When reading values repeatedly within the main loop. [import]uid: 3953 topic_id: 2678 reply_id: 9330[/import]

OK, I can easily reuse the circle objects, but can I reuse lines? I guess for each line segment I could calculate the distance between the two points, change the length of the line, rotate it and move it to the correct location. But seeing that lines are opengl primitives, this seems overkill :wink:

Can I modify line some other way? eg for a line x, something like x.LineCoords = (x0 = 1, y0 =2, x1 = 3, y1 = 4) --? [import]uid: 5652 topic_id: 2678 reply_id: 9530[/import]

It does seem overkill, but I think that’s the only way to do it. But it would have to be with rects, as it looks like you can’t set line points once created. [import]uid: 3953 topic_id: 2678 reply_id: 9548[/import]

I can’t even color-fill the calculated shapes :frowning:

Corona devs, please give us some rudimentary access to OpenGL ES primitives. We can handle the power, we promise. [import]uid: 5652 topic_id: 2678 reply_id: 9588[/import]

OK, I know it’s an old thread but this is such a great piece of code. First off I made it so it was a “one-time” run to create random points JUST ONCE and then I use it to create a starmap, but I need starlanes between the points themselves (I think that’s the actual Delaunay part of the Delaunay/Voronoi combo). But does anyone know how I’d apply the “finishEdges” code to the points themselves?! I’ve been trolling the net on and off for a year or so to find a solution in LUA but I couldn’t find anything I could actually piece together in a usable way (or it was for a different framework I couldn’t shoe-horn into Corona).

http://www.cs.cornell.edu/home/chew/Delaunay.html

and hit the Delaunay Triangulation…THAT’S what I’m having trouble with.

Oh wait, actually somewhere in the forums I found something that might fit the bill, a tesselation chunk of code to draw filled polygons that were complex…I’m gonna go look for that too.

Just thought I’d ask since such a great deal of code was already thrown with gusto at this. :slight_smile: [import]uid: 11636 topic_id: 2678 reply_id: 112446[/import]

Hi,

This is my code. I’m glad someone found a use for it; it took ages to write!
I gave up on using Corona for the time being, as I need pixel access, and real polygon fills (not hacks).

Here is the code for Delauney Triangulation, I hope it help you in you project! In return, could your give me a Promo code for an iOS version of the app you use it in? :slight_smile:

Have fun with it!

-Dave

[lua]–[[

Copyright © 2010 David Ng

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.

Based of the work of Paul Bourke (1989), Efficient Triangulation
Algorithm Suitable for Terrain Modelling.
http://local.wasp.uwa.edu.au/~pbourke/papers/triangulate/index.html

–]]

function delaunay(points_list)
local triangle_list = {}
local numpoints = #points_list

–Insertion sort by x
local w = points_list[1].x
for i = 1, numpoints do
local p = points_list[i]
local x = p.x
if x < w then
local j = i
while j>1 and points_list[j -1].x > x do
points_list[j] = points_list[j - 1]
j = j - 1
end
points_list[j] = p;
else
w = x
end
end

–Create Supertriangle
table.insert(points_list, {x = -5000, y = -5000})
table.insert(points_list, {x = 5000, y = 0})
table.insert(points_list, {x = 0, y = 5000})
table.insert(triangle_list, {numpoints+1,numpoints+2,numpoints+3})

local function inCircle(point, triangle_counter)
–[[
‘’‘Series of calculations to check if a certain point lies inside lies inside the circumcircle
made up by points in triangle (x1,y1) (x2,y2) (x3,y3)’’’
#adapted from Dimitrie Stefanescu’s Rhinoscript version

#Return TRUE if the point (xp,yp)
#The circumcircle centre is returned in (xc,yc) and the radius r
#NOTE: A point on the edge is inside the circumcircle --]]
if triangle_list[triangle_counter].done then
return false
end

local xp = point.x
local yp = point.y

if triangle_list[triangle_counter].r then

local r = triangle_list[triangle_counter].r
local xc = triangle_list[triangle_counter].xc
local yc = triangle_list[triangle_counter].yc

local dx = xp - xc
local dy = yp - yc
local rsqr = r*r
local drsqr = dx * dx + dy * dy

if xp > xc + r then
triangle_list[triangle_counter].done = true
end

if drsqr <= rsqr then
return true
else
return false
end
end

local x1 = points_list[triangle_list[triangle_counter][1]].x
local y1 = points_list[triangle_list[triangle_counter][1]].y
local x2 = points_list[triangle_list[triangle_counter][2]].x
local y2 = points_list[triangle_list[triangle_counter][2]].y
local x3 = points_list[triangle_list[triangle_counter][3]].x
local y3 = points_list[triangle_list[triangle_counter][3]].y
local eps = 0.0001

if math.abs(y1-y2) < eps and math.abs(y2-y3) < eps then
return false
end

if math.abs(y2-y1) < eps then
m2 = -(x3 - x2) / (y3 - y2)
mx2 = (x2 + x3) / 2
my2 = (y2 + y3) / 2
xc = (x2 + x1) / 2
yc = m2 * (xc - mx2) + my2
elseif math.abs(y3-y2) < eps then

m1 = -(x2 - x1) / (y2 - y1)

mx1 = (x1 + x2) / 2
my1 = (y1 + y2) / 2
xc = (x3 + x2) / 2
yc = m1 * (xc - mx1) + my1
else
m1 = -(x2 - x1) / (y2 - y1)
m2 = -(x3 - x2) / (y3 - y2)
mx1 = (x1 + x2) / 2
mx2 = (x2 + x3) / 2
my1 = (y1 + y2) / 2
my2 = (y2 + y3) / 2
xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2)
yc = m1 * (xc - mx1) + my1
end

dx = x2 - xc
dy = y2 - yc
rsqr = dx * dx + dy * dy
r = math.sqrt(rsqr)

triangle_list[triangle_counter].r = r
triangle_list[triangle_counter].xc = xc
triangle_list[triangle_counter].yc = yc

dx = xp - xc
dy = yp - yc
drsqr = (dx * dx) + (dy * dy)

if drsqr <= rsqr then
return true
else
return false
end
end

for i = 1, numpoints do
local edges = {}
local point = points_list[i]

local triangles_remain = true
local j = 1

while triangles_remain do

if inCircle(point, j) then
table.insert(edges, {triangle_list[j][1],triangle_list[j][2]})
table.insert(edges, {triangle_list[j][2],triangle_list[j][3]})
table.insert(edges, {triangle_list[j][3],triangle_list[j][1]})
table.remove(triangle_list,j)
j = j - 1
end

j = j + 1
if j == (#triangle_list + 1) then
triangles_remain = false
end
end

–Remove duplicates
local k = 1
while k < #edges do
l = k + 1
while l <= #edges do
if edges[k][1] == edges[l][2] and edges[k][2] == edges[l][1] then
edges[k][1] = nil
edges[k][2] = nil
edges[l][1] = nil
edges[l][2] = nil
end
l = l + 1
end
k = k + 1
end

– Make triangles from edges
for k = 1, #edges do
if edges[k][1] then
table.insert(triangle_list, {i,edges[k][1],edges[k][2]})
end
end
end

–remove Super Triangle and its verticies
local i = 1
while i < #triangle_list + 1 do
if triangle_list[i][1] > numpoints then
table.remove(triangle_list,i)
i = i-1
elseif triangle_list[i][2] > numpoints then
table.remove(triangle_list,i)
i = i-1
elseif triangle_list[i][3] > numpoints then
table.remove(triangle_list,i)
i = i-1
end
i = i+1
end
points_list[numpoints+1] = nil
points_list[numpoints+2] = nil
points_list[numpoints+3] = nil

return triangle_list
end

stars = {}
lines = {}
points_list = {}
speed_list = {}
for i = 1,30 do
points_list[i] = {x = math.random(0,768),y = math.random(0,1024),dx = math.random(-2,2), dy = math.random(-2,2)}
end

function stars:enterFrame( event )

for i = #lines,1,-1 do
lines[i]:removeSelf()
lines[i] = nil
end
lines = {}

for i = 1,#points_list do

points_list[i].x = points_list[i].x + points_list[i].dx
if points_list[i].x < 0 then
points_list[i].x = 0
points_list[i].dx = -1*points_list[i].dx
end
if points_list[i].x > 768 then
points_list[i].x = 768
points_list[i].dx = -1*points_list[i].dx
end

points_list[i].y = points_list[i].y + points_list[i].dy
if points_list[i].y < 0 then
points_list[i].y = 0
points_list[i].dy = -1*points_list[i].dy
end
if points_list[i].y > 1024 then
points_list[i].y = 1024
points_list[i].dy = -1*points_list[i].dy
end

end

local tri = delaunay(points_list)

—[[
for i = 1, #tri do
local x

x = display.newLine(points_list[tri[i][1] ].x, points_list[tri[i][1] ].y, points_list[tri[i][2] ].x, points_list[tri[i][2] ].y)
x:setColor( 255, 0, 255, 255 )
x.width = 3
table.insert(lines, x)
x = nil
x = display.newLine(points_list[tri[i][2] ].x, points_list[tri[i][2] ].y, points_list[tri[i][3] ].x, points_list[tri[i][3] ].y)
x:setColor( 255, 255, 0, 255 )
x.width = 3
table.insert(lines, x)
x = nil
x = display.newLine(points_list[tri[i][3] ].x, points_list[tri[i][3] ].y, points_list[tri[i][1] ].x, points_list[tri[i][1] ].y)
x:setColor( 0, 255, 255, 255 )
x.width = 3
table.insert(lines, x)
x = nil
end
–]]

end

–stars:enterFrame( )
Runtime:addEventListener( “enterFrame”, stars )[/lua] [import]uid: 5652 topic_id: 2678 reply_id: 112461[/import]

Holy sh*tsnacks Dave! Thanks a ton! You wrote the whole thing for me!! :slight_smile: Next time, I’ll request “Please write my game for me and make it extra snazzy!” lol!!!

Really, this does mean a lot to me. I’ve banged my head against this challenge before and just got nowhere. Now, you’ve delivered a complete version to me. I hope others will realize just what a mathematical challenge this is and how you just handed us a great solution, finished and ready to go!! Can I paypal you a coffee or something? Lemme know!!!

Super appreciative,
Mario [import]uid: 11636 topic_id: 2678 reply_id: 112536[/import]

Nah, use it and enjoy. I’m like coding hard math stuff, but when ever I try making a game, I get the engine done, but then get bored making the menus and tutorials, and progress grinds to a halt.

I have a great tetris/jigsaw puzzle game called Shapeshifter 90% finished, but I can’t get around to making more levels, and a game called Hammerhead Handyman, where you are a, well, a hammerhead shark who has to assemble ikea-type furniture to progress though levels, up to repairing a nuclear reactor core. It uses physics, and the shark has a great swimming action, using programmed box2d joints.

But, yeah, as soon as I had the engine done… bored! Thats why my Corona license expired, unused. But I got in during the beta, so I didn’t loose too much money.

[import]uid: 5652 topic_id: 2678 reply_id: 112675[/import]

Hey Dave,

I really like your code, too - I’ve tried making my own, but just end up with a lot of triangles:

http://developer.coronalabs.com/code/simple-polygon-triangulation#comment-139497

Your code reduces these beautifully. The only problem is that it uses the point list as the point cloud outline. This means that it can’t be used for triangulating a concave polygon.

Do you have a solution for that, please?

Thanks,

Matt. [import]uid: 8271 topic_id: 2678 reply_id: 139561[/import]

Have a look at this discussion thread:
http://www.mathworks.com/matlabcentral/newsreader/view_thread/306105

-David [import]uid: 5652 topic_id: 2678 reply_id: 139565[/import]