1 // Hxt - Copyright (C)
2 // 2016 - 2020 UCLouvain
3 //
4 // See the LICENSE.txt file for license information.
5 //
6 // Contributor(s):
7 //   Célestin Marot
8 
9 #include "HXTSPR.h"
10 #include "hxt_tetOptiUtils.h"
11 #include "hxt_tetFlag.h"
12 #include "predicates.h"
13 
14 
15 #ifdef _MSC_VER
16 #define SPRNOINLINE __declspec(noinline)
17 #else
18 #define SPRNOINLINE __attribute__ ((noinline))
19 #endif
20 
21 // get the index in a tet. of node i from facet j by doing nodeFromFacet[i][j];
22 static const int nodeFromFacet[3][4] = {{1, 2, 3, 0 },
23                                         {3, 3, 1, 1 },
24                                         {2, 0, 0, 2 }};
25 
add_face_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,uint16_t index)26 static inline void add_face_map(SPRCavity* SPR,
27                                 unsigned v0,
28                                 unsigned v1,
29                                 unsigned v2,
30                                 uint16_t index)
31 {
32   SPR->map.faces[v0*(SPR_MAX_PTS*SPR_MAX_PTS)+v1*SPR_MAX_PTS+v2] = index;
33   SPR->map.faces[v1*(SPR_MAX_PTS*SPR_MAX_PTS)+v2*SPR_MAX_PTS+v0] = index;
34   SPR->map.faces[v2*(SPR_MAX_PTS*SPR_MAX_PTS)+v0*SPR_MAX_PTS+v1] = index;
35 }
36 
remove_face_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2)37 static inline void remove_face_map(SPRCavity* SPR,
38                                    unsigned v0,
39                                    unsigned v1,
40                                    unsigned v2)
41 {
42   add_face_map(SPR, v0, v1, v2, UINT16_MAX);
43 }
44 
get_face_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2)45 static inline uint16_t get_face_map(SPRCavity* SPR,
46                                     unsigned v0,
47                                     unsigned v1,
48                                     unsigned v2) {
49   return SPR->map.faces[v0*(SPR_MAX_PTS*SPR_MAX_PTS) + v1*SPR_MAX_PTS + v2];
50 }
51 
52 
decrement_3valences(SPRCavity * SPR,uint8_t v0,uint8_t v1,uint8_t v2)53 static inline void decrement_3valences(SPRCavity* SPR,
54                                        uint8_t v0,
55                                        uint8_t v1,
56                                        uint8_t v2) {
57   SPR->points.array[v0].valence--;
58   SPR->points.array[v1].valence--;
59   SPR->points.array[v2].valence--;
60 }
61 
62 
increment_3valences(SPRCavity * SPR,uint8_t v0,uint8_t v1,uint8_t v2)63 static inline void increment_3valences(SPRCavity* SPR,
64                                        uint8_t v0,
65                                        uint8_t v1,
66                                        uint8_t v2) {
67   SPR->points.array[v0].valence++;
68   SPR->points.array[v1].valence++;
69   SPR->points.array[v2].valence++;
70 }
71 
72 
get_compressed_index(unsigned v0,unsigned v1,unsigned v2,unsigned v3)73 static inline unsigned get_compressed_index(unsigned v0,
74                                             unsigned v1,
75                                             unsigned v2,
76                                             unsigned v3)
77 {
78 #define SPRSWAP(x,y) if(x>y){unsigned tmp=x; x=y; y=tmp;}
79   SPRSWAP(v0,v1)
80   SPRSWAP(v2,v3)
81   SPRSWAP(v0,v2)
82   SPRSWAP(v1,v3)
83   SPRSWAP(v1,v2)
84 #undef SPRSWAP
85 
86   unsigned aux = v3*(v3-3);
87   return v0 + (v1*(v1-1)>>1) + v2*(v2-1)*(v2-2)/6 + aux*(aux+2)/24;
88   // *index = v0 + v1*(v1-1)/2 + v2*(v2-1)*(v2-2)/6 + v3*(v3-1)*(v3-2)*(v3-3)/24;
89 }
90 
91 #ifdef SPR_SAVE_ORIENT3D
add_orient3d(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3)92 static SPRNOINLINE uint32_t add_orient3d(SPRCavity* SPR,
93                                          unsigned v0,
94                                          unsigned v1,
95                                          unsigned v2,
96                                          unsigned v3)
97 {
98   const unsigned config[12][4] = {
99     {0, 1, 2, 3},{0, 2, 3, 1},{0, 3, 1, 2},
100     {1, 2, 0, 3},{1, 0, 3, 2},{1, 3, 2, 0},
101     {2, 3, 0, 1},{2, 0, 1, 3},{2, 1, 3, 0},
102     {3, 1, 0, 2},{3, 0, 2, 1},{3, 2, 1, 0}
103   };
104 
105   const unsigned v[4] = {v0, v1, v2, v3};
106 
107   // compute the orientation
108   double orientation = orient3d(SPR->points.array[v0].coord,
109                                 SPR->points.array[v1].coord,
110                                 SPR->points.array[v2].coord,
111                                 SPR->points.array[v3].coord);
112 
113   uint32_t val = 2 + (orientation>0.0) - (orientation<0.0);
114   uint32_t oppval = 2 + (orientation<0.0) - (orientation>0.0);
115   const unsigned n1 = SPR_MAX_PTS;
116   const unsigned n2 = SPR_MAX_PTS*SPR_MAX_PTS;
117   const unsigned n3 = n2*n1;
118   unsigned ID, index, bit;
119   for (int i=0; i<12; i++)
120   {
121     v0 = v[config[i][0]];
122     v1 = v[config[i][1]];
123     v2 = v[config[i][2]];
124     v3 = v[config[i][3]];
125 
126     ID = v0*n3+v1*n2+v2*n1+v3;
127     index = ID/16;
128     bit = ID%16 * 2;
129     SPR->map.orient3d[index] &= ~(3U<<bit); // clear bits
130     SPR->map.orient3d[index] |= val<<bit; // set bits
131 
132     ID = v0*n3+v1*n2+v3*n1+v2;
133     index = ID/16;
134     bit = ID%16 * 2;
135     SPR->map.orient3d[index] &= ~(3U<<bit); // clear bits
136     SPR->map.orient3d[index] |= oppval<<bit; // set bits
137   }
138 
139   return val;
140 }
141 
get_orient3d(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3)142 static int get_orient3d(SPRCavity* SPR,
143                         unsigned v0,
144                         unsigned v1,
145                         unsigned v2,
146                         unsigned v3)
147 {
148   if(v0==v1 || v0==v2 || v0==v3 || v1==v2 || v1==v3 || v2==v3)
149     return 0;
150   const unsigned n = SPR_MAX_PTS;
151   unsigned ID = v0*(n*n*n)+v1*(n*n)+v2*n+v3;
152   unsigned index = ID/16;
153   unsigned bit = ID%16 * 2;
154   uint32_t val = SPR->map.orient3d[index]>>bit & 3;
155   if(val==0) {
156     return (int)add_orient3d(SPR, v0, v1, v2 ,v3) - 2;
157   }
158   else {
159     return (int)val - 2;
160   }
161 }
162 
163 #else
get_orient3d(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3)164 static int get_orient3d(SPRCavity* SPR,
165                         unsigned v0,
166                         unsigned v1,
167                         unsigned v2,
168                         unsigned v3)
169 {
170 
171   if(v0==v1 || v0==v2 || v0==v3 || v1==v2 || v1==v3 || v2==v3)
172     return 0;
173 
174   return orient3d_inline(SPR->points.array[v0].coord,
175                          SPR->points.array[v1].coord,
176                          SPR->points.array[v2].coord,
177                          SPR->points.array[v3].coord);
178 }
179 #endif
180 
181 
182 // triangle-edge intersection following orient3ds
183 // !! the case where v0 or v1 lie on the triangle
184 // must be checked independently !!
tri_edge_intersection(int t012v0,int t012v1,int t01v01,int t12v01,int t20v01)185 static inline int tri_edge_intersection(int t012v0,
186                                         int t012v1,
187                                         int t01v01,
188                                         int t12v01,
189                                         int t20v01)
190 {
191   return t012v0*t012v1<0 &&
192          t012v0*t01v01<=0 &&
193          t012v0*t12v01<=0 &&
194          t012v0*t20v01<=0;
195 }
196 
tet_tri_intersection(SPRCavity * SPR,HXTBbox * tetBbox,uint8_t tet[4],uint8_t * tri)197 static inline int tet_tri_intersection(SPRCavity* SPR,
198                                        HXTBbox* tetBbox,
199                                        uint8_t tet[4],
200                                        uint8_t* tri)
201 {
202   HXTBbox triangleBbox;
203   hxtBboxFrom(&triangleBbox, SPR->points.array[tri[0]].coord);
204   hxtBboxAddOne(&triangleBbox, SPR->points.array[tri[1]].coord);
205   hxtBboxAddOne(&triangleBbox, SPR->points.array[tri[2]].coord);
206   hxtBboxAddOne(&triangleBbox, SPR->points.array[tri[3]].coord);
207   if(hxtBboxesIntersect(tetBbox, &triangleBbox)==0)
208     return 0;
209 
210 #ifndef NDEBUG
211   int v = get_orient3d(SPR, tet[0], tet[1], tet[2], tet[3]);
212   if(v>=0) {
213     HXT_TRACE_MSG(HXT_STATUS_ERROR, "Fatal Error: degenerated tetrahedron created %d\n",v);
214     exit(EXIT_FAILURE);
215   }
216 #endif
217 
218   int tri0_to_tet = -1;
219   int tri1_to_tet = -1;
220   int tri2_to_tet = -1;
221 
222   int tet0_to_tri = -1;
223   int tet1_to_tri = -1;
224   int tet2_to_tri = -1;
225   int tet3_to_tri = -1;
226 
227   if(tri[0]==tet[0]){
228     tri0_to_tet = 0;
229     tet0_to_tri = 0;
230   }
231   else if(tri[0]==tet[1]){
232     tri0_to_tet = 1;
233     tet1_to_tri = 0;
234   }
235   else if(tri[0]==tet[2]){
236     tri0_to_tet = 2;
237     tet2_to_tri = 0;
238   }
239   else if(tri[0]==tet[3]){
240     tri0_to_tet = 3;
241     tet3_to_tri = 0;
242   }
243 
244   if(tri[1]==tet[0]){
245     tri1_to_tet = 0;
246     tet0_to_tri = 1;
247   }
248   else if(tri[1]==tet[1]){
249     tri1_to_tet = 1;
250     tet1_to_tri = 1;
251   }
252   else if(tri[1]==tet[2]){
253     tri1_to_tet = 2;
254     tet2_to_tri = 1;
255   }
256   else if(tri[1]==tet[3]){
257     tri1_to_tet = 3;
258     tet3_to_tri = 1;
259   }
260 
261   if(tri[2]==tet[0]){
262     tri2_to_tet = 0;
263     tet0_to_tri = 2;
264   }
265   else if(tri[2]==tet[1]){
266     tri2_to_tet = 1;
267     tet1_to_tri = 2;
268   }
269   else if(tri[2]==tet[2]){
270     tri2_to_tet = 2;
271     tet2_to_tri = 2;
272   }
273   else if(tri[2]==tet[3]){
274     tri2_to_tet = 3;
275     tet3_to_tri = 2;
276   }
277 
278   if(tri0_to_tet!=-1 && tri1_to_tet!=-1 && tri2_to_tet!=-1) // the triangle is there
279     return 0;
280 
281   int f0v0 = get_orient3d(SPR, tri[0], tet[1], tet[2], tet[3]);
282   int f0v1 = get_orient3d(SPR, tri[1], tet[1], tet[2], tet[3]);
283   int f0v2 = get_orient3d(SPR, tri[2], tet[1], tet[2], tet[3]);
284 
285   // if the triangle is entirely on one side of the tet: no intersection
286   if((f0v0 > 0 || (f0v0==0 && tri0_to_tet!=-1)) &&
287      (f0v1 > 0 || (f0v1==0 && tri1_to_tet!=-1)) &&
288      (f0v2 > 0 || (f0v2==0 && tri2_to_tet!=-1)) )
289     return 0;
290 
291   int f1v0 = get_orient3d(SPR, tet[0], tri[0], tet[2], tet[3]);
292   int f1v1 = get_orient3d(SPR, tet[0], tri[1], tet[2], tet[3]);
293   int f1v2 = get_orient3d(SPR, tet[0], tri[2], tet[2], tet[3]);
294 
295   // we repeat that with the 3 other faces
296   if((f1v0 > 0 || (f1v0==0 && tri0_to_tet!=-1)) &&
297      (f1v1 > 0 || (f1v1==0 && tri1_to_tet!=-1)) &&
298      (f1v2 > 0 || (f1v2==0 && tri2_to_tet!=-1)) )
299     return 0;
300 
301   int f2v0 = get_orient3d(SPR, tet[0], tet[1], tri[0], tet[3]);
302   int f2v1 = get_orient3d(SPR, tet[0], tet[1], tri[1], tet[3]);
303   int f2v2 = get_orient3d(SPR, tet[0], tet[1], tri[2], tet[3]);
304 
305   if((f2v0 > 0 || (f2v0==0 && tri0_to_tet!=-1)) &&
306      (f2v1 > 0 || (f2v1==0 && tri1_to_tet!=-1)) &&
307      (f2v2 > 0 || (f2v2==0 && tri2_to_tet!=-1)) )
308      return 0;
309 
310   int f3v0 = get_orient3d(SPR, tet[0], tet[1], tet[2], tri[0]);
311   int f3v1 = get_orient3d(SPR, tet[0], tet[1], tet[2], tri[1]);
312   int f3v2 = get_orient3d(SPR, tet[0], tet[1], tet[2], tri[2]);
313 
314   if((f3v0 > 0 || (f3v0==0 && tri0_to_tet!=-1)) &&
315      (f3v1 > 0 || (f3v1==0 && tri1_to_tet!=-1)) &&
316      (f3v2 > 0 || (f3v2==0 && tri2_to_tet!=-1)) )
317      return 0;
318 
319   // if a point is inside but is not a point of the tet: intersection
320   if( (tri0_to_tet==-1 && f0v0<=0 && f1v0<=0 && f2v0<=0 && f3v0<=0) ||
321       (tri1_to_tet==-1 && f0v1<=0 && f1v1<=0 && f2v1<=0 && f3v1<=0) ||
322       (tri2_to_tet==-1 && f0v2<=0 && f1v2<=0 && f2v2<=0 && f3v2<=0) )
323     return 1;
324 
325   int v0f = get_orient3d(SPR, tet[0], tri[0], tri[1], tri[2]);
326   int v1f = get_orient3d(SPR, tet[1], tri[0], tri[1], tri[2]);
327   int v2f = get_orient3d(SPR, tet[2], tri[0], tri[1], tri[2]);
328   int v3f = get_orient3d(SPR, tet[3], tri[0], tri[1], tri[2]);
329 
330   // if the tet is entirely on one side of the triangle: no intersection
331   if(v0f > 0 && v1f > 0 && v2f > 0 && v3f > 0)
332     return 0;
333 
334   if(v0f < 0 && v1f < 0 && v2f < 0 && v3f < 0)
335     return 0;
336 
337   // now we want to know if an edge of the triangle pierce a facet or not
338   int e0e0, e1e0, e2e0, e3e0, e4e0, e5e0;
339   int e0e1, e1e1, e2e1, e3e1, e4e1, e5e1;
340   int e0e2, e1e2, e2e2, e3e2, e4e2, e5e2;
341 
342   e0e0 = get_orient3d(SPR, tet[0], tet[1], tri[0], tri[1]);
343   e1e0 = get_orient3d(SPR, tet[0], tet[2], tri[0], tri[1]);
344   e2e0 = get_orient3d(SPR, tet[0], tet[3], tri[0], tri[1]);
345   e3e0 = get_orient3d(SPR, tet[1], tet[2], tri[0], tri[1]);
346   e4e0 = get_orient3d(SPR, tet[1], tet[3], tri[0], tri[1]);
347   e5e0 = get_orient3d(SPR, tet[2], tet[3], tri[0], tri[1]);
348 
349   // if a point of the edge is coplanar with the facet of
350   // the tet, we don't really care trying to check the intersection
351   // between the edge and this facet. Indeed:
352   // - if the whole line is coplanar to the facet and intersects it,
353   // it will intersect another facet
354   // - if only one point is coplanar to the facet, it must be on the
355   // triangle to intersect it. That case is detected earlier anyway
356   if( tri_edge_intersection( f0v0,  f0v1, -e3e0, -e5e0,  e4e0)
357    || tri_edge_intersection( f1v0,  f1v1, -e2e0,  e5e0,  e1e0)
358    || tri_edge_intersection( f2v0,  f2v1, -e0e0, -e4e0,  e2e0)
359    || tri_edge_intersection( f3v0,  f3v1, -e1e0,  e3e0,  e0e0))
360     return 1;
361 
362 
363   e0e1 = get_orient3d(SPR, tet[0], tet[1], tri[1], tri[2]);
364   e1e1 = get_orient3d(SPR, tet[0], tet[2], tri[1], tri[2]);
365   e2e1 = get_orient3d(SPR, tet[0], tet[3], tri[1], tri[2]);
366   e3e1 = get_orient3d(SPR, tet[1], tet[2], tri[1], tri[2]);
367   e4e1 = get_orient3d(SPR, tet[1], tet[3], tri[1], tri[2]);
368   e5e1 = get_orient3d(SPR, tet[2], tet[3], tri[1], tri[2]);
369 
370   if( tri_edge_intersection( f0v1,  f0v2, -e3e1, -e5e1,  e4e1)
371    || tri_edge_intersection( f1v1,  f1v2, -e2e1,  e5e1,  e1e1)
372    || tri_edge_intersection( f2v1,  f2v2, -e0e1, -e4e1,  e2e1)
373    || tri_edge_intersection( f3v1,  f3v2, -e1e1,  e3e1,  e0e1))
374     return 1;
375 
376   e0e2 = get_orient3d(SPR, tet[0], tet[1], tri[2], tri[0]);
377   e1e2 = get_orient3d(SPR, tet[0], tet[2], tri[2], tri[0]);
378   e2e2 = get_orient3d(SPR, tet[0], tet[3], tri[2], tri[0]);
379   e3e2 = get_orient3d(SPR, tet[1], tet[2], tri[2], tri[0]);
380   e4e2 = get_orient3d(SPR, tet[1], tet[3], tri[2], tri[0]);
381   e5e2 = get_orient3d(SPR, tet[2], tet[3], tri[2], tri[0]);
382 
383   if( tri_edge_intersection( f0v2,  f0v0, -e3e2, -e5e2,  e4e2)
384    || tri_edge_intersection( f1v2,  f1v0, -e2e2,  e5e2,  e1e2)
385    || tri_edge_intersection( f2v2,  f2v0, -e0e2, -e4e2,  e2e2)
386    || tri_edge_intersection( f3v2,  f3v0, -e1e2,  e3e2,  e0e2))
387     return 1;
388 
389   // now we want to know if an edge of the tetrahedron pierce the triangle or not
390   // if v0f or v1f or v2f or v3f is 0 and an edge that is coplanar to the triangle
391   // intersects it, then another edge should also intersect. (2nd nice property of tet :p)
392   if( (tet0_to_tri==-1 && tet1_to_tri==-1 && tri_edge_intersection(v0f==0?v1f:-v0f, -v1f, e0e0, e0e1, e0e2))
393    || (tet0_to_tri==-1 && tet2_to_tri==-1 && tri_edge_intersection(v0f==0?v2f:-v0f, -v2f, e1e0, e1e1, e1e2))
394    || (tet0_to_tri==-1 && tet3_to_tri==-1 && tri_edge_intersection(v0f==0?v3f:-v0f, -v3f, e2e0, e2e1, e2e2))
395    || (tet1_to_tri==-1 && tet2_to_tri==-1 && tri_edge_intersection(v1f==0?v2f:-v1f, -v2f, e3e0, e3e1, e3e2))
396    || (tet1_to_tri==-1 && tet3_to_tri==-1 && tri_edge_intersection(v1f==0?v3f:-v1f, -v3f, e4e0, e4e1, e4e2))
397    || (tet2_to_tri==-1 && tet3_to_tri==-1 && tri_edge_intersection(v2f==0?v3f:-v2f, -v3f, e5e0, e5e1, e5e2))
398     )
399     return 1;
400 
401   return 0;
402 }
403 
404 
tet_edge_intersection(SPRCavity * SPR,HXTBbox * tetBbox,uint8_t tet[4],uint8_t * edge)405 static SPRNOINLINE int tet_edge_intersection(SPRCavity* SPR,
406                                              HXTBbox* tetBbox,
407                                              uint8_t tet[4],
408                                              uint8_t* edge)
409 {
410   double* l0 = SPR->points.array[edge[0]].coord;
411   double* l1 = SPR->points.array[edge[1]].coord;
412   if((tetBbox->max[0]<l0[0] && tetBbox->max[0]<l1[0]) ||
413      (tetBbox->max[1]<l0[1] && tetBbox->max[1]<l1[1]) ||
414      (tetBbox->max[2]<l0[2] && tetBbox->max[2]<l1[2]) ||
415      (tetBbox->min[0]>l0[0] && tetBbox->min[0]>l1[0]) ||
416      (tetBbox->min[1]>l0[1] && tetBbox->min[1]>l1[1]) ||
417      (tetBbox->min[2]>l0[2] && tetBbox->min[2]>l1[2]) )
418     return 0;
419 
420   int l0_to_tet = -1;
421   int l1_to_tet = -1;
422 
423   if(edge[0]==tet[0]){
424     l0_to_tet = 0;
425   }
426   else if(edge[0]==tet[1]){
427     l0_to_tet = 1;
428   }
429   else if(edge[0]==tet[2]){
430     l0_to_tet = 2;
431   }
432   else if(edge[0]==tet[3]){
433     l0_to_tet = 3;
434   }
435 
436   if(edge[1]==tet[0]){
437     l1_to_tet = 0;
438   }
439   else if(edge[1]==tet[1]){
440     l1_to_tet = 1;
441   }
442   else if(edge[1]==tet[2]){
443     l1_to_tet = 2;
444   }
445   else if(edge[1]==tet[3]){
446     l1_to_tet = 3;
447   }
448 
449   if(l0_to_tet!=-1 && l1_to_tet!=-1) // the line is there: no intersection
450     return 0;
451 
452   int f0v0 = get_orient3d(SPR, edge[0], tet[1], tet[2], tet[3]);
453   int f0v1 = get_orient3d(SPR, edge[1], tet[1], tet[2], tet[3]);
454 
455   // if the triangle is entirely on one side of the tet: no intersection
456   if((f0v0 > 0 || (f0v0==0 && l0_to_tet!=-1)) &&
457      (f0v1 > 0 || (f0v1==0 && l1_to_tet!=-1)) )
458     return 0;
459 
460   int f1v0 = get_orient3d(SPR, tet[0], edge[0], tet[2], tet[3]);
461   int f1v1 = get_orient3d(SPR, tet[0], edge[1], tet[2], tet[3]);
462 
463   if((f1v0 > 0 || (f1v0==0 && l0_to_tet!=-1)) &&
464      (f1v1 > 0 || (f1v1==0 && l1_to_tet!=-1)) )
465     return 0;
466 
467   int f2v0 = get_orient3d(SPR, tet[0], tet[1], edge[0], tet[3]);
468   int f2v1 = get_orient3d(SPR, tet[0], tet[1], edge[1], tet[3]);
469 
470   if((f2v0 > 0 || (f2v0==0 && l0_to_tet!=-1)) &&
471      (f2v1 > 0 || (f2v1==0 && l1_to_tet!=-1)) )
472     return 0;
473 
474   int f3v0 = get_orient3d(SPR, tet[0], tet[1], tet[2], edge[0]);
475   int f3v1 = get_orient3d(SPR, tet[0], tet[1], tet[2], edge[1]);
476 
477   if((f3v0 > 0 || (f3v0==0 && l0_to_tet!=-1)) &&
478      (f3v1 > 0 || (f3v1==0 && l1_to_tet!=-1)) )
479     return 0;
480 
481   // if a point is inside but is not a point of the tet: intersection
482   if( (l0_to_tet==-1 && f0v0<=0 && f1v0<=0 && f2v0<=0 && f3v0<=0) ||
483       (l1_to_tet==-1 && f0v1<=0 && f1v1<=0 && f2v1<=0 && f3v1<=0) )
484     return 1;
485 
486   // now, we know that a point in the plane of a triangle is never
487   // in the triangle, so we can conclude a little bit more
488   if((f0v0 == 0 && f0v1 > 0) || (f0v1 == 0 && f0v0 > 0) ||
489      (f1v0 == 0 && f1v1 > 0) || (f1v1 == 0 && f1v0 > 0) ||
490      (f2v0 == 0 && f2v1 > 0) || (f2v1 == 0 && f2v0 > 0) ||
491      (f3v0 == 0 && f3v1 > 0) || (f3v1 == 0 && f3v0 > 0))
492     return 0;
493 
494   // the line with each edge of the tetrahedra
495   int c0 = get_orient3d(SPR, tet[0], tet[1], edge[0], edge[1]);
496   int c1 = get_orient3d(SPR, tet[0], tet[2], edge[0], edge[1]);
497   int c2 = get_orient3d(SPR, tet[0], tet[3], edge[0], edge[1]);
498   int c3 = get_orient3d(SPR, tet[1], tet[2], edge[0], edge[1]);
499   int c4 = get_orient3d(SPR, tet[1], tet[3], edge[0], edge[1]);
500   int c5 = get_orient3d(SPR, tet[2], tet[3], edge[0], edge[1]);
501 
502   // if one point of the edge is coplanar with the facet of
503   // the tet, we don't really care trying to check the intersection
504   // between the edge and this facet.
505   // If the point is on the facet, it would have been detected earlier
506   // If the line is coplanar to the facet and intersects it,
507   // it will intersect another facet (1st nice property of tets :p)
508   if( tri_edge_intersection( f0v0,  f0v1,-c3, -c5,  c4)
509    || tri_edge_intersection( f1v0,  f1v1,-c2,  c5,  c1)
510    || tri_edge_intersection( f2v0,  f2v1,-c0, -c4,  c2)
511    || tri_edge_intersection( f3v0,  f3v1,-c1,  c3,  c0) )
512     return 1;
513 
514   return 0;
515 }
516 
517 
tet_contains_pt(SPRCavity * SPR,HXTBbox * tetBbox,uint8_t tet[4],unsigned pt)518 static int tet_contains_pt(SPRCavity* SPR,
519                            HXTBbox* tetBbox,
520                            uint8_t tet[4],
521                            unsigned pt) {
522   double* pt_coord = SPR->points.array[pt].coord;
523 
524   if(pt==tet[0] || pt==tet[1] || pt==tet[2] || pt==tet[3] ||
525      pt_coord[0] < tetBbox->min[0] ||
526      pt_coord[1] < tetBbox->min[1] ||
527      pt_coord[2] < tetBbox->min[2] ||
528      pt_coord[0] > tetBbox->max[0] ||
529      pt_coord[1] > tetBbox->max[1] ||
530      pt_coord[2] > tetBbox->max[2] ||
531      get_orient3d(SPR, tet[1], tet[3], tet[2], pt)>0 ||
532      get_orient3d(SPR, tet[2], tet[3], tet[0], pt)>0 ||
533      get_orient3d(SPR, tet[3], tet[1], tet[0], pt)>0 ||
534      get_orient3d(SPR, tet[0], tet[1], tet[2], pt)>0)
535     return 0;
536 
537   return 1;
538 }
539 
add_quality_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3,unsigned index)540 static SPRNOINLINE double add_quality_map(SPRCavity* SPR,
541                                          unsigned v0,
542                                          unsigned v1,
543                                          unsigned v2,
544                                          unsigned v3,
545                                          unsigned index)
546 {
547   double* c0 = SPR->points.array[v0].coord;
548   double* c1 = SPR->points.array[v1].coord;
549   double* c2 = SPR->points.array[v2].coord;
550   double* c3 = SPR->points.array[v3].coord;
551   double qual = SPR->quality.function(c0, c1, c2, c3,
552                                  SPR->quality.userData);
553 
554   if(qual<0.0) {
555     HXT_WARNING("negative quality with correct orientation\n");
556     qual = DBL_MIN;
557   }
558 
559   SPR->map.qualities[index] = qual;
560 
561   return qual;
562 }
563 
564 
get_quality_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3,unsigned * index_ptr)565 static double get_quality_map(SPRCavity* SPR,
566                               unsigned v0,
567                               unsigned v1,
568                               unsigned v2,
569                               unsigned v3,
570                               unsigned* index_ptr)
571 {
572   int orientation = get_orient3d(SPR, v0, v1, v2, v3);
573   if(orientation >= 0.0)
574     return -DBL_MAX;
575 
576   unsigned index = get_compressed_index(v0, v1, v2, v3);
577   if(index_ptr!=NULL)
578     *index_ptr = index;
579 
580   double qual = SPR->map.qualities[index];
581 
582   if(!isnan(qual)) {
583     return qual;
584   }
585   else if(SPR->quality.function==NULL) {
586       return DBL_MAX;
587   }
588   else {
589     return add_quality_map(SPR, v0, v1, v2, v3,
590                            index);
591   }
592 }
593 
594 
595 /* Compute the worst face (the face that we should begin with)
596  * and the minimum value among all maximum quality we can
597  * get from a face. This value is an estimation for the quality
598  * that the best tetrahedralization could have */
best_face_heuristic(SPRCavity * SPR,double * qualMax)599 static inline uint16_t best_face_heuristic(SPRCavity* SPR, double* qualMax)
600 {
601   const uint16_t nfaces = SPR->bndTriangles.num;
602 
603   const uint8_t npts = SPR->points.num;
604   int minValid = 255;
605   uint16_t worstFaceID = UINT16_MAX;
606   double minimax = DBL_MAX;
607 
608   // new faces have a better chances of being bad, that why we decrement
609   for (uint16_t i=nfaces; i>0; i--) {
610     uint16_t triangleID = i-1;
611     SPRTet tet = SPR->bndTriangles.array[triangleID];
612 
613     int numValid = 0;
614     double max = 0.0;
615 
616     for (tet.node[3]=0; tet.node[3] < npts && numValid < minValid; tet.node[3]++)
617     {
618       if(tet.node[3] == tet.node[0] ||
619          tet.node[3] == tet.node[1] ||
620          tet.node[3] == tet.node[2] ||
621          SPR->points.array[tet.node[3]].valence==0)
622         continue;
623 
624       double qual = get_quality_map(SPR,
625                                     tet.node[0],
626                                     tet.node[1],
627                                     tet.node[2],
628                                     tet.node[3], NULL);
629       if(qual<=SPR->tetrahedra.quality)
630         continue;
631 
632       if(qual>max)
633         max = qual;
634 
635       numValid++;
636     }
637 
638     if(numValid == 0)
639       return UINT16_MAX;
640 
641     if(max < minimax)
642       minimax = max;
643 
644     if(numValid < minValid) {
645       minValid = numValid;
646       worstFaceID = triangleID;
647     }
648   }
649 
650   *qualMax = minimax;
651   return worstFaceID;
652 }
653 
654 
compute_candidates(SPRCavity * SPR,SPRStep * step,double qualMax)655 static inline void compute_candidates(SPRCavity* SPR,
656                                       SPRStep* step,
657                                       double qualMax)
658 {
659   const int nPts = SPR->points.num;
660 
661   SPRTet* tet = &step->tet;
662   uint8_t num = 0;
663 
664   uint8_t score[SPR_MAX_PTS];
665   uint8_t candidate[SPR_MAX_PTS];
666 
667   for (tet->node[3]=0; tet->node[3]<nPts; tet->node[3]++)
668   {
669     if(tet->node[3] == tet->node[0] ||
670        tet->node[3] == tet->node[1] ||
671        tet->node[3] == tet->node[2] ||
672        SPR->points.array[tet->node[3]].valence==0)
673       continue;
674 
675     double qual = get_quality_map(SPR, tet->node[0],
676                                        tet->node[1],
677                                        tet->node[2],
678                                        tet->node[3], NULL);
679     if(qual<=SPR->tetrahedra.quality)
680       continue;
681 
682     // those heuristics can be changed in many way
683     // if you have some theory validating some choice,
684     // you can give a higher/lower score depending on
685     //  - valence of points
686     //  - ...
687     uint8_t curScore = 0;
688 
689     // big bonus if the point is an interior point
690     if(SPR->points.array[tet->node[3]].is_interior)
691       curScore += 4;
692 
693     // bonus if you don't degrade partial cavity
694     if(qual > step->quality)
695       curScore+=2;
696     else if(qual == step->quality)
697       curScore+=1;
698 
699     // bonus if you don't degrade the best quality we can hope for...
700     if(qual > qualMax)
701       curScore+=2;
702     else if(qual == qualMax)
703       curScore+=1;
704 
705     // bonus if the point is in an adjacent triangle
706     for (int f=0; f<3; f++) {
707       uint8_t p0 = tet->node[nodeFromFacet[0][f]];
708       uint8_t p1 = tet->node[nodeFromFacet[1][f]];
709       uint8_t p2 = tet->node[nodeFromFacet[2][f]];
710 
711       if(get_face_map(SPR, p0, p1, p2)!=UINT16_MAX)
712         curScore+=2;
713     }
714 
715     // the maximum score is 4+2+2+3*2 = 14
716 
717     candidate[num] = tet->node[3];
718     score[num] = curScore;
719     num++;
720   }
721 
722   // simple radix sort pass
723   uint8_t count[16] = {0};
724   for (uint8_t i=0; i<num; i++) {
725     count[score[i]]++;
726   }
727 
728   uint8_t sum = 0;
729   for (int i=0; i<16; i++) {
730     uint8_t tsum = sum + count[i];
731     count[i] = sum;
732     sum = tsum;
733   }
734 
735   HXT_ASSERT(sum==num);
736 
737   for (uint8_t i=0; i<num; i++) {
738     step->candidate[count[score[i]]++] = candidate[i];
739   }
740 
741   step->numCandidates=num;
742 }
743 
744 
745 
add_face(SPRCavity * SPR,uint8_t v0,uint8_t v1,uint8_t v2)746 static void add_face(SPRCavity* SPR,
747                      uint8_t v0,
748                      uint8_t v1,
749                      uint8_t v2)
750 {
751   SPR->bndTriangles.array[SPR->bndTriangles.num].node[0] = v0;
752   SPR->bndTriangles.array[SPR->bndTriangles.num].node[1] = v1;
753   SPR->bndTriangles.array[SPR->bndTriangles.num].node[2] = v2;
754   SPR->bndTriangles.array[SPR->bndTriangles.num].node[3] = v0;
755   add_face_map(SPR, v0, v1, v2, SPR->bndTriangles.num);
756   increment_3valences(SPR, v0, v1, v2);
757   SPR->bndTriangles.num++;
758 }
759 
remove_face(SPRCavity * SPR,uint16_t triangleID)760 static void remove_face(SPRCavity* SPR, uint16_t triangleID)
761 {
762   SPRTriangle* del = &SPR->bndTriangles.array[triangleID];
763   remove_face_map(SPR, del->node[0], del->node[1], del->node[2]);
764   decrement_3valences(SPR, del->node[0], del->node[1], del->node[2]);
765 
766   if(triangleID!=SPR->bndTriangles.num-1) {
767     SPRTriangle* last = &SPR->bndTriangles.array[SPR->bndTriangles.num-1];
768     add_face_map(SPR, last->node[0], last->node[1], last->node[2], triangleID);
769     *del = *last;
770   }
771 
772   SPR->bndTriangles.num--;
773 }
774 
new_tetrahedralization(SPRCavity * SPR,SPRStep * step)775 static inline void new_tetrahedralization(SPRCavity* SPR, SPRStep* step)
776 {
777 
778   HXT_ASSERT(step->numCandidates<0);
779   HXT_ASSERT(step->quality > SPR->tetrahedra.quality);
780 #ifndef NDEBUG
781   for (int i=0; i<SPR->points.num; i++) {
782     if(SPR->points.array[i].is_interior && SPR->points.array[i].valence!=1){
783       HXT_TRACE_MSG(HXT_STATUS_ERROR, "DEBUG: This should never happen... valence!=1 for interior point %u\n", i);
784       exit(EXIT_FAILURE);
785     }
786     else if(!SPR->points.array[i].is_interior && SPR->points.array[i].valence!=0) {
787       HXT_TRACE_MSG(HXT_STATUS_ERROR, "DEBUG: This should never happen... valence!=0 for boundary point %u\n", i);
788       exit(EXIT_FAILURE);
789     }
790   }
791 #endif
792 
793   SPR->tetrahedra.quality = step->quality;
794 
795   for (int i=0; i<SPR->steps.num; i++) {
796     SPR->tetrahedra.array[i] = SPR->steps.array[i].tet;
797   }
798   SPR->tetrahedra.num = SPR->steps.num;
799 }
800 
801 
802 /***************************
803  * Initialization
804  ***************************/
SPR_clear_maps(SPRCavity * SPR)805 static void SPR_clear_maps(SPRCavity* SPR)
806 {
807   { // initializing map.face
808     size_t face_len = sizeof(SPR->map.faces)/sizeof(uint16_t);
809 
810     // compiler should replace this by a memset (UINT16_MAX is all ones)
811     for (size_t i=0; i<face_len; i++) {
812       SPR->map.faces[i] = UINT16_MAX;
813     }
814   }
815 
816   { // initializing map.qualities
817     size_t qual_len = sizeof(SPR->map.qualities)/sizeof(double);
818 
819     // compiler should also replace this by a memset (NAN is all ones)
820     for (size_t i=0; i<qual_len; i++) {
821       SPR->map.qualities[i] = NAN;
822     }
823   }
824 
825 #ifdef SPR_SAVE_ORIENT3D
826   { // allocating & setting to zero map.orient3d
827     size_t ori_len = sizeof(SPR->map.orient3d)/sizeof(uint32_t);
828 
829     // compiler should also replace this by a memset
830     for (size_t i=0; i<ori_len; i++) {
831       SPR->map.orient3d[i] = 0;
832     }
833   }
834 #endif
835 }
836 
837 
SPR_detect_interior_points(SPRCavity * SPR)838 static void SPR_detect_interior_points(SPRCavity* SPR)
839 {
840   for (int i=0; i<SPR->points.num; i++) {
841     if(SPR->points.array[i].valence==0) {
842       SPR->points.array[i].is_interior = 1;
843 
844       /* interior points should have a positive valence */
845       SPR->points.array[i].valence = 1;
846     }
847   }
848 }
849 
SPR_init_faceMap_and_valences(SPRCavity * SPR)850 static void SPR_init_faceMap_and_valences(SPRCavity* SPR)
851 {
852   for (int i=0; i<SPR->points.num; i++) {
853     SPRPoint* p = &SPR->points.array[i];
854     p->valence = 0;
855     p->is_interior = 0;
856   }
857 
858   for (uint16_t i=0; i<SPR->bndTriangles.num; i++) {
859     uint8_t* v = SPR->bndTriangles.array[i].node;
860     v[3] = v[0];
861     increment_3valences(SPR, v[0], v[1], v[2]);
862     add_face_map(SPR, v[0], v[1], v[2], i);
863   }
864 }
865 
866 
SPR_init_step(SPRCavity * SPR)867 static void SPR_init_step(SPRCavity* SPR)
868 {
869   SPR->steps.num = 0;
870   SPRStep* step = &SPR->steps.array[0];
871   step->numCandidates = -1; // candidates not computed yet
872   step->quality = DBL_MAX;  // we can hope for the biggest quality at the moment
873 }
874 
875 
876 /*|=======================================================|*\
877 |*|    _____                                              |*|
878 |*|   / ____|                                             |*|
879 |*|  | (___              _ _                              |*|
880 |*|   \___ \  _ __  __ _| | |                             |*|
881 |*|   ____) || '  \/ _` | | |                             |*|
882 |*|  |_____/ |_|_|_\__,_|_|_|                             |*|
883 |*|   _____                                               |*|
884 |*|  |  __ \                                              |*|
885 |*|  | |__) |   _      _           _                      |*|
886 |*|  |  ___/___| |_  _| |_  ___ __| |_ _ ___ _ _          |*|
887 |*|  | |   / _ \ | || | ' \/ -_) _` | '_/ _ \ ' \         |*|
888 |*|  |_|   \___/_|\_, |_||_\___\__,_|_| \___/_||_|        |*|
889 |*|   _____       |__/                                    |*|
890 |*|  |  __ \                                              |*|
891 |*|  | |__) |                             _   _           |*|
892 |*|  |  _  /  ___ __ ___ _ _  _ _  ___ __| |_(_)___ _ _   |*|
893 |*|  | | \ \ / -_) _/ _ \ ' \| ' \/ -_) _|  _| / _ \ ' \  |*|
894 |*|  |_|  \_\\___\__\___/_||_|_||_\___\__|\__|_\___/_||_| |*|
895 \*|=======================================================|*/
hxtSPR_advanced(SPRCavity * SPR)896 static HXTStatus hxtSPR_advanced(SPRCavity* SPR) {
897 
898   // this variable could be avoided, as step is
899   // SPR->steps.array[SPR->steps.num] all along
900   SPRStep* step = &SPR->steps.array[SPR->steps.num];
901 
902   while(1) {
903     if(SPR->num_search_nodes >= SPR->max_search_nodes){
904       return HXT_STATUS_INTERNAL;
905     }
906 
907     int rewind = 0;
908     if(SPR->bndTriangles.num==0) { // we found a new (better) tetrahedralization
909       new_tetrahedralization(SPR, step);
910       rewind = 1;
911     }
912     else if(step->numCandidates<0){
913 
914       // choosing the starting triangle
915       double qualMax;
916       const uint16_t triangleID = best_face_heuristic(SPR, &qualMax);
917 
918       if(triangleID == UINT16_MAX) {
919         step->numCandidates = -1;
920         rewind = 1;
921       }
922       else {
923         step->tet = SPR->bndTriangles.array[triangleID];
924 
925         // create (and order) the candidate array
926         compute_candidates(SPR, step, qualMax);
927 
928         // remove the face from the map
929         remove_face(SPR, triangleID);
930       }
931     }
932 
933     if(step->numCandidates>0) {
934       step->tet.node[3] = step->candidate[--step->numCandidates];
935 
936       unsigned index;
937       double qual = get_quality_map(SPR,
938                                     step->tet.node[0],
939                                     step->tet.node[1],
940                                     step->tet.node[2],
941                                     step->tet.node[3], &index);
942 
943       // best quality could have changed if there was a rewind
944       if(qual <= SPR->tetrahedra.quality || qual == -DBL_MAX)
945         continue;
946 
947       int stop = 0;
948       HXTBbox tetBbox;
949       hxtBboxFrom(&tetBbox, SPR->points.array[step->tet.node[0]].coord);
950       hxtBboxAddOne(&tetBbox, SPR->points.array[step->tet.node[1]].coord);
951       hxtBboxAddOne(&tetBbox, SPR->points.array[step->tet.node[2]].coord);
952       hxtBboxAddOne(&tetBbox, SPR->points.array[step->tet.node[3]].coord);
953 
954       for (int ptID=0; ptID<SPR->points.num; ptID++) {
955         if(tet_contains_pt(SPR, &tetBbox, step->tet.node, ptID)) {
956           stop = 1;
957           SPR->map.qualities[index] = -DBL_MAX;
958           break;
959         }
960       }
961 
962       if(stop)
963         continue;
964 
965       for (int edgeID=0; edgeID<SPR->CIEdges.num; edgeID++) {
966         uint8_t* edge = SPR->CIEdges.array[edgeID].node;
967         if(tet_edge_intersection(SPR, &tetBbox, step->tet.node, edge)) {
968           stop = 1;
969           SPR->map.qualities[index] = -DBL_MAX;
970           break;
971         }
972       }
973 
974       if(stop)
975         continue;
976 
977       for (int triangleID=0; triangleID<SPR->CITriangles.num; triangleID++) {
978         uint8_t* tri = SPR->CITriangles.array[triangleID].node;
979         if(tet_tri_intersection(SPR, &tetBbox, step->tet.node, tri)) {
980           stop = 1;
981           SPR->map.qualities[index] = -DBL_MAX;
982           break;
983         }
984       }
985 
986       if(stop)
987         continue;
988 
989       // test intersection with boundary triangles
990       for (int otherFaceID=0; otherFaceID<SPR->bndTriangles.num; otherFaceID++) {
991         uint8_t* tri = SPR->bndTriangles.array[otherFaceID].node;
992         if(tet_tri_intersection(SPR, &tetBbox, step->tet.node, tri)){
993           stop = 1;
994           break;
995         }
996       }
997 
998       if(stop)
999         continue;
1000 
1001       SPR->num_search_nodes++;
1002 
1003       for (int f=0; f<3; f++) {
1004         uint8_t p0 = step->tet.node[nodeFromFacet[0][f]];
1005         uint8_t p1 = step->tet.node[nodeFromFacet[1][f]];
1006         uint8_t p2 = step->tet.node[nodeFromFacet[2][f]];
1007 
1008         uint16_t index = get_face_map(SPR, p0, p1, p2);
1009 
1010         if(index == UINT16_MAX) {
1011           // add the face to the map
1012           add_face(SPR, p0, p2, p1);
1013         }
1014         else {
1015           // remove the face from the map
1016           remove_face(SPR, index);
1017         }
1018       }
1019 
1020       double oldQual = step->quality;
1021       step = &SPR->steps.array[++SPR->steps.num];
1022       step->quality = qual < oldQual ? qual : oldQual;
1023       step->numCandidates = -1;
1024     }
1025     else if(step->numCandidates==0){
1026       add_face(SPR,
1027                step->tet.node[0],
1028                step->tet.node[1],
1029                step->tet.node[2]);
1030       rewind = 1;
1031     }
1032 
1033     if(rewind) {
1034       if(SPR->steps.num==0) {
1035         return HXT_STATUS_OK;
1036       }
1037 
1038       step = &SPR->steps.array[--SPR->steps.num];
1039       for (int f=0; f<3; f++) {
1040         uint8_t p0 = step->tet.node[nodeFromFacet[0][f]];
1041         uint8_t p1 = step->tet.node[nodeFromFacet[1][f]];
1042         uint8_t p2 = step->tet.node[nodeFromFacet[2][f]];
1043 
1044         uint16_t index = get_face_map(SPR, p0, p2, p1);
1045 
1046         if(index == UINT16_MAX) {
1047           // add the face to the map
1048           add_face(SPR, p0, p1, p2);
1049         }
1050         else {
1051           // remove the face from the map
1052           remove_face(SPR, index);
1053         }
1054       }
1055 
1056       if(step->quality <= SPR->tetrahedra.quality) {
1057         step->numCandidates = 0;
1058       }
1059     }
1060   }
1061 }
1062 
1063 
hxtSPR(SPRCavity * SPR)1064 HXTStatus hxtSPR(SPRCavity* SPR)
1065 {
1066   SPR_clear_maps(SPR);
1067   SPR_init_faceMap_and_valences(SPR);
1068   SPR_detect_interior_points(SPR);
1069   SPR_init_step(SPR);
1070 
1071   HXT_CHECK( hxtSPR_advanced(SPR) );
1072 
1073   return HXT_STATUS_OK;
1074 }
1075 
1076 
1077 /* if hxtSPR or hxtSPR_advanced stopped abruptly
1078  * because it reached max_search_nodes, we can rewind
1079  * the cavity to its initial state thanks to this function
1080  */
hxtSPR_rewind(SPRCavity * SPR)1081 static void hxtSPR_rewind(SPRCavity* SPR) {
1082   SPRStep* step = &SPR->steps.array[SPR->steps.num];
1083 
1084   while(1) {
1085     if(step->numCandidates>=0){
1086       add_face(SPR,
1087                step->tet.node[0],
1088                step->tet.node[1],
1089                step->tet.node[2]);
1090     }
1091 
1092     if(SPR->steps.num==0) {
1093       return;
1094     }
1095 
1096     step = &SPR->steps.array[--SPR->steps.num];
1097     for (int f=0; f<3; f++) {
1098       uint8_t p0 = step->tet.node[nodeFromFacet[0][f]];
1099       uint8_t p1 = step->tet.node[nodeFromFacet[1][f]];
1100       uint8_t p2 = step->tet.node[nodeFromFacet[2][f]];
1101 
1102       uint16_t index = get_face_map(SPR, p0, p2, p1);
1103 
1104       if(index == UINT16_MAX) {
1105         // add the face to the map
1106         add_face(SPR, p0, p1, p2);
1107       }
1108       else {
1109         // remove the face from the map
1110         remove_face(SPR, index);
1111       }
1112     }
1113   }
1114 }
1115 
1116 
1117 
1118 /*****************************************************************
1119 
1120 
1121 
1122 
1123  The following part uses SPR in the context of mesh optimization
1124 
1125 
1126 
1127 
1128 ******************************************************************/
1129 typedef struct {
1130   SPRCavity cavity;
1131 
1132   // neighbor (mesh indices) of boundary triangles, given in original order
1133   uint64_t meshAdjacencies[SPR_MAX_BNDTRIANGLES];
1134 
1135   double worstQuality;
1136 } SPRGrowingCavity;
1137 
1138 
add_knownPositive_quality_map(SPRCavity * SPR,unsigned v0,unsigned v1,unsigned v2,unsigned v3,double qual)1139 static void add_knownPositive_quality_map(SPRCavity* SPR,
1140                                           unsigned v0,
1141                                           unsigned v1,
1142                                           unsigned v2,
1143                                           unsigned v3,
1144                                           double qual)
1145 {
1146 #ifndef NDEBUG
1147   int orientation = get_orient3d(SPR, v0, v1, v2, v3);
1148   if(orientation >= 0.0){
1149     HXT_WARNING("a tetrahedron has a negative volume but a quality of %f", qual);
1150   }
1151   if(qual<0.0) {
1152     HXT_WARNING("WARNING: negative quality (maybe even negative orientation ?) in mesh");
1153     qual = DBL_MIN;
1154   }
1155 #endif
1156 
1157   unsigned index = get_compressed_index(v0, v1, v2, v3);
1158   SPR->map.qualities[index] = qual;
1159 }
1160 
1161 
tetOutOfPartition(HXTVertex * vertices,HXTPartition * partition,uint32_t * nodes)1162 static int tetOutOfPartition(HXTVertex* vertices,
1163                              HXTPartition* partition,
1164                              uint32_t* nodes)
1165 {
1166   if(vertexOutOfPartition(vertices, nodes[0], partition->lengthDist, partition->startDist) +
1167      vertexOutOfPartition(vertices, nodes[1], partition->lengthDist, partition->startDist) +
1168      vertexOutOfPartition(vertices, nodes[2], partition->lengthDist, partition->startDist) +
1169      vertexOutOfPartition(vertices, nodes[3], partition->lengthDist, partition->startDist) > 1)
1170     return 1;
1171   return 0;
1172 }
1173 
1174 
1175 /* initialize growingCav->cavity (SPR) with a cavity composed of badTet only
1176  *            growingCav->worstQuality is the quality of that tet
1177  *            growingCav->meshAdjacencies[] are the adjacencies of that tet */
SPROpti_init(SPRGrowingCavity * growingCav,ThreadLocal * local,uint64_t badTet)1178 static inline HXTStatus SPROpti_init(SPRGrowingCavity* growingCav,
1179                                      ThreadLocal* local,
1180                                      uint64_t badTet)
1181 {
1182   SPRCavity* SPR = &growingCav->cavity;
1183   growingCav->worstQuality = local->quality->values[badTet];
1184 
1185   // initialize the SPR structure maps and arrays
1186   SPR_clear_maps(SPR);
1187   // SPR_init_step(SPR); % already done later
1188   SPR->points.num = 4;    // we add those 4 points in the for loop hereafter
1189   SPR->CIEdges.num = 0;     // only constrained edge entirely interior
1190   SPR->CITriangles.num = 0; // add_face() (in second for loop) already increments num
1191   SPR->bndTriangles.num = 0;
1192   SPR->tetrahedra.num = 1;
1193   SPR->tetrahedra.array[0] = (SPRTet) {{0,1,2,3}};
1194   SPR->tetrahedra.quality = local->quality->values[badTet];
1195   SPR->quality.function = local->quality->function;
1196   SPR->quality.userData = local->quality->userData;
1197   SPR->num_search_nodes = 0;
1198   SPR->max_search_nodes = local->SPR.maxSearchNodes;
1199 
1200   HXTMesh* mesh = local->toSync->mesh;
1201   HXTPartition* partition = &local->partition;
1202   HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
1203 
1204   // maybe this is unnecessary... better check anyway
1205   if(tetOutOfPartition(vertices, partition, &mesh->tetrahedra.node[4*badTet]))
1206     return HXT_STATUS_CONFLICT;
1207 
1208   HXTDeleted* deleted = &local->deleted;
1209   HXT_CHECK( askForDeleted(deleted, 1) );
1210   setDeletedFlag(mesh, badTet);
1211   deleted->array[deleted->num++] = badTet;
1212 
1213   // adding the points of the bad tet
1214   for(int i=0; i<4; i++) {
1215     uint32_t node = mesh->tetrahedra.node[4*badTet + i];
1216     double* coord = vertices[node].coord;
1217 
1218     SPR->points.array[i] = (SPRPoint) {
1219       .coord = {coord[0], coord[1], coord[2]},
1220       .userID = node,
1221       .valence = 0, // add_face already increments the valences
1222       .is_interior = 0,
1223       .is_optional = 0,
1224     };
1225   }
1226 
1227   add_knownPositive_quality_map(SPR, 0, 1, 2, 3, local->quality->values[badTet]);
1228 
1229   // adding faces and linked stuffs
1230   for(int i=0; i<4; i++) {
1231     // add the faces of the tet
1232     unsigned v0 = getNode0FromFacet(i);
1233     unsigned v1 = getNode1FromFacet(i);
1234     unsigned v2 = getNode2FromFacet(i);
1235 
1236     add_face(SPR, v0, v1, v2);
1237     growingCav->meshAdjacencies[i] = mesh->tetrahedra.neigh[4*badTet + i];
1238   }
1239 
1240   return HXT_STATUS_OK;
1241 }
1242 
1243 
1244 
find_best_point(SPRGrowingCavity * growingCav,ThreadLocal * local,int * bestPoint)1245 static inline HXTStatus find_best_point(SPRGrowingCavity* growingCav,
1246                                         ThreadLocal* local,
1247                                         int* bestPoint) {
1248   /* A point is good if it is very connected to the cavity !
1249    * (connection <==> a tet containing the point having a face on the cavity)
1250    * If multiple points have the same number of connections the quality of
1251    * tets that makes the connections are taken into account (highest sum win)
1252    */
1253 
1254   /* For every face of the cavity, we take the opposite point on the
1255    * external side.
1256    * We add each point, if it is not already present, to the
1257    * opposite_point[] array, If it is present, we increment numConnected.
1258    */
1259   SPRCavity* SPR = &growingCav->cavity;
1260   HXTMesh* mesh = local->toSync->mesh;
1261   uint64_t* adjacencies = growingCav->meshAdjacencies;
1262 
1263   uint32_t opposite_point[SPR_MAX_BNDTRIANGLES];
1264   uint16_t numConnected[SPR_MAX_BNDTRIANGLES];
1265   int numPoints = 0;
1266 
1267   // for each point outside of the cavity
1268   for(int i=0; i<SPR->bndTriangles.num; i++) {
1269     unsigned facet = adjacencies[i]%4;
1270     uint64_t tet = adjacencies[i]/4;
1271 
1272     if(adjacencies[i]==HXT_NO_ADJACENT || getFacetConstraint(mesh, tet, facet))
1273       continue;
1274 
1275 #ifdef DEBUG
1276     if(getDeletedFlag(mesh, tet))
1277       return HXT_ERROR_MSG(HXT_STATUS_ERROR, "DEBUG: adjacent tet already deleted");
1278 #endif
1279 
1280     uint32_t node = mesh->tetrahedra.node[adjacencies[i]];
1281     int p;
1282 
1283     // just make a stupid linear search
1284     for(p=0; p<numPoints; p++) {
1285       if(opposite_point[p]==node) {
1286         numConnected[p]++;
1287         break;
1288       }
1289     }
1290 
1291     if(p==numPoints) {
1292       opposite_point[p] = node;
1293       numConnected[p] = 1;
1294       numPoints++;
1295     }
1296   }
1297 
1298   /* we determine if there is equality
1299    * if not, we determine the most connect point */
1300   uint32_t mostConnectedPoint;
1301   uint16_t mostNumConnected = 0;
1302   int equality = 0;
1303   for(int i=0; i<numPoints; i++) {
1304     if(numConnected[i]>mostNumConnected) {
1305       mostNumConnected = numConnected[i];
1306       mostConnectedPoint = opposite_point[i];
1307       equality = 0;
1308     }
1309     else if(numConnected[i]==mostNumConnected) {
1310       equality = 1;
1311     }
1312   }
1313 
1314   if(mostNumConnected==0) // cavity is completely constrained
1315     return HXT_STATUS_INTERNAL;
1316 
1317   /* if there is equality, we must compute the sum of qualities
1318    * for each point with mostNumConnected connection   */
1319   if(equality) {
1320     double bestScore = -DBL_MAX;
1321 
1322     for(int i=0; i<numPoints; i++) {
1323 
1324       if(numConnected[i]==mostNumConnected) {
1325         uint32_t node = opposite_point[i];
1326         double score = 0.0;
1327 
1328         for(int t=0; t<SPR->bndTriangles.num; t++) {
1329           if(adjacencies[t]==HXT_NO_ADJACENT || mesh->tetrahedra.node[adjacencies[t]]!=node || getFacetConstraint(mesh, adjacencies[t]/4, adjacencies[t]%4))
1330             continue;
1331           score -= local->quality->values[adjacencies[t]/4];
1332         }
1333 
1334         if(score > bestScore) {
1335           bestScore = score;
1336           mostConnectedPoint = node;
1337         }
1338       }
1339     }
1340   }
1341 
1342   *bestPoint = mostConnectedPoint;
1343 
1344   return HXT_STATUS_OK;
1345 }
1346 
1347 
1348 /* determine if an edge needs to be added to SPR->edges.
1349  * <=> if the edge is constrained and is completely contained within the cavity */
needNewSPREdge(HXTMesh * mesh,uint64_t meshTet,int in_facet,int out_facet)1350 static inline int needNewSPREdge(HXTMesh* mesh, uint64_t meshTet, int in_facet, int out_facet)
1351 {
1352   int edgeNum = getEdgeFromFacets(in_facet, out_facet);
1353 
1354   if(!getEdgeConstraint(mesh, meshTet, edgeNum))
1355     return -1;
1356 
1357   // we will turn around the edge and see if we stay in the cavity
1358   // if a tet is not deleted, it's not in the cavity so we return -1
1359   uint64_t firstTet=meshTet;
1360 
1361   do
1362   {
1363     if(getFacetConstraint(mesh, meshTet, out_facet))
1364       return -1;
1365 
1366     uint32_t oppositeNode = mesh->tetrahedra.node[4*meshTet + in_facet];
1367 
1368     // go into the neighbor through out_facet
1369     uint64_t neigh = mesh->tetrahedra.neigh[4*meshTet + out_facet];
1370     if(neigh==HXT_NO_ADJACENT ||
1371        getDeletedFlag(mesh, neigh/4)==0) {
1372       return -1;
1373     }
1374 
1375     meshTet = neigh/4;
1376     in_facet = neigh%4;
1377     uint32_t* nodes = mesh->tetrahedra.node + 4*meshTet;
1378     for (out_facet=0; out_facet<3; out_facet++)
1379       if(nodes[out_facet]==oppositeNode)
1380         break;
1381 
1382   } while (meshTet!=firstTet);
1383 
1384   return edgeNum;
1385 }
1386 
1387 
1388 
1389 /* attach the best point (see find_best_point()) to the cavity */
attach_best_point(SPRGrowingCavity * growingCav,ThreadLocal * local)1390 static inline HXTStatus attach_best_point(SPRGrowingCavity* growingCav,
1391                                           ThreadLocal* local)
1392 {
1393   SPRCavity* SPR = &growingCav->cavity;
1394   HXTMesh* mesh = local->toSync->mesh;
1395 
1396   int newNode;
1397   HXT_CHECK( find_best_point(growingCav, local, &newNode) );
1398 
1399   // we can now add mostConnectPoint !! :-)
1400   double* coord = &mesh->vertices.coord[4*newNode];
1401   uint8_t newV = SPR->points.num++;
1402   SPR->points.array[newV] = (SPRPoint) {
1403     .coord = {coord[0], coord[1], coord[2]},
1404     .userID = newNode,
1405     .valence = 0, // add_face already increments the valences
1406     .is_interior = 0,
1407     .is_optional = 0,
1408   };
1409 
1410   uint64_t* adjacencies = growingCav->meshAdjacencies;
1411 
1412   /* we delete tetrahedra that contain the added point. */
1413   HXTPartition* partition = &local->partition;
1414   HXTVertex* vertices = (HXTVertex*) mesh->vertices.coord;
1415 
1416   HXTDeleted* deleted = &local->deleted;
1417 
1418   for(int i=0; i<SPR->bndTriangles.num;) { // notice we don't increment i here
1419     uint64_t meshTet = adjacencies[i]/4;
1420     unsigned facet = adjacencies[i]%4;
1421 
1422     if(adjacencies[i]==HXT_NO_ADJACENT ||
1423        getFacetConstraint(mesh, meshTet, facet)) {
1424       i++;
1425       continue;
1426     }
1427 
1428     uint32_t oppositeNode = mesh->tetrahedra.node[adjacencies[i]];
1429     uint8_t oppositeV = 255;
1430 
1431     for(int j=0; j<SPR->points.num; j++) {
1432       if(oppositeNode==SPR->points.array[j].userID) {
1433         oppositeV = j;
1434         break;
1435       }
1436     }
1437 
1438     if(oppositeV==255) {
1439       i++;
1440       continue;
1441     }
1442 
1443     // verify that the tet is in our partition
1444     if(tetOutOfPartition(vertices, partition, &mesh->tetrahedra.node[4*meshTet])){
1445         return HXT_STATUS_CONFLICT;
1446     }
1447 
1448     // delete the tet and flag it accordingly
1449     HXT_CHECK( askForDeleted(deleted, 1) );
1450     setDeletedFlag(mesh, meshTet);
1451     deleted->array[deleted->num++] = meshTet;
1452 
1453 #ifdef DEBUG
1454     if(!getDeletedFlag(mesh, mesh->tetrahedra.neigh[adjacencies[i]]/4))
1455       return HXT_ERROR_MSG(HXT_STATUS_ERROR, "DEBUG: reverse adjacency is not deleted");
1456 #endif
1457 
1458     // we must now find the corresponding points ID in the SPR structure
1459     // and we add the tet to the SPR structure :-)
1460     uint8_t* triV = SPR->bndTriangles.array[i].node;
1461     uint8_t* tetV = SPR->tetrahedra.array[SPR->tetrahedra.num++].node;
1462 
1463     tetV[facet] = oppositeV;
1464     for(int j=0; j<3; j++) {
1465       if(SPR->points.array[triV[j]].userID == mesh->tetrahedra.node[4*meshTet + getNode0FromFacet(facet)]) {
1466         tetV[getNode0FromFacet(facet)] = triV[j];
1467         tetV[getNode1FromFacet(facet)] = triV[(j+2)%3];
1468         tetV[getNode2FromFacet(facet)] = triV[(j+1)%3];
1469         break;
1470       }
1471     }
1472 
1473 #ifdef DEBUG
1474     // verify that we filled the tetV array correctly
1475     for(int j=0; j<4; j++) {
1476       if(SPR->points.array[tetV[j]].userID != mesh->tetrahedra.node[4*meshTet + j])
1477         return HXT_ERROR_MSG(HXT_STATUS_ERROR, "DEBUG: bug in the way we make SPR<->mesh correspond");
1478     }
1479 #endif
1480 
1481     // add the quality of the meshTet to known cavities :-)
1482     add_knownPositive_quality_map(SPR,
1483                                   tetV[0], tetV[1], tetV[2], tetV[3],
1484                                   local->quality->values[meshTet]);
1485 
1486     // update the quality to the worst one...
1487     if(local->quality->values[meshTet] < growingCav->worstQuality) {
1488       SPR->tetrahedra.quality = local->quality->values[meshTet];
1489       growingCav->worstQuality = local->quality->values[meshTet];
1490     }
1491 
1492     adjacencies[i] = adjacencies[SPR->bndTriangles.num-1];
1493     remove_face(SPR, i);
1494 
1495     // for each facet
1496     for (unsigned f=0; f<4; f++) {
1497       if(f==facet)
1498         continue;
1499       uint8_t p0 = tetV[nodeFromFacet[0][f]];
1500       uint8_t p1 = tetV[nodeFromFacet[1][f]];
1501       uint8_t p2 = tetV[nodeFromFacet[2][f]];
1502 
1503       uint16_t index = get_face_map(SPR, p0, p2, p1);
1504 
1505       if(index == UINT16_MAX) {
1506         // add the adjacency
1507         adjacencies[SPR->bndTriangles.num] = mesh->tetrahedra.neigh[4*meshTet + f];
1508         // add the face to the map
1509         add_face(SPR, p0, p1, p2);
1510       }
1511       else {
1512         if(getFacetConstraint(mesh, meshTet, f)) {
1513           // we remove the found face and add the facet to the constrained facet.
1514           adjacencies[index] = adjacencies[SPR->bndTriangles.num-1];
1515           remove_face(SPR, index);
1516 
1517           SPR->CITriangles.array[SPR->CITriangles.num++] = (SPRTriangle) {{p0, p1, p2, p0}};
1518         }
1519         else {
1520           // we may have a new constrained edge that is completely inside the cavity
1521           for(unsigned f2=0; f2<4; f2++) {
1522             if(f2==f)
1523               continue;
1524 
1525             int edgeNum = needNewSPREdge(mesh, meshTet, f, f2);
1526             if(edgeNum!=-1) {
1527               unsigned n0, n1;
1528               getNodesFromEdge(edgeNum, &n0, &n1);
1529 
1530               // add the edge to the SPR structure
1531               SPR->CIEdges.array[SPR->CIEdges.num++] = (SPREdge) {{tetV[n0], tetV[n1]}};
1532             }
1533           }
1534 
1535 
1536           // remove the face from the map, only if is not constrained
1537           adjacencies[index] = adjacencies[SPR->bndTriangles.num-1];
1538           remove_face(SPR, index);
1539 
1540           // except now we may have a face we did not look at which is before i...
1541           if(index < i) {
1542             i = index;
1543           }
1544         }
1545       }
1546     }
1547 
1548 
1549   }
1550 
1551   return HXT_STATUS_OK;
1552 }
1553 
1554 
1555 /* if edge a-b is constrained, put a '1' in constrainedEdgeMap[a][b]
1556  * and in constrainedEdgeMap[b][a] */
fillConstrainedEdgeMap(HXTMesh * mesh,SPRCavity * SPR,uint64_t * adjacencies,char constrainedEdgeMap[][SPR_MAX_PTS])1557 static HXTStatus fillConstrainedEdgeMap(HXTMesh* mesh, SPRCavity* SPR,
1558                                         uint64_t* adjacencies,
1559                                         char constrainedEdgeMap[][SPR_MAX_PTS])
1560 {
1561   // the edge that are in the SPR cavity structure are constrained
1562   for(int i=0; i<SPR->CIEdges.num; i++) {
1563     int v0 = SPR->CIEdges.array[i].node[0];
1564     int v1 = SPR->CIEdges.array[i].node[1];
1565     constrainedEdgeMap[v0][v1] = 1;
1566     constrainedEdgeMap[v1][v0] = 1;
1567   }
1568 
1569   // there might be other constrained edge on the surface of the cavity
1570   for(int i=0; i<SPR->bndTriangles.num; i++) {
1571     if(adjacencies[i]==HXT_NO_ADJACENT)
1572       continue;
1573 
1574     uint64_t meshTet = adjacencies[i]/4;
1575     int facet = adjacencies[i]%4;
1576 
1577     // the indices of the node of the triangle in the tet
1578     unsigned ind[3] = {getNode0FromFacet(facet), getNode2FromFacet(facet), getNode1FromFacet(facet)};
1579 
1580     uint8_t* triV = SPR->bndTriangles.array[i].node;
1581     // we must test the 3 rotations of the triangle to get one that match...
1582     int rotation;
1583     for(rotation=0; rotation<3; rotation++) {
1584       if(SPR->points.array[triV[rotation]].userID == mesh->tetrahedra.node[4*meshTet + ind[0]]) {
1585         break;
1586       }
1587     }
1588 
1589 #ifdef DEBUG
1590     for(int j=0; j<3; j++) {
1591       if(SPR->points.array[triV[(rotation+j)%3]].userID!=mesh->tetrahedra.node[4*meshTet + ind[j]]){
1592         return HXT_ERROR_MSG(HXT_STATUS_ERROR, "DEBUG: bug in the way we make SPR<->mesh correspond");
1593       }
1594     }
1595 #endif
1596 
1597     for(int j=0; j<3; j++) {
1598       if(getEdgeConstraint(mesh, meshTet, getEdgeFromNodes(ind[j], ind[(j+1)%3]))) {
1599         // edge 01 is constrained
1600         constrainedEdgeMap[triV[(rotation+j)%3]][(rotation+j+1)%3] = 1;
1601         constrainedEdgeMap[(rotation+j+1)%3][triV[(rotation+j)%3]] = 1;
1602       }
1603     }
1604   }
1605 
1606   return HXT_STATUS_OK;
1607 }
1608 
1609 
1610 /* replace the interior of the cavity within the mesh by the triangulation
1611  * found by hxtSPR() */
rebuildMesh(SPRGrowingCavity * growingCav,ThreadLocal * local,uint64_t prevNumDeleted,uint64_t badTet)1612 static inline HXTStatus rebuildMesh(SPRGrowingCavity* growingCav,
1613                                     ThreadLocal* local,
1614 #ifdef DEBUG
1615                                     uint64_t prevNumDeleted,
1616 #endif
1617                                     uint64_t badTet)
1618 {
1619   HXTDeleted* deleted = &local->deleted;
1620   HXTMesh* mesh = local->toSync->mesh;
1621   uint32_t color = mesh->tetrahedra.color[badTet];
1622   uint16_t lastCheck = local->SPR.dateOfLastCheck;
1623   local->SPR.dateOfLastCreation = lastCheck;
1624 
1625   SPRCavity* SPR = &growingCav->cavity;
1626 
1627 #ifdef DEBUG
1628   int adjacencies_to_nowhere = 0;
1629   for(uint64_t i=prevNumDeleted; i<deleted->num; i++) {
1630     uint64_t oldTet = deleted->array[i];
1631     for(int j=0; j<4; j++) {
1632       if(mesh->tetrahedra.neigh[4*oldTet + j] == HXT_NO_ADJACENT)
1633         adjacencies_to_nowhere++;
1634     }
1635   }
1636 #endif
1637 
1638   // we integrate the SPR cavity into the mesh
1639   if((uint64_t) SPR->tetrahedra.num > deleted->num) {
1640     HXT_CHECK( createNewDeleted(local->toSync, deleted, SPR->tetrahedra.num) );
1641   }
1642 
1643   uint64_t* adjacencies = growingCav->meshAdjacencies;
1644   uint64_t* newTet = &deleted->array[deleted->num - SPR->tetrahedra.num];
1645 
1646   //make a map for edge constraints
1647   char constrainedEdgeMap[SPR_MAX_PTS][SPR_MAX_PTS] = {0};
1648   HXT_CHECK( fillConstrainedEdgeMap(mesh, SPR, adjacencies, constrainedEdgeMap) );
1649 
1650 
1651 
1652 #ifdef DEBUG
1653   int numFound = 0;
1654 #endif
1655   for(int i=0; i<SPR->tetrahedra.num; i++) {
1656     uint64_t meshTet = newTet[i];
1657     uint8_t* v = SPR->tetrahedra.array[i].node;
1658 
1659     for(int j=0; j<4; j++)
1660       mesh->tetrahedra.node[4*meshTet + j] = SPR->points.array[v[j]].userID;
1661 
1662     mesh->tetrahedra.color[meshTet] = color;
1663     mesh->tetrahedra.flag[meshTet] = 0;
1664 
1665     unsigned index = get_compressed_index(v[0], v[1], v[2], v[3]);
1666     local->quality->values[meshTet] = SPR->map.qualities[index];
1667     local->date->values[meshTet].creation = lastCheck;
1668     local->date->values[meshTet].check = lastCheck;
1669 
1670     for(int j=0; j<4; j++) {
1671       // try to find the face in SPRTriangles
1672       uint16_t index = get_face_map(SPR,
1673                                     v[getNode0FromFacet(j)],
1674                                     v[getNode1FromFacet(j)],
1675                                     v[getNode2FromFacet(j)]);
1676 
1677       remove_face_map(SPR, v[getNode0FromFacet(j)],
1678                            v[getNode1FromFacet(j)],
1679                            v[getNode2FromFacet(j)]);
1680 
1681       if(index==UINT16_MAX) {
1682         mesh->tetrahedra.neigh[4*meshTet + j] = HXT_NO_ADJACENT;
1683       }
1684       else {
1685         uint64_t adj = adjacencies[index];
1686 
1687         if(adj==HXT_NO_ADJACENT) { // happens when there are no ghosts
1688           mesh->tetrahedra.neigh[4*meshTet + j] = HXT_NO_ADJACENT;
1689           setFacetConstraint(mesh, meshTet, j);
1690         }
1691         else {
1692           mesh->tetrahedra.neigh[4*meshTet + j] = adj;
1693           mesh->tetrahedra.neigh[adj] = 4*meshTet + j;
1694           if(getFacetConstraint(mesh, adj/4, adj%4))
1695              setFacetConstraint(mesh, meshTet, j);
1696         }
1697 #ifdef DEBUG
1698         numFound++;
1699 #endif
1700       }
1701     }
1702 
1703     for(int e=0; e<6; e++) {
1704       unsigned n0, n1;
1705       getNodesFromEdge(e, &n0, &n1);
1706       int v0 = SPR->tetrahedra.array[i].node[n0];
1707       int v1 = SPR->tetrahedra.array[i].node[n1];
1708 
1709       if(constrainedEdgeMap[v0][v1]) {
1710         setEdgeConstraint(mesh, meshTet, e);
1711       }
1712     }
1713   }
1714 
1715 #ifdef DEBUG
1716   if(numFound!=SPR->bndTriangles.num)
1717     return HXT_ERROR_MSG(HXT_STATUS_ERROR, "DEBUG: missing external adjacency");
1718 #endif
1719 
1720   /* now, all external adjacencies (except HXT_NO_ADJACENT ones obviously)
1721    *  have been found, we just have to find internal adjacencies :p */
1722   for(int i=0; i<SPR->tetrahedra.num; i++) {
1723     uint64_t meshTet = newTet[i];
1724 
1725     for(int j=0; j<4; j++) {
1726       if(mesh->tetrahedra.neigh[4*meshTet+j]==HXT_NO_ADJACENT) {
1727         add_face_map(SPR,
1728                      SPR->tetrahedra.array[i].node[getNode0FromFacet(j)],
1729                      SPR->tetrahedra.array[i].node[getNode1FromFacet(j)],
1730                      SPR->tetrahedra.array[i].node[getNode2FromFacet(j)],
1731                      4*i+j);
1732       }
1733     }
1734   }
1735 
1736   // reset constrained triangles flags
1737   for(int i=0; i<SPR->CITriangles.num; i++) {
1738     uint16_t adj0 = get_face_map(SPR,
1739                                  SPR->CITriangles.array[i].node[0],
1740                                  SPR->CITriangles.array[i].node[1],
1741                                  SPR->CITriangles.array[i].node[2]);
1742     uint16_t adj1 = get_face_map(SPR,
1743                                  SPR->CITriangles.array[i].node[0],
1744                                  SPR->CITriangles.array[i].node[2],
1745                                  SPR->CITriangles.array[i].node[1]);
1746 
1747 #ifndef NDEBUG
1748     if(adj0==UINT16_MAX || adj1==UINT16_MAX)
1749       return HXT_ERROR_MSG(HXT_STATUS_ERROR, "constrained triangle not found in the tetrahedralization");
1750 #endif
1751     uint64_t tet0 = newTet[adj0/4];
1752     unsigned facet0 = adj0%4;
1753     setFacetConstraint(mesh, tet0, facet0);
1754 
1755     uint64_t tet1 = newTet[adj1/4];
1756     unsigned facet1 = adj1%4;
1757     setFacetConstraint(mesh, tet1, facet1);
1758 
1759     mesh->tetrahedra.neigh[4*tet0 + facet0] = 4*tet1 + facet1;
1760     mesh->tetrahedra.neigh[4*tet1 + facet1] = 4*tet0 + facet0;
1761   }
1762 
1763   for(int i=0; i<SPR->tetrahedra.num; i++) {
1764     uint64_t meshTet = newTet[i];
1765 
1766     for(int j=0; j<4; j++) {
1767       if(mesh->tetrahedra.neigh[4*meshTet+j]==HXT_NO_ADJACENT) {
1768         uint16_t adj = get_face_map(SPR,
1769                                     SPR->tetrahedra.array[i].node[getNode0FromFacet(j)],
1770                                     SPR->tetrahedra.array[i].node[getNode2FromFacet(j)],
1771                                     SPR->tetrahedra.array[i].node[getNode1FromFacet(j)]);
1772         if(adj!=UINT16_MAX) {
1773           uint64_t neighTet = newTet[adj/4];
1774           mesh->tetrahedra.neigh[4*meshTet+j] = 4*neighTet + adj%4;
1775         }
1776       }
1777     }
1778   }
1779 
1780 #ifdef DEBUG
1781   for (int i=0; i<SPR->tetrahedra.num; i++) {
1782     uint64_t meshTet = newTet[i];
1783 
1784     for (int j=0; j<4; ++j)
1785     {
1786       if(mesh->tetrahedra.neigh[4*meshTet+j]==HXT_NO_ADJACENT) {
1787         adjacencies_to_nowhere--;
1788       }
1789       else if(mesh->tetrahedra.neigh[4*meshTet+j]!=HXT_NO_ADJACENT && mesh->tetrahedra.neigh[4*meshTet+j]/4>=mesh->tetrahedra.num) {
1790         return HXT_ERROR_MSG(HXT_STATUS_ERROR, "invalid neighbor");
1791       }
1792       else if(mesh->tetrahedra.neigh[mesh->tetrahedra.neigh[4*meshTet+j]]!=4*meshTet+j){
1793         return HXT_ERROR_MSG(HXT_STATUS_ERROR, "one of the tetrahedron is not the neighbor of its neighbor");
1794       }
1795     }
1796   }
1797 
1798   if(adjacencies_to_nowhere!=0)
1799     return HXT_ERROR_MSG(HXT_STATUS_ERROR, "there are unrecovered adjacencies");
1800 #endif
1801 
1802   deleted->num -= SPR->tetrahedra.num;
1803 
1804   return HXT_STATUS_OK;
1805 }
1806 
1807 
1808 /* check the creation dates of each tet of the cavity
1809  * if none of the date are as recent as the last check
1810  * return 0
1811  * else, return 1 */
verifyDates(ThreadLocal * local,uint64_t prevNumDeleted,uint16_t lastCheck)1812 int verifyDates(ThreadLocal* local, uint64_t prevNumDeleted, uint16_t lastCheck)
1813 {
1814   HXTDeleted* deleted = &local->deleted;
1815   for(uint64_t i=prevNumDeleted; i<deleted->num; i++) {
1816     uint16_t creation = local->date->values[deleted->array[i]].creation;
1817     if(creation >= lastCheck)
1818       return 1;
1819   }
1820 
1821   return 0;
1822 }
1823 
1824 
1825 /* we checked the growing cavity till the end but it didn't work
1826  * we mark the date of this failure... */
setCheckDate(ThreadLocal * local,uint64_t badTet)1827 HXTStatus setCheckDate(ThreadLocal* local, uint64_t badTet)
1828 {
1829   local->date->values[badTet].check = local->SPR.dateOfLastCreation + 1;
1830   local->SPR.dateOfLastCheck = local->SPR.dateOfLastCreation + 1;
1831   return HXT_STATUS_OK;
1832 }
1833 
1834 
deleted_reset(ThreadLocal * local,uint64_t prevNumDeleted)1835 static void deleted_reset(ThreadLocal* local, uint64_t prevNumDeleted)
1836 {
1837   HXTDeleted* deleted = &local->deleted;
1838   HXTMesh* mesh = local->toSync->mesh;
1839   for(uint64_t i=prevNumDeleted; i<deleted->num; i++) {
1840     unsetDeletedFlag(mesh, deleted->array[i]);
1841   }
1842   deleted->num = prevNumDeleted;
1843 }
1844 
1845 
1846 
1847 
1848 
1849 
hxtSPR_opti(ThreadLocal * local,uint64_t badTet)1850 HXTStatus hxtSPR_opti(ThreadLocal* local,
1851                       uint64_t badTet)
1852 {
1853   SPRGrowingCavity growingCav;  // all the structure on the stack... it should fit ^^
1854   SPRCavity* SPR = &growingCav.cavity;
1855   uint64_t prevNumDeleted = local->deleted.num;
1856 
1857   uint16_t lastCheck = local->date->values[badTet].check;
1858   int datesOk = 0;
1859 
1860   HXT_CHECK( SPROpti_init(&growingCav, local, badTet) );
1861 
1862   do {
1863     // return HXT_STATUS_INTERNAL if the cavity if completely constrained
1864     HXTStatus status = attach_best_point(&growingCav, local);
1865 
1866     if(status==HXT_STATUS_CONFLICT || status==HXT_STATUS_INTERNAL) {
1867       deleted_reset(local, prevNumDeleted);
1868       return status;
1869     }
1870     else if(status!=HXT_STATUS_OK) {
1871       HXT_TRACE(status);
1872       return status;
1873     }
1874 
1875     if(datesOk==0)
1876       datesOk = verifyDates(local, prevNumDeleted, lastCheck);
1877 
1878     if(datesOk) {
1879       /* applying the SPR algorithm, only initializing the necessary minimum */
1880       SPR_detect_interior_points(SPR);
1881       SPR_init_step(SPR);
1882 
1883       // save the triangles in their current order
1884       SPRTriangle original[SPR_MAX_BNDTRIANGLES];
1885       uint64_t adjacencies[SPR_MAX_BNDTRIANGLES];
1886 
1887       memcpy(original, SPR->bndTriangles.array, SPR->bndTriangles.num*sizeof(SPRTriangle));
1888       memcpy(adjacencies, growingCav.meshAdjacencies, SPR->bndTriangles.num*sizeof(uint64_t));
1889 
1890       status = hxtSPR_advanced(SPR);
1891 
1892       if(status==HXT_STATUS_INTERNAL) {
1893         if(growingCav.worstQuality < growingCav.cavity.tetrahedra.quality){
1894           hxtSPR_rewind(SPR);
1895         }
1896         else {
1897           // HXT_WARNING("Maximum nodes of SPR search reached");
1898           deleted_reset(local, prevNumDeleted);
1899           setCheckDate(local, badTet);
1900           return HXT_STATUS_INTERNAL;
1901         }
1902       }
1903 
1904       // reorder the adjacencies in the new order
1905       for(int i=0; i<SPR->bndTriangles.num; i++) {
1906         uint8_t* triV = original[i].node;
1907 
1908         uint16_t index = get_face_map(SPR, triV[0], triV[1], triV[2]);
1909 
1910 #ifndef NDEBUG
1911         if(index==UINT16_MAX) {
1912           return HXT_ERROR_MSG(HXT_STATUS_ERROR, "original is not present in the cavity anymore");
1913         }
1914 #endif
1915         growingCav.meshAdjacencies[index] = adjacencies[i];
1916       }
1917 
1918       if(growingCav.worstQuality < growingCav.cavity.tetrahedra.quality){
1919         break;// we found a better solution
1920       }
1921     }
1922 
1923     if(growingCav.cavity.points.num==SPR_MAX_PTS) {
1924       // HXT_WARNING("Too much points in SPR cavity");
1925       deleted_reset(local, prevNumDeleted);
1926       setCheckDate(local, badTet);
1927       return HXT_STATUS_INTERNAL;
1928     }
1929   } while(1);
1930 
1931 
1932 #ifdef DEBUG
1933     HXT_CHECK(rebuildMesh(&growingCav, local, prevNumDeleted, badTet));
1934 #else
1935     HXT_CHECK(rebuildMesh(&growingCav, local, badTet));
1936 #endif
1937 
1938   return HXT_STATUS_OK;
1939 }
1940