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