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