1 // Copyright (c) 2009 INRIA Sophia-Antipolis (France).
2 // All rights reserved.
3 //
4 // This file is part of CGAL (www.cgal.org).
5 //
6 // $URL: https://github.com/CGAL/cgal/blob/v5.3/Mesh_3/include/CGAL/Mesh_3/Sizing_grid.h $
7 // $Id: Sizing_grid.h e9d41d7 2020-04-21T10:03:00+02:00 Maxime Gimeno
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 //
11 // Author(s) : Stephane Tayeb, Jane Tournois, Camille Wormser, Pierre Alliez
12 //
13 //******************************************************************************
14 // File Description :
15 //******************************************************************************
16
17 #ifndef CGAL_MESH_3_SIZING_GRID_H
18 #define CGAL_MESH_3_SIZING_GRID_H
19
20 #include <CGAL/license/Mesh_3.h>
21
22
23 #include <CGAL/Mesh_3/config.h>
24
25 #include <CGAL/basic.h>
26 #include <queue>
27
28 namespace CGAL {
29
30 #define CGAL_MESH_3_INFINITE_SIZE 1e30
31
32 namespace Mesh_3 {
33
34 template <class Gt>
35 class Sizing_grid_node
36 {
37 public:
38 typedef typename Gt::Point_3 Point;
39 typedef typename Gt::FT FT;
40
41 FT m_init_size;
42 FT m_size;
43 FT m_distance;
44 bool m_done;
45 Point m_point;
46 int m_indices[3];
47 Sizing_grid_node* m_pRef_node;
48
49 public:
Sizing_grid_node()50 Sizing_grid_node()
51 {
52 m_done = false;
53 m_size = CGAL_MESH_3_INFINITE_SIZE;
54 m_pRef_node = nullptr;
55 m_indices[0] = m_indices[1] = m_indices[2] = 0;
56 }
57
~Sizing_grid_node()58 ~Sizing_grid_node() {}
59
point()60 const Point& point() const { return m_point; }
point()61 Point& point() { return m_point; }
62
ref_node()63 Sizing_grid_node* ref_node() { return m_pRef_node; }
ref_node(Sizing_grid_node * pRef_node)64 void ref_node(Sizing_grid_node* pRef_node) { m_pRef_node = pRef_node; }
65
done()66 bool& done() { return m_done; }
done()67 const bool& done() const { return m_done; }
68
size()69 FT& size() { return m_size; }
size()70 const FT& size() const { return m_size; }
71
init_size()72 FT& init_size() { return m_init_size; }
init_size()73 const FT& init_size() const { return m_init_size; }
74
i()75 const int& i() const { return m_indices[0]; }
j()76 const int& j() const { return m_indices[1]; }
k()77 const int& k() const { return m_indices[2]; }
i()78 int& i() { return m_indices[0]; }
j()79 int& j() { return m_indices[1]; }
k()80 int& k() { return m_indices[2]; }
81
indices(const int i,const int j,const int k)82 void indices(const int i,
83 const int j,
84 const int k)
85 {
86 m_indices[0] = i;
87 m_indices[1] = j;
88 m_indices[2] = k;
89 }
90 };
91
92 // functor for priority queue
93 template<class c>
94 struct less_candidate_size
95 {
operatorless_candidate_size96 bool operator()(const c& c1,
97 const c& c2) const
98 {
99 return c1.size() > c2.size();
100 }
101 };
102
103
104
105
106
107 template <class Tr>
108 class Sizing_grid
109 {
110 public:
111 typedef typename Tr::Geom_traits Gt;
112 typedef typename Gt::FT FT;
113
114 typedef typename Tr::Weighted_point Weighted_point;
115 typedef typename Tr::Bare_point Bare_point;
116
117 typedef typename Gt::Vector_3 Vector;
118 typedef Sizing_grid_node<Gt> Node;
119 typedef typename std::pair<Bare_point, FT> Constraint;
120
121 private:
122 // grid
123 Node ***m_pppNodes;
124 FT m_k;
125 FT m_ds;
126 FT m_dv;
127 FT m_xrange[3];
128 FT m_yrange[3];
129 FT m_zrange[3];
130 unsigned int m_nx,m_ny,m_nz;
131 FT m_max_size;
132 bool m_updated;
133 std::list<Constraint> m_constraints;
134
135 class Candidate_size
136 {
137 private:
138 FT m_size;
139 Node* m_pNode;
140 Node* m_pRef_node;
141
142 public:
143
Candidate_size(Node * pNode,Node * pRef_node,const FT k)144 Candidate_size(Node* pNode,
145 Node* pRef_node,
146 const FT k)
147 {
148 m_pNode = pNode;
149 m_pRef_node = pRef_node;
150
151 // size = K d(node,v) + init_size(v)
152 const Bare_point& p1 = pNode->point();
153 const Bare_point& p2 = m_pRef_node->point();
154 FT distance = (FT)std::sqrt(CGAL_NTS to_double(CGAL::squared_distance(p1,p2)));
155 m_size = k * distance + m_pRef_node->init_size();
156 }
157
~Candidate_size()158 ~Candidate_size() {}
159
160 public:
Candidate_size(const Candidate_size & c)161 Candidate_size(const Candidate_size& c)
162 {
163 m_pNode = c.node();
164 m_pRef_node = c.ref_node();
165 m_size = c.size();
166 }
167
node()168 Node* node() const { return m_pNode; }
ref_node()169 Node* ref_node() const { return m_pRef_node; }
170
size()171 FT& size() { return m_size; }
size()172 const FT& size() const { return m_size; }
173 };
174
175
176 typedef typename std::priority_queue<Candidate_size,
177 std::vector<Candidate_size>,
178 less_candidate_size<Candidate_size> > PQueue;
179
180
181 public:
Sizing_grid(const Tr & tr)182 Sizing_grid(const Tr& tr)
183 {
184 m_k = 1.0;
185 m_ds = m_dv = 0;
186 m_pppNodes = nullptr;
187 m_nx = m_ny = m_nz = 0;
188 m_xrange[0] = m_xrange[1] = m_xrange[2] = 0;
189 m_yrange[0] = m_yrange[1] = m_yrange[2] = 0;
190 m_zrange[0] = m_zrange[1] = m_zrange[2] = 0;
191 m_updated = false;
192
193 // Build grid
194 unsigned int nb_nodes = tr.number_of_vertices()*27;
195
196 Bbox_3 tr_bbox;
197 for ( typename Tr::Finite_cells_iterator cit = tr.finite_cells_begin() ;
198 cit != tr.finite_cells_end() ;
199 ++cit )
200 {
201 tr_bbox = tr_bbox + tr.tetrahedron(cit).bbox();
202 }
203
204 init(tr_bbox.xmin(), tr_bbox.xmax(),
205 tr_bbox.ymin(), tr_bbox.ymax(),
206 tr_bbox.zmin(), tr_bbox.zmax(),
207 nb_nodes);
208 }
209
~Sizing_grid()210 ~Sizing_grid()
211 {
212 cleanup();
213 }
214
fill(const std::map<Weighted_point,FT> & value_map)215 void fill(const std::map<Weighted_point,FT>& value_map)
216 {
217 for ( typename std::map<Weighted_point,FT>::const_iterator it = value_map.begin() ;
218 it != value_map.end() ;
219 ++it )
220 {
221 add_constraint(it->first, it->second);
222 }
223
224 update();
225 }
226
operator()227 FT operator()(const Point& query) const
228 {
229 return size_trilinear(query);
230 }
231
232 private:
233
k()234 FT& k() { return m_k; }
k()235 const FT& k() const { return m_k; }
236
cleanup()237 void cleanup()
238 {
239 m_constraints.clear();
240 unsigned int i,j;
241 for(i=0; i<m_nx; i++)
242 {
243 for(j=0; j<m_ny; j++)
244 delete [] m_pppNodes[i][j];
245 delete [] m_pppNodes[i];
246 }
247 delete [] m_pppNodes;
248 m_pppNodes = nullptr;
249 m_nx = m_ny = m_nz = 0;
250 m_updated = false;
251 }
252
alloc(const unsigned int nx,const unsigned int ny,const unsigned int nz)253 bool alloc(const unsigned int nx,
254 const unsigned int ny,
255 const unsigned int nz)
256 {
257 // cleanup
258 cleanup();
259
260 // alloc
261 m_pppNodes = new Node**[nx];
262 if(m_pppNodes == nullptr)
263 {
264 cleanup();
265 return false;
266 }
267 unsigned int i,j;
268 for(i=0; i<nx; i++)
269 {
270 m_pppNodes[i] = new Node*[ny];
271 if(m_pppNodes[i] == nullptr)
272 {
273 cleanup();
274 return false;
275 }
276 for(j=0; j<ny; j++)
277 {
278 m_pppNodes[i][j] = new Node[nz];
279 if(m_pppNodes[i][j] == nullptr)
280 {
281 cleanup();
282 return false;
283 }
284 }
285 }
286 m_nx = nx;
287 m_ny = ny;
288 m_nz = nz;
289 return true;
290 }
291
292
293
294 // trilinear interpolation of size
295 // https://en.wikipedia.org/wiki/Trilinear_interpolation
size_trilinear(const Point & query)296 FT size_trilinear(const Point& query) const
297 {
298 FT px = query.x();
299 if ( px < m_xrange[0] )
300 px = m_xrange[0];
301 if ( px >= m_xrange[1] )
302 px = m_xrange[1]-m_ds;
303
304 FT py = query.y();
305 if ( py < m_yrange[0] )
306 py = m_yrange[0];
307 if ( py >= m_yrange[1] )
308 py = m_yrange[1]-m_ds;
309
310 FT pz = query.z();
311 if ( pz < m_zrange[0] )
312 pz = m_zrange[0];
313 if ( pz >= m_zrange[1] )
314 pz = m_zrange[1]-m_ds;
315
316 CGAL_assertion(px >= m_xrange[0] && px < m_xrange[1] &&
317 py >= m_yrange[0] && py < m_yrange[1] &&
318 pz >= m_zrange[0] && pz < m_zrange[1]);
319
320 FT x = ((px-m_xrange[0])/m_xrange[2]*(FT)m_nx);
321 FT y = ((py-m_yrange[0])/m_yrange[2]*(FT)m_ny);
322 FT z = ((pz-m_zrange[0])/m_zrange[2]*(FT)m_nz);
323 int xl = (int)floor(x);
324 int yl = (int)floor(y);
325 int zl = (int)floor(z);
326 int xh = (int)ceil(x);
327 int yh = (int)ceil(y);
328 int zh = (int)ceil(z);
329 FT xd = x - xl;
330 FT yd = y - yl;
331 FT zd = z - zl;
332 CGAL_assertion(xd >= 0.0 && xd <= 1.0);
333 CGAL_assertion(yd >= 0.0 && yd <= 1.0);
334 CGAL_assertion(zd >= 0.0 && zd <= 1.0);
335 FT i1 = node(xl,yl,zl)->size() * (1.0 - zd) + node(xl,yl,zh)->size() * zd;
336 FT i2 = node(xl,yh,zl)->size() * (1.0 - zd) + node(xl,yh,zh)->size() * zd;
337 FT j1 = node(xh,yl,zl)->size() * (1.0 - zd) + node(xh,yl,zh)->size() * zd;
338 FT j2 = node(xh,yh,zl)->size() * (1.0 - zd) + node(xh,yh,zh)->size() * zd;
339 FT w1 = i1 * (1.0 - yd) + i2 * yd;
340 FT w2 = j1 * (1.0 - yd) + j2 * yd;
341 return w1 * (1.0 - xd) + w2 * xd;
342 }
343
node(const Point & query)344 Node *node(const Point& query) const
345 {
346 const FT x = query.x();
347 const FT y = query.y();
348 const FT z = query.z();
349 if(x >= m_xrange[0] && x < m_xrange[1] &&
350 y >= m_yrange[0] && y < m_yrange[1] &&
351 z >= m_zrange[0] && z < m_zrange[1])
352 {
353 int i = (int)((x-m_xrange[0])/m_xrange[2]*(FT)m_nx);
354 int j = (int)((y-m_yrange[0])/m_yrange[2]*(FT)m_ny);
355 int k = (int)((z-m_zrange[0])/m_zrange[2]*(FT)m_nz);
356 return node(i,j,k);
357 }
358 return nullptr;
359 }
360
node(const int i,const int j,const int k)361 Node* node(const int i,
362 const int j,
363 const int k) const
364 {
365 if(m_pppNodes == nullptr)
366 return nullptr;
367
368 if(i < 0 ||
369 j < 0 ||
370 k < 0 ||
371 i >= (int)m_nx ||
372 j >= (int)m_ny ||
373 k >= (int)m_nz)
374 return nullptr;
375
376 return &m_pppNodes[i][j][k];
377 }
378
neighbor(Node * n,unsigned int index)379 Node* neighbor(Node* n,
380 unsigned int index)
381 {
382 if(n == nullptr)
383 return nullptr;
384
385 switch(index)
386 {
387 case 0:
388 return node(n->i()-1,n->j(),n->k());
389 case 1:
390 return node(n->i()+1,n->j(),n->k());
391 case 2:
392 return node(n->i(),n->j()-1,n->k());
393 case 3:
394 return node(n->i(),n->j()+1,n->k());
395 case 4:
396 return node(n->i(),n->j(),n->k()-1);
397 case 5:
398 return node(n->i(),n->j(),n->k()+1);
399 default:
400 return nullptr;
401 }
402 }
403
404 bool init(const FT xmin,
405 const FT xmax,
406 const FT ymin,
407 const FT ymax,
408 const FT zmin,
409 const FT zmax,
410 const unsigned int nb_samples,
411 const FT ratio = 1.3)
412 {
413 reset();
414 if(!init_range_and_alloc(xmin,xmax,ymin,ymax,zmin,zmax,nb_samples,ratio))
415 return false;
416 init_positions_and_indices();
417 return true;
418 }
419
len(const Vector & v)420 static FT len(const Vector &v)
421 {
422 return (FT)std::sqrt(CGAL_NTS to_double(v*v));
423 }
sqlen(const Vector & v)424 static FT sqlen(const Vector &v)
425 {
426 return v*v;
427 }
428
flood(PQueue & priority_queue)429 void flood(PQueue& priority_queue)
430 {
431 m_max_size = 0.0;
432 // flood using priority queue
433 while(!priority_queue.empty())
434 {
435 // pop candidate out of the queue
436 Candidate_size candidate = priority_queue.top();
437 priority_queue.pop();
438
439 Node* pNode = candidate.node();
440 if(pNode->done() == true)
441 continue;
442
443 pNode->ref_node(candidate.ref_node());
444 pNode->size() = candidate.size();
445 pNode->done() = true;
446 m_max_size = (std::max)(m_max_size,pNode->size());
447
448 // explore neighbors
449 for(unsigned int index_neighbor = 0;
450 index_neighbor < 6;
451 index_neighbor++)
452 {
453 // TODO: change size of seeds
454 Node *pNeighbor = neighbor(pNode,index_neighbor);
455 if(pNeighbor != nullptr &&
456 pNeighbor->done() == false)
457 priority_queue.push(Candidate_size(pNeighbor,pNode->ref_node(),m_k));
458 }
459 }
460 }
461
init_pqueue(PQueue & priority_queue)462 void init_pqueue(PQueue& priority_queue)
463 {
464 // insert sizing constraints and init priority queue
465 typename std::list<Constraint>::iterator it;
466 for(it = m_constraints.begin();
467 it != m_constraints.end();
468 it++)
469 {
470 const Point& p = (*it).first;
471 const FT init_size = (*it).second;
472
473 // get node at position p
474 Node *pNode = node(p);
475 if(pNode != nullptr && pNode->done() == false) // specific
476 {
477 pNode->point() = p;
478 pNode->init_size() = init_size;
479 pNode->done() = true;
480 pNode->size() = init_size;
481 pNode->ref_node(pNode);
482
483 // insert all valid neighbors to the priority queue
484 for(unsigned int index_neighbor = 0;
485 index_neighbor < 6;
486 index_neighbor++)
487 {
488 Node *pNeighbor = neighbor(pNode,index_neighbor);
489 if(pNeighbor != nullptr &&
490 pNeighbor->done() == false)
491 {
492 Candidate_size candidate(pNeighbor,pNode->ref_node(),m_k);
493 priority_queue.push(candidate);
494 }
495 }
496 }
497 }
498 }
499
500 bool init_range_and_alloc(const FT xmin,
501 const FT xmax,
502 const FT ymin,
503 const FT ymax,
504 const FT zmin,
505 const FT zmax,
506 const unsigned int nb_samples,
507 const FT ratio = 1.1)
508 {
509 m_xrange[2] = ratio*(xmax - xmin);
510 m_yrange[2] = ratio*(ymax - ymin);
511 m_zrange[2] = ratio*(zmax - zmin);
512
513 FT xmid = 0.5*(xmin+xmax);
514 FT ymid = 0.5*(ymin+ymax);
515 FT zmid = 0.5*(zmin+zmax);
516
517 m_xrange[0] = xmid - 0.5*m_xrange[2];
518 m_yrange[0] = ymid - 0.5*m_yrange[2];
519 m_zrange[0] = zmid - 0.5*m_zrange[2];
520
521 m_xrange[1] = xmid + 0.5*m_xrange[2];
522 m_yrange[1] = ymid + 0.5*m_yrange[2];
523 m_zrange[1] = zmid + 0.5*m_zrange[2];
524
525 if(m_xrange[2] == 0.0 ||
526 m_yrange[2] == 0.0 ||
527 m_zrange[2] == 0.0)
528 return false;
529
530 // deduce nx, ny, nz
531 FT volume = m_xrange[2] * m_yrange[2] * m_zrange[2];
532 m_dv = volume / (FT)nb_samples;
533 m_ds = std::pow(m_dv,1.0/3.0);
534
535 unsigned nx = (unsigned)(m_xrange[2] / m_ds);
536 unsigned ny = (unsigned)(m_yrange[2] / m_ds);
537 unsigned nz = (unsigned)(m_zrange[2] / m_ds);
538
539 // alloc (and set m_nx.. variables)
540 if(!alloc(nx,ny,nz))
541 return false;
542 return true;
543 }
544
init_positions_and_indices()545 void init_positions_and_indices()
546 {
547 // init positions and tags
548 FT x = m_xrange[0] + m_ds/2;
549 for(int i=0;i<(signed)m_nx;i++)
550 {
551 FT y = m_yrange[0] + m_ds/2;
552 for(int j=0;j<(signed)m_ny;j++)
553 {
554 FT z = m_zrange[0] + m_ds/2;
555 for(int k=0;k<(signed)m_nz;k++)
556 {
557 Node* pNode = &m_pppNodes[i][j][k];
558 pNode->indices(i,j,k);
559 pNode->point() = Point(x,y,z);
560 z += m_ds;
561 }
562 y += m_ds;
563 }
564 x += m_ds;
565 }
566 }
567
tag_done(const bool done)568 void tag_done(const bool done)
569 {
570 for(unsigned int i=0;i<m_nx;i++)
571 for(unsigned int j=0;j<m_ny;j++)
572 for(unsigned int k=0;k<m_nz;k++)
573 m_pppNodes[i][j][k].done() = done;
574 }
575
576 // total reset
reset(FT k)577 void reset(FT k)
578 {
579 m_k = k;
580 cleanup();
581 }
582
583 // reset where m_k is kept
reset()584 void reset()
585 {
586 cleanup();
587 }
588
set_k(const FT k)589 void set_k(const FT k)
590 {
591 if(k != m_k)
592 {
593 m_k = k;
594 update();
595 }
596 }
597
add_constraint(const Point & point,const FT & size)598 void add_constraint(const Point& point,
599 const FT& size)
600 {
601 Constraint constraint(point,size);
602 m_constraints.push_back(constraint);
603 }
604
605 // update sizing grid
update()606 void update()
607 {
608 tag_done(false);
609 PQueue priority_queue;
610 init_pqueue(priority_queue);
611 flood(priority_queue);
612 m_updated = true;
613 }
614 };
615
616 }// end namespace Mesh_3
617
618
619 } //namespace CGAL
620
621 #endif // CGAL_MESH_3_SIZING_GRID_H
622