Salome HOME
873eb37d7ab9d631c93d0c934fc5697e26455151
[modules/smesh.git] / src / SMESHUtils / SMESH_Triangulate.cxx
1 // Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 // File      : SMESH_Triangulate.cxx
23 // Created   : Thu Jan 18 18:00:13 2018
24 // Author    : Edward AGAPOV (eap)
25
26 // Extracted from ../DriverSTL/DriverSTL_W_SMDS_Mesh.cxx
27
28 #include "SMESH_MeshAlgos.hxx"
29
30 #include <Standard_ErrorHandler.hxx>
31 #include <Standard_Failure.hxx>
32 #include <gp_Ax2.hxx>
33
34 #include <boost/container/flat_set.hpp>
35
36 using namespace SMESH_MeshAlgos;
37
38 namespace
39 {
40   struct Node // node of a triangle
41   {
42     size_t _triaIndex; // triangle index == index of the 1st triangle node in triangulation array
43     size_t _nodeIndex; // node index within triangle [0-2]
44
45     //! return node index within the node array
46     size_t Index() const { return  _triaIndex + _nodeIndex; }
47
48     //! return local 3-d index [0-2]
49     static size_t ThirdIndex( size_t i1, size_t i2 )
50     {
51       size_t i3 = ( i2 + 1 ) % 3;
52       if ( i3 == i1 )
53         i3 = ( i2 + 2 ) % 3;
54       return i3;
55     }
56     //! return 3-d node index within the node array
57     static size_t ThirdIndex( const Node& n1, const Node& n2 )
58     {
59       return n1._triaIndex + ThirdIndex( n1._nodeIndex, n2._nodeIndex );
60     }
61     bool operator<(const Node& other) const { return _triaIndex < other._triaIndex; }
62   };
63   typedef boost::container::flat_set< Node >                 TriaNodeSet;
64
65 }
66 /*!
67  * \brief Vertex of a polygon. Together with 2 neighbor Vertices represents a triangle
68  */
69 struct Triangulate::PolyVertex
70 {
71   SMESH_NodeXYZ _nxyz;
72   size_t        _index;
73   gp_XY         _xy;
74   PolyVertex*   _prev;
75   PolyVertex*   _next;
76
77   void   SetNodeAndNext( const SMDS_MeshNode* n, PolyVertex& v, size_t index );
78   void   GetTriaNodes( const SMDS_MeshNode** nodes, size_t* nodeIndices) const;
79   double TriaArea() const;
80   bool   IsInsideTria( const PolyVertex* v );
81   PolyVertex* Delete();
82
83   struct Compare // compare PolyVertex'es by node
84   {
85     bool operator()(const PolyVertex* a, const PolyVertex* b) const
86     {
87       return ( a->_nxyz.Node() <  b->_nxyz.Node() );
88     }
89   };
90   // set of PolyVertex sorted by mesh node
91   typedef boost::container::flat_set< PolyVertex*, Compare > PVSet;
92 };
93
94 struct Triangulate::Data
95 {
96   std::vector< PolyVertex > _pv;
97   std::vector< size_t >     _nodeIndex;
98   PolyVertex::PVSet         _uniqueNodePV;
99 };
100
101 struct Triangulate::Optimizer
102 {
103   std::vector< TriaNodeSet > _nodeUsage; // inclusions of a node in triangles
104
105   //================================================================================
106   /*!
107    * \brief Optimize triangles by edge swapping
108    *  \param [inout] nodes - polygon triangulation, i.e. connectivity of all triangles to optimize
109    *  \param [in] points - coordinates of nodes of the input polygon
110    *  \param [in] nodeIndices - indices of triangulation nodes within the input polygon
111    */
112   //================================================================================
113
114   void optimize( std::vector< const SMDS_MeshNode*>& nodes,
115                  std::vector< PolyVertex > &         points,
116                  std::vector< size_t > &             nodeIndices)
117   {
118     // for each node of the polygon, remember triangles using it
119     _nodeUsage.resize( points.size() );
120     for ( size_t i = 0; i < points.size(); ++i ) // clear old data
121     {
122       _nodeUsage[ i ].clear();
123     }
124     for ( size_t i = 0, iTria = 0; i < nodeIndices.size(); ++iTria )
125     {
126       _nodeUsage[ nodeIndices[ i++ ]].insert({ iTria * 3, 0 });
127       _nodeUsage[ nodeIndices[ i++ ]].insert({ iTria * 3, 1 });
128       _nodeUsage[ nodeIndices[ i++ ]].insert({ iTria * 3, 2 });
129     }
130
131     // optimization
132     for ( size_t iTria = 0; iTria < nodeIndices.size(); iTria += 3 )
133     {
134       double badness1 = computeBadness( nodeIndices[ iTria + 0 ],
135                                         nodeIndices[ iTria + 1 ],
136                                         nodeIndices[ iTria + 2 ],
137                                         points );
138       for ( size_t i = 0; i < 3; ++i ) // loop on triangle edges to find a neighbor triangle
139       {
140         size_t i1 = iTria + i; // node index in nodeIndices
141         size_t i2 = iTria + ( i + 1 ) % 3;
142         size_t ind1 = nodeIndices[ i1 ]; // node index in points
143         size_t ind2 = nodeIndices[ i2 ];
144         TriaNodeSet & usage1 = _nodeUsage[ ind1 ]; // triangles using a node
145         TriaNodeSet & usage2 = _nodeUsage[ ind2 ];
146         if ( usage1.size() < 2 ||
147              usage2.size() < 2 )
148           continue;
149
150         // look for another triangle using two nodes
151         TriaNodeSet::iterator usIt1 = usage1.begin();
152         for ( ; usIt1 != usage1.end(); ++usIt1 )
153         {
154           if ( usIt1->_triaIndex == iTria )
155             continue; // current triangle
156           TriaNodeSet::iterator usIt2 = usage2.find( *usIt1 );
157           if ( usIt2 == usage2.end() )
158             continue; // no common _triaIndex in two usages
159
160           size_t i3 = iTria + ( i + 2 ) % 3;
161           size_t i4 = Node::ThirdIndex( *usIt1, *usIt2 ); // 4th node of quadrangle
162           size_t ind3 = nodeIndices[ i3 ];
163           size_t ind4 = nodeIndices[ i4 ];
164
165           double badness2 = computeBadness( ind2, ind1, ind4, points );
166           double badness3 = computeBadness( ind1, ind4, ind3, points, /*checkArea=*/true );
167           double badness4 = computeBadness( ind2, ind3, ind4, points, /*checkArea=*/true );
168
169           if ( Max( badness1, badness2 ) < Max( badness3, badness4 ))
170             continue;
171
172           // swap edge by modifying nodeIndices
173
174           nodeIndices[ i2 ] = ind4;
175           _nodeUsage[ ind4 ].insert({ iTria, i2 - iTria });
176           _nodeUsage[ ind2 ].erase ({ iTria, i2 - iTria });
177
178           i1 = usIt1->Index();
179           nodeIndices[ i1 ] = ind3;
180           _nodeUsage[ ind3 ].insert( *usIt1 );
181           _nodeUsage[ ind1 ].erase ( *usIt1 );
182
183           --i; // to re-check a current edge
184           badness1 = badness3;
185           break;
186         }
187       }
188     }
189
190     // update nodes by updated nodeIndices
191     for ( size_t i = 0; i < nodeIndices.size(); ++i )
192       nodes[ i ] = points[ nodeIndices[ i ]]._nxyz.Node();
193
194     return;
195   }
196
197   //================================================================================
198   /*!
199    * \brief Return 1./area. Initially: max cos^2 of triangle angles
200    */
201   //================================================================================
202
203   double computeBadness( size_t i1, size_t i2, size_t i3,
204                          std::vector< PolyVertex > & points,
205                          bool                        checkArea = false )
206   {
207     if ( checkArea )
208     {
209       points[ i2 ]._prev = & points[ i1 ];
210       points[ i2 ]._next = & points[ i3 ];
211       double a = points[ i2 ].TriaArea();
212       // if ( a < 0 )
213       //   return std::numeric_limits<double>::max();
214       // return 1. / a;
215
216       if ( a < 0 )
217         return 2;
218     }
219     const gp_XY & p1 = points[ i1 ]._xy;
220     const gp_XY & p2 = points[ i2 ]._xy;
221     const gp_XY & p3 = points[ i3 ]._xy;
222     gp_XY vec[3] = { p2 - p1,
223                      p3 - p2,
224                      p1 - p3 };
225     double len[3] = { vec[0].SquareModulus(),
226                       vec[1].SquareModulus(),
227                       vec[2].SquareModulus() };
228     if ( len[0] < gp::Resolution() ||
229          len[1] < gp::Resolution() ||
230          len[2] < gp::Resolution() )
231       return 2;
232
233     double maxCos2 = 0;
234     for ( int i = 0; i < 3; ++i )
235     {
236       int i2 = ( i+1 ) % 3;
237       double dot = -vec[ i ] * vec[ i2 ];
238       if ( dot > 0 )
239         maxCos2 = Max( maxCos2, dot * dot / len[ i ] / len[ i2 ] );
240     }
241     return maxCos2;
242   }
243 };
244
245 //================================================================================
246 /*!
247  * \brief Initialization
248  */
249 //================================================================================
250
251 void Triangulate::PolyVertex::SetNodeAndNext( const SMDS_MeshNode* n,
252                                               PolyVertex&          v,
253                                               size_t               index )
254 {
255   _nxyz.Set( n );
256   _next = &v;
257   v._prev = this;
258   _index = index;
259 }
260 //================================================================================
261 /*!
262  * \brief Remove self from a polygon
263  */
264 //================================================================================
265
266 Triangulate::PolyVertex* Triangulate::PolyVertex::Delete()
267 {
268   _prev->_next = _next;
269   _next->_prev = _prev;
270   return _next;
271 }
272
273 //================================================================================
274 /*!
275  * \brief Return nodes of a triangle
276  */
277 //================================================================================
278
279   void Triangulate::PolyVertex::GetTriaNodes( const SMDS_MeshNode** nodes,
280                                               size_t*               nodeIndices) const
281 {
282   nodes[0] = _prev->_nxyz._node;
283   nodes[1] =  this->_nxyz._node;
284   nodes[2] = _next->_nxyz._node;
285   nodeIndices[0] = _prev->_index;
286   nodeIndices[1] =  this->_index;
287   nodeIndices[2] = _next->_index;
288 }
289
290 //================================================================================
291 /*!
292  * \brief Compute triangle area
293  */
294 //================================================================================
295
296 inline static double Area( const gp_XY& xy0, const gp_XY& xy1, const gp_XY& xy2 )
297 {
298   gp_XY vPrev = xy0 - xy1;
299   gp_XY vNext = xy2 - xy1;
300   return vNext ^ vPrev;
301 }
302
303 //================================================================================
304 /*!
305  * \brief Compute triangle area
306  */
307 //================================================================================
308
309 double Triangulate::PolyVertex::TriaArea() const
310 {
311   return Area( _prev->_xy, this->_xy, _next->_xy );
312 }
313
314 //================================================================================
315 /*!
316  * \brief Check if a vertex is inside a triangle
317  */
318 //================================================================================
319
320 bool Triangulate::PolyVertex::IsInsideTria( const PolyVertex* v )
321 {
322   if ( this ->_nxyz == v->_nxyz ||
323        _prev->_nxyz == v->_nxyz ||
324        _next->_nxyz == v->_nxyz )
325     return false;
326
327   gp_XY p = _prev->_xy - v->_xy;
328   gp_XY t =  this->_xy - v->_xy;
329   gp_XY n = _next->_xy - v->_xy;
330   const double tol = -1e-7;
331   return (( p ^ t ) >= tol &&
332           ( t ^ n ) >= tol &&
333           ( n ^ p ) >= tol );
334   // return ( Area( _prev, this, v ) > 0 &&
335   //          Area( this, _next, v ) > 0 &&
336   //          Area( _next, _prev, v ) > 0 );
337 }
338
339 //================================================================================
340 /*!
341  * \brief Triangulate a polygon. Assure correct orientation for concave polygons
342  */
343 //================================================================================
344
345 bool Triangulate::triangulate( std::vector< const SMDS_MeshNode*>& nodes,
346                                const size_t                        nbNodes)
347 {
348   std::vector< PolyVertex >&    _pv = _data->_pv;
349   std::vector< size_t >& _nodeIndex = _data->_nodeIndex;
350   PolyVertex::PVSet&  _uniqueNodePV = _data->_uniqueNodePV;
351
352   // connect nodes into a ring
353   _pv.resize( nbNodes );
354   for ( size_t i = 1; i < nbNodes; ++i )
355     _pv[i-1].SetNodeAndNext( nodes[i-1], _pv[i], i-1 );
356   _pv[ nbNodes-1 ].SetNodeAndNext( nodes[ nbNodes-1 ], _pv[0], nbNodes-1 );
357
358   // assure correctness of PolyVertex::_index as a node can encounter more than once
359   // within a polygon boundary
360   if ( _optimizer && nbNodes > 4 )
361   {
362     _uniqueNodePV.clear();
363     for ( size_t i = 0; i < nbNodes; ++i )
364     {
365       PolyVertex::PVSet::iterator pv = _uniqueNodePV.insert( &_pv[i] ).first;
366       _pv[i]._index = (*pv)->_index;
367     }
368   }
369
370   // get a polygon normal
371   gp_XYZ normal(0,0,0), p0,v01,v02;
372   p0  = _pv[0]._nxyz;
373   v01 = _pv[1]._nxyz - p0;
374   for ( size_t i = 2; i < nbNodes; ++i )
375   {
376     v02 = _pv[i]._nxyz - p0;
377     normal += v01 ^ v02;
378     v01 = v02;
379   }
380   // project nodes to the found plane
381   gp_Ax2 axes;
382   try {
383     axes = gp_Ax2( p0, normal, v01 );
384   }
385   catch ( Standard_Failure ) {
386     return false;
387   }
388   for ( size_t i = 0; i < nbNodes; ++i )
389   {
390     gp_XYZ p = _pv[i]._nxyz - p0;
391     _pv[i]._xy.SetX( axes.XDirection().XYZ() * p );
392     _pv[i]._xy.SetY( axes.YDirection().XYZ() * p );
393   }
394
395   // compute minimal triangle area
396   double sumArea = 0;
397   for ( size_t i = 0; i < nbNodes; ++i )
398     sumArea += _pv[i].TriaArea();
399   const double minArea = 1e-6 * sumArea / ( nbNodes - 2 );
400
401   // in a loop, find triangles with positive area and having no vertices inside
402   int iN = 0, nbTria = nbNodes - 2;
403   nodes.resize( nbTria * 3 );
404   _nodeIndex.resize( nbTria * 3 );
405   PolyVertex* v = &_pv[0], *vi;
406   int nbVertices = nbNodes, nbBadTria = 0, isGoodTria;
407   while ( nbBadTria < nbVertices )
408   {
409     if (( isGoodTria = v->TriaArea() > minArea ))
410     {
411       for ( vi = v->_next->_next;
412             vi != v->_prev;
413             vi = vi->_next )
414       {
415         if ( v->IsInsideTria( vi ))
416           break;
417       }
418       isGoodTria = ( vi == v->_prev );
419     }
420     if ( isGoodTria )
421     {
422       v->GetTriaNodes( &nodes[ iN ], &_nodeIndex[ iN ] );
423       iN += 3;
424       v = v->Delete();
425       if ( --nbVertices == 3 )
426       {
427         // last triangle remains
428         v->GetTriaNodes( &nodes[ iN ], &_nodeIndex[ iN ] );
429         if ( _optimizer )
430           _optimizer->optimize( nodes, _pv, _nodeIndex );
431         return true;
432       }
433       nbBadTria = 0;
434     }
435     else
436     {
437       v = v->_next;
438       ++nbBadTria;
439     }
440   }
441
442   // the polygon is invalid; add triangles with positive area
443   nbBadTria = 0;
444   while ( nbBadTria < nbVertices )
445   {
446     isGoodTria = v->TriaArea() > minArea;
447     if ( isGoodTria )
448     {
449       v->GetTriaNodes( &nodes[ iN ], &_nodeIndex[ iN ] );
450       iN += 3;
451       v = v->Delete();
452       if ( --nbVertices == 3 )
453       {
454         // last triangle remains
455         v->GetTriaNodes( &nodes[ iN ], &_nodeIndex[ iN ] );
456         return true;
457       }
458       nbBadTria = 0;
459     }
460     else
461     {
462       v = v->_next;
463       ++nbBadTria;
464     }
465   }
466
467   // add all the rest triangles
468   while ( nbVertices >= 3 )
469   {
470     v->GetTriaNodes( &nodes[ iN ], &_nodeIndex[ iN ] );
471     iN += 3;
472     v = v->Delete();
473     --nbVertices;
474   }
475
476   return true;
477
478 } // triangulate()
479
480 //================================================================================
481 /*!
482  * \brief Constructor
483  */
484 //================================================================================
485
486 Triangulate::Triangulate( bool optimize ): _optimizer(0)
487 {
488   _data = new Data;
489   if ( optimize )
490     _optimizer = new Optimizer;
491 }
492
493 //================================================================================
494 /*!
495  * \brief Destructor
496  */
497 //================================================================================
498
499 Triangulate::~Triangulate()
500 {
501   delete _data;
502   delete _optimizer;
503   _optimizer = 0;
504 }
505
506 //================================================================================
507 /*!
508  * \brief Return nb triangles in a decomposed mesh face
509  *  \retval int - number of triangles
510  */
511 //================================================================================
512
513 int Triangulate::GetNbTriangles( const SMDS_MeshElement* face )
514 {
515   // WARNING: counting triangles must be coherent with GetTriangles()
516   switch ( face->GetEntityType() )
517   {
518   case SMDSEntity_BiQuad_Triangle:
519   case SMDSEntity_BiQuad_Quadrangle:
520     return face->NbNodes() - 1;
521     // case SMDSEntity_Triangle:
522     // case SMDSEntity_Quad_Triangle:
523     // case SMDSEntity_Quadrangle:
524     // case SMDSEntity_Quad_Quadrangle:
525     // case SMDSEntity_Polygon:
526     // case SMDSEntity_Quad_Polygon:
527   default:
528     return face->NbNodes() - 2;
529   }
530   return 0;
531 }
532
533 //================================================================================
534 /*!
535  * \brief Decompose a mesh face into triangles
536  *  \retval int - number of triangles
537  */
538 //================================================================================
539
540 int Triangulate::GetTriangles( const SMDS_MeshElement*             face,
541                                std::vector< const SMDS_MeshNode*>& nodes)
542 {
543   if ( face->GetType() != SMDSAbs_Face )
544     return 0;
545
546   // WARNING: decomposing into triangles must be coherent with getNbTriangles()
547   int nbTria, i = 0, nbNodes = face->NbNodes();
548   SMDS_NodeIteratorPtr nIt = face->interlacedNodesIterator();
549   nodes.resize( nbNodes * 3 );
550   nodes[ i++ ] = nIt->next();
551   nodes[ i++ ] = nIt->next();
552
553   const SMDSAbs_EntityType type = face->GetEntityType();
554   switch ( type )
555   {
556   case SMDSEntity_BiQuad_Triangle:
557   case SMDSEntity_BiQuad_Quadrangle:
558
559     nbTria = ( type == SMDSEntity_BiQuad_Triangle ) ? 6 : 8;
560     nodes[ i++ ] = face->GetNode( nbTria );
561     for ( i = 3; i < 3*(nbTria-1); i += 3 )
562     {
563       nodes[ i+0 ] = nodes[ i-2 ];
564       nodes[ i+1 ] = nIt->next();
565       nodes[ i+2 ] = nodes[ 2 ];
566     }
567     nodes[ i+0 ] = nodes[ i-2 ];
568     nodes[ i+1 ] = nodes[ 0 ];
569     nodes[ i+2 ] = nodes[ 2 ];
570     break;
571
572   case SMDSEntity_Triangle:
573
574     nbTria = 1;
575     nodes[ i++ ] = nIt->next();
576     break;
577
578   default:
579
580     // case SMDSEntity_Quad_Triangle:
581     // case SMDSEntity_Quadrangle:
582     // case SMDSEntity_Quad_Quadrangle:
583     // case SMDSEntity_Polygon:
584     // case SMDSEntity_Quad_Polygon:
585
586     nbTria = nbNodes - 2;
587     while ( nIt->more() )
588       nodes[ i++ ] = nIt->next();
589
590     if ( nbTria > 1 && !triangulate( nodes, nbNodes ))
591     {
592       nIt = face->interlacedNodesIterator();
593       nodes[ 0 ] = nIt->next();
594       nodes[ 1 ] = nIt->next();
595       nodes[ 2 ] = nIt->next();
596       for ( i = 3; i < 3*nbTria; i += 3 )
597       {
598         nodes[ i+0 ] = nodes[ 0 ];
599         nodes[ i+1 ] = nodes[ i-1 ];
600         nodes[ i+2 ] = nIt->next();
601       }
602     }
603   }
604
605   return nbTria;
606 }