Séparation en trois fichiers lua, et fonction d'ajout de points d'une grille à l...
[delaunay.git] / luamesh.lua
1 require "luamesh-polygon"
2 require "luamesh-tex"
3
4 -- Bowyer and Watson algorithm
5 -- Delaunay meshing
6 function BowyerWatson (listPoints,bbox)
7    local triangulation = {}
8    local lgth = #listPoints
9    -- add four points to listPoints to have a bounding box
10    listPoints = buildBoundingBox(listPoints)
11    -- the first triangle
12    triangulation[1] = {lgth+1, lgth+2, lgth+3,type="bbox"}
13    -- the second triangle
14    triangulation[2] = {lgth+1, lgth+3, lgth+4,type="bbox"}
15    -- add points one by one
16    for i=1,lgth do
17       -- find the triangles which the circumcircle contained the point to add
18       badTriangles = buildBadTriangles(listPoints[i],triangulation)
19       -- build the polygon of the cavity containing the point to add
20       polygon = buildCavity(badTriangles, triangulation)
21       -- remove the bad triangles
22       for j=1,#badTriangles do
23          table.remove(triangulation,badTriangles[j]-(j-1))
24       end
25       -- build the new triangles and add them to triangulation
26       for j=1,#polygon do
27          if((polygon[j][1]>lgth) or (polygon[j][2]>lgth) or (i>lgth)) then
28             table.insert(triangulation,{polygon[j][1],polygon[j][2],i,type="bbox"})
29          else
30             table.insert(triangulation,{polygon[j][1],polygon[j][2],i,type="in"})
31          end
32       end
33    end  -- end adding points of the listPoints
34    -- remove bounding box
35    if(bbox ~= "bbox") then
36       triangulation = removeBoundingBox(triangulation,lgth)
37       table.remove(listPoints,lgth+1)
38       table.remove(listPoints,lgth+1)
39       table.remove(listPoints,lgth+1)
40       table.remove(listPoints,lgth+1)
41    end
42    return triangulation
43 end
44
45
46 function buildBoundingBox(listPoints)
47    -- listPoints : list of points
48    -- epsV        : parameter for the distance of the bounding box
49    local xmin, xmax, ymin, ymax, eps
50    xmin = 1000
51    ymin = 1000
52    xmax = -1000
53    ymax = -1000
54    for i=1,#listPoints do
55       if (listPoints[i].x < xmin) then
56          xmin = listPoints[i].x
57       end
58       if (listPoints[i].x > xmax) then
59          xmax = listPoints[i].x
60       end
61       if (listPoints[i].y < ymin) then
62          ymin = listPoints[i].y
63       end
64       if (listPoints[i].y > ymax) then
65          ymax = listPoints[i].y
66       end
67    end
68    eps = math.max(math.abs(xmax-xmin),math.abs(ymax-ymin))*0.15
69    xmin = xmin - eps
70    xmax = xmax + eps
71    ymin = ymin - eps
72    ymax = ymax + eps
73    -- add points of the bounding box in last positions
74    table.insert(listPoints,{x=xmin,y=ymin,type="bbox"})
75    table.insert(listPoints,{x=xmin,y=ymax,type="bbox"})
76    table.insert(listPoints,{x=xmax,y=ymax,type="bbox"})
77    table.insert(listPoints,{x=xmax,y=ymin,type="bbox"})
78    return listPoints
79 end
80
81 function BoundingBox(listPoints)
82    -- listPoints : list of points
83    -- epsV        : parameter for the distance of the bounding box
84    local xmin, xmax, ymin, ymax, eps
85    xmin = 1000
86    ymin = 1000
87    xmax = -1000
88    ymax = -1000
89    for i=1,#listPoints do
90       if (listPoints[i].x < xmin) then
91          xmin = listPoints[i].x
92       end
93       if (listPoints[i].x > xmax) then
94          xmax = listPoints[i].x
95       end
96       if (listPoints[i].y < ymin) then
97          ymin = listPoints[i].y
98       end
99       if (listPoints[i].y > ymax) then
100          ymax = listPoints[i].y
101       end
102    end
103    eps = math.max(math.abs(xmax-xmin),math.abs(ymax-ymin))*0.15
104    xmin = xmin - eps
105    xmax = xmax + eps
106    ymin = ymin - eps
107    ymax = ymax + eps
108    return xmin, xmax, ymin, ymax
109 end
110
111 function removeBoundingBox(triangulation,lgth)
112    -- build the four bounding box edge
113    point1 = lgth+1
114    point2 = lgth+2
115    point3 = lgth+3
116    point4 = lgth+4
117    -- for all triangle
118    local newTriangulation = {}
119    for i=1,#triangulation do
120       boolE1 = pointInTriangle(point1,triangulation[i])
121       boolE2 = pointInTriangle(point2,triangulation[i])
122       boolE3 = pointInTriangle(point3,triangulation[i])
123       boolE4 = pointInTriangle(point4,triangulation[i])
124       if((not boolE1) and (not boolE2) and (not boolE3) and (not boolE4)) then
125          table.insert(newTriangulation,triangulation[i])
126       end
127    end
128    return newTriangulation
129 end
130
131
132 function buildBadTriangles(point, triangulation)
133    local badTriangles = {}
134    for j=1,#triangulation do -- for all triangles
135       A = listPoints[triangulation[j][1]]
136       B = listPoints[triangulation[j][2]]
137       C = listPoints[triangulation[j][3]]
138       center, radius = circoncircle(A,B,C)
139       CP = Vector(center,point)
140       if(VectorNorm(CP)<radius) then -- the point belongs to the circoncirle
141          table.insert(badTriangles,j)
142       end
143    end
144    return badTriangles
145 end
146
147 -- construction of the cavity composed by the bad triangles around the point to add
148 function buildCavity(badTriangles, triangulation)
149    local polygon = {}
150    for j=1,#badTriangles do -- for all bad triangles
151       ind = badTriangles[j]
152       for k=1,3 do -- for all edges
153          edge = {triangulation[ind][k],triangulation[ind][k%3+1]}
154          edgeBord = false
155          for l = 1,#badTriangles do -- for all badtriangles
156             badInd = badTriangles[l]
157             if(badInd ~= ind) then -- if not the current one
158                edgeBord = edgeBord or edgeInTriangle(edge,triangulation[badInd])
159             end
160          end --
161          -- if the edge does not belong to another bad triangle
162          if(edgeBord == false) then
163             -- insert the edge to the cavity
164             table.insert(polygon,edge)
165          end
166       end --
167    end --
168    return polygon
169 end
170
171 function edgeInTriangle(e,t)
172    in1 = false
173    in2 = false
174    for i=1,3 do
175       if e[1] == t[i] then
176          in1 = true
177       end
178       if e[2] == t[i] then
179          in2 = true
180       end
181    end
182    out = (in1 and in2)
183    return out
184 end
185
186 function pointInTriangle(e,t)
187    in1 = false
188    for i=1,3 do
189       if e == t[i] then
190          in1 = true
191       end
192    end
193    return in1
194 end
195
196
197 function Vector(A,B)
198    local out = {x = B.x - A.x, y = B.y - A.y}
199    return out
200 end
201
202 function VectorNorm(NP)
203    return math.sqrt(NP.x*NP.x +NP.y*NP.y)
204 end
205
206 -- circoncircle
207 function circoncircle(M, N, P)
208    -- Compute center and radius of the circoncircle of the triangle M N P
209
210    -- return : (center [Point],radius [float])
211
212    local MN = Vector(M,N)
213    local NP = Vector(N,P)
214    local PM = Vector(P,M)
215    m = VectorNorm(NP)  -- |NP|
216    n = VectorNorm(PM)  -- |PM|
217    p = VectorNorm(MN)  -- |MN|
218
219    d = (m + n + p) * (-m + n + p) * (m - n + p) * (m + n - p)
220    if d > 0 then
221       rad = m * n * p / math.sqrt(d)
222    else
223       rad = 0
224    end
225    d = -2 * (M.x * NP.y + N.x * PM.y + P.x * MN.y)
226    O = {x=0, y=0}
227    OM = Vector(O, M)
228    ON = Vector(O, N)
229    OP = Vector(O, P)
230    om2 = math.pow(VectorNorm(OM),2)  -- |OM|**2
231    on2 = math.pow(VectorNorm(ON),2)  -- |ON|**2
232    op2 = math.pow(VectorNorm(OP),2)  -- |OP|**2
233    x0 = -(om2 * NP.y + on2 * PM.y + op2 * MN.y) / d
234    y0 = (om2 * NP.x + on2 * PM.x + op2 * MN.x) / d
235    if d == 0 then
236       Out = {nil, nil}
237    else
238       Out = {x=x0, y=y0}
239    end
240    return Out, rad  -- (center [Point], R [float])
241 end
242
243 -- compute the list of the circumcircle of a triangulation
244 function listCircumCenter(listPoints,triangulation)
245    local list = {}
246    for j=1,#triangulation do
247       A = listPoints[triangulation[j][1]]
248       B = listPoints[triangulation[j][2]]
249       C = listPoints[triangulation[j][3]]
250       center, radius = circoncircle(A,B,C)
251       table.insert(list,{x=center.x,y=center.y,r=radius})
252    end
253    return list
254 end
255
256 -- find the three neighbour triangles of T
257 function findNeighbour(T,i,triangulation)
258    -- T : triangle
259    -- i : index of T in triangualation
260    -- triangulation
261
262    list = {}
263    -- define the three edge
264    e1 = {T[1],T[2]}
265    e2 = {T[2],T[3]}
266    e3 = {T[3],T[1]}
267    for j=1,#triangulation do
268       if j~= i then
269          if(edgeInTriangle(e1,triangulation[j])) then
270             table.insert(list,j)
271          end
272          if(edgeInTriangle(e2,triangulation[j])) then
273             table.insert(list,j)
274          end
275          if(edgeInTriangle(e3,triangulation[j])) then
276             table.insert(list,j)
277          end
278       end
279    end
280    return list
281 end
282
283 -- test if edge are the same (reverse)
284 function equalEdge(e1,e2)
285    if(((e1[1] == e2[1]) and (e1[2] == e2[2])) or ((e1[1] == e2[2]) and (e1[2] == e2[1]))) then
286       return true
287    else
288       return false
289    end
290 end
291
292 -- test if the edge belongs to the list
293 function edgeInList(e,listE)
294    output = false
295    for i=1,#listE do
296       if(equalEdge(e,listE[i])) then
297          output = true
298       end
299    end
300    return output
301 end
302
303 -- build the edges of the Voronoi diagram with a given triangulation
304 function buildVoronoi(listPoints, triangulation)
305    local listCircumCircle = listCircumCenter(listPoints, triangulation)
306    local listVoronoi = {}
307    for i=1,#listCircumCircle do
308       listN = findNeighbour(triangulation[i],i,triangulation)
309       for j=1,#listN do
310          edge = {i,listN[j]}
311          if( not edgeInList(edge, listVoronoi)) then
312             table.insert(listVoronoi, edge)
313          end
314       end
315    end
316    return listVoronoi
317 end
318
319 -------------------------- TeX
320 -- build the list of points
321 function buildList(chaine, mode)
322    -- if mode = int : the list is given in the chaine string (x1,y1);(x2,y2);...;(xn,yn)
323    -- if mode = ext : the list is given in a file line by line with space separation
324    local listPoints = {}
325    if mode == "int" then
326       local points = string.explode(chaine, ";")
327       local lgth=#points
328       for i=1,lgth do
329          Sx,Sy=string.match(points[i],"%((.+),(.+)%)")
330          listPoints[i]={x=tonumber(Sx),y=tonumber(Sy)}
331       end
332    elseif mode == "ext" then
333       io.input(chaine) -- open the file
334       text=io.read("*all")
335       lines=string.explode(text,"\n+") -- all the lines
336       tablePoints={}
337       for i=1,#lines do
338          xy=string.explode(lines[i]," +")
339          listPoints[i]={x=tonumber(xy[1]),y=tonumber(xy[2])}
340       end
341    else
342       print("Non existing mode")
343    end
344    return listPoints
345 end
346
347
348 -- function to build a gridpoints from the bounding box
349 -- with a prescribed
350 function buildGrid(listPoints,h)
351    -- listPoints : list of the points of the polygon, ordered
352    -- h : parameter for the grid
353    xmin, xmax, ymin, ymax = BoundingBox(listPoints)
354
355    local grid = rectangleList(xmin,xmax,ymin,ymax,h)
356    return grid
357 end
358
359 -- function to build the list of points in the rectangle
360 function rectangleList(xmin,xmax,ymin,ymax,h)
361    nbrX = math.floor(math.abs(xmax-xmin)/h)
362    nbrY = math.floor(math.abs(ymax-ymin)/h)
363    local listPoints = {}
364    k=1
365    for i=1,(nbrX+1) do
366       for j=1,(nbrY+1) do
367          listPoints[k] = {x = xmin+(i-1)*h, y=ymin+(j-1)*h}
368          k=k+1
369       end
370    end
371    return listPoints
372 end
373
374
375 -- function to add points from a grid to the interior of a polygon
376 function addGridPoints(polygon, grid)
377    local listPoints = polygon
378    k = #polygon
379    for i=1, #grid do
380       --print(grid[i].x,grid[i].y)
381       --print(isInside(polygon,grid[i]))
382       if(isInside(polygon,grid[i])) then
383          k=k+1
384          listPoints[k] = grid[i]
385       end
386    end
387    return listPoints
388 end
389
390
391 local function shallowCopy(original)
392    local copy = {}
393    for key, value in pairs(original) do
394       copy[key] = value
395    end
396    return copy
397 end
398
399 -- function give a real polygon without repeting points
400 function cleanPoly(polygon)
401    polyNew = {}
402    polyCopy = shallowCopy(polygon)
403    e1 = polyCopy[1][1]
404    e2 = polyCopy[1][2]
405    table.insert(polyNew, e1)
406    table.insert(polyNew, e2)
407    table.remove(polyCopy,1)
408    j = 2
409    while #polyCopy>1 do
410       i=1
411       find = false
412       while (i<=#polyCopy and find==false) do
413          bool1 = (polyCopy[i][1] == polyNew[j])
414          bool2 = (polyCopy[i][2] == polyNew[j])
415          if(bool1 or bool2) then -- the edge has a common point with polyNew[j]
416             if(not bool1) then
417                table.insert(polyNew, polyCopy[i][1])
418                find = true
419                table.remove(polyCopy,i)
420                j = j+1
421             elseif(not bool2) then
422                table.insert(polyNew, polyCopy[i][2])
423                find = true
424                table.remove(polyCopy,i)
425                j = j+1
426             end
427          end
428          i=i+1
429       end
430    end
431    return polyNew
432 end
433
434
435
436 -- build the list of points extern and stop at nbr
437 function buildListExt(chaine, stop)
438    listPoints = {}
439    io.input(chaine) -- open the file
440    text=io.read("*all")
441    lines=string.explode(text,"\n+") -- all the lines
442    for i=1,tonumber(stop) do
443       xy=string.explode(lines[i]," +")
444       table.insert(listPoints,{x=tonumber(xy[1]),y=tonumber(xy[2])})
445    end
446    xy=string.explode(lines[stop+1]," +")
447    point={x=tonumber(xy[1]),y=tonumber(xy[2])}
448    return point, listPoints
449 end
450
451 function split(pString, pPattern)
452    local Table = {}  -- NOTE: use {n = 0} in Lua-5.0
453    local fpat = "(.-)" .. pPattern
454    local last_end = 1
455    local s, e, cap = pString:find(fpat, 1)
456    while s do
457       if s ~= 1 or cap ~= "" then
458          table.insert(Table,cap)
459       end
460       last_end = e+1
461       s, e, cap = pString:find(fpat, last_end)
462    end
463    if last_end <= #pString then
464       cap = pString:sub(last_end)
465       table.insert(Table, cap)
466    end
467    return Table
468 end
469
470 function readGmsh(file)
471    io.input(file) -- open the file
472    text=io.read("*all")
473    local lines = split(text,"\n+") -- all the lines
474    listPoints={}
475    triangulation ={}
476    boolNodes = false
477    Jnodes = 0
478    boolElements = false
479    Jelements = 0
480    J=0
481    for i=1,#lines-J do
482       if(lines[i+J] == "$EndNodes") then
483          boolNodes = false
484          -- go to the next line
485       end
486       if(boolNodes) then -- we are in the Nodes environment
487          xy=split(lines[i+J]," +")
488          table.insert(listPoints,{x=tonumber(xy[2]),y=tonumber(xy[3])})
489       end
490       if(lines[i+J] == "$Nodes") then
491          boolNodes = true
492          -- go to the next line
493          J=J+1
494       end
495       if(lines[i+J] == "$EndElements") then
496          boolElements = false
497          -- go to the next line
498       end
499       if(boolElements) then -- we are in the Nodes environment
500          xy=split(lines[i+J]," +")
501          if(tonumber(xy[2]) == 2) then -- if the element is a triangle
502             nbrTags = xy[3]+1
503             table.insert(triangulation,{tonumber(xy[2+nbrTags+1]),tonumber(xy[2+nbrTags+2]),tonumber(xy[2+nbrTags+3])})
504          end
505       end
506       if(lines[i+J] == "$Elements") then
507          boolElements = true
508          -- go to the next line
509          J=J+1
510       end
511    end
512    return listPoints, triangulation
513 end

Licence Creative Commons Les fichiers de Syracuse sont mis à disposition (sauf mention contraire) selon les termes de la
Licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International.