Salome HOME
d54c0ca5c5174c67937945de3e9ef8d305f3c951
[tools/medcoupling.git] / src / INTERP_KERNEL / TransformedTriangleMath.cxx
1 // Copyright (C) 2007-2020  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "TransformedTriangle.hxx"
21 #include <iostream>
22 #include <fstream>
23 #include <cassert>
24 #include <cmath>
25 #include <limits>
26 #include <map>
27 #include <utility>
28
29 #include "VectorUtils.hxx"
30
31 namespace INTERP_KERNEL
32 {
33   
34   // ----------------------------------------------------------------------------------
35   //  Tables                                                       
36   // ----------------------------------------------------------------------------------
37
38   /// Table with first coordinate (a) used to calculate double product c^pq_ab = p_a * q_b - p_b * q_a (index to be used : DoubleProduct)
39   const int TransformedTriangle::DP_OFFSET_1[8] = {1, 2, 0, 2, 0, 1, 4, 1};
40
41   /// Table with second coordinate (b) used to calculate double product c^pq_ab = p_a * q_b - p_b * q_a (index to be used : DoubleProduct)
42   const int TransformedTriangle::DP_OFFSET_2[8] = {2, 0, 1, 3, 3, 3, 0, 4};
43
44   /// Coordinates used to calculate triple products by the expanding one of the three rows of the determinant (index to be used : 3*Corner + row)
45   const int TransformedTriangle::COORDINATE_FOR_DETERMINANT_EXPANSION[12] =
46     {
47       // row 1, 2, 3
48       0, 1, 2, // O
49       3, 1, 2, // X
50       0, 3, 2, // Y
51       0, 1, 3  // Z
52     };
53   
54   /// Double products used to calculate triple products by expanding one of the three rows of the determinant (index to be used : 3*Corner + row)
55   const TransformedTriangle::DoubleProduct TransformedTriangle::DP_FOR_DETERMINANT_EXPANSION[12] = 
56     {
57       // row 1, 2, 3
58       C_YZ, C_ZX, C_XY, // O
59       C_YZ, C_ZH, C_YH, // X
60       C_ZH, C_ZX, C_XH, // Y
61       C_YH, C_XH, C_XY  // Z
62     };
63   
64   /// The machine epsilon, used in precision corrections
65   const long double TransformedTriangle::MACH_EPS = std::numeric_limits<double>::epsilon();
66   
67   /// 4.0 * the machine epsilon, represents the precision of multiplication when performing corrections corrections ( f in Grandy )
68   const long double TransformedTriangle::MULT_PREC_F = 4.0 * TransformedTriangle::MACH_EPS;
69
70   /// Threshold for resetting double and triple products to zero; ( F / f in Grandy )
71   const long double TransformedTriangle::THRESHOLD_F = 500.0;
72
73   /// Threshold for what is considered a small enough angle to warrant correction of triple products by Grandy, [57]
74   const double TransformedTriangle::TRIPLE_PRODUCT_ANGLE_THRESHOLD = 0.1;
75
76
77   // after transformation to the U-space, coordinates are inaccurate
78   // small variations around zero should not be taken into account
79   void TransformedTriangle::resetNearZeroCoordinates()
80   {
81     for (int i=0; i<15; i++)
82       if (fabs(_coords[i])<TransformedTriangle::MACH_EPS*40.0) _coords[i]=0.0;
83   }
84   
85   // ----------------------------------------------------------------------------------
86   //  Double and triple product calculations                           
87   // ----------------------------------------------------------------------------------
88   
89   /**
90    * Pre-calculates all double products for this triangle, and stores
91    * them internally. This method makes compensation for precision errors,
92    * and it is thus the "stable" double products that are stored.
93    *
94    */
95   void TransformedTriangle::preCalculateDoubleProducts(void)
96   {
97     if(_is_double_products_calculated)
98       return;
99
100     // -- calculate all unstable double products -- store in _doubleProducts
101     for(TriSegment seg = PQ ; seg <= RP ; seg = TriSegment(seg + 1))
102       {
103         for(DoubleProduct dp = C_YZ ; dp <= C_10 ; dp = DoubleProduct(dp + 1))
104           _doubleProducts[8*seg + dp] = calcUnstableC(seg, dp);
105       }
106
107     std::map<double, TetraCorner> distances;
108
109     // -- (1) for each segment : check that double products satisfy Grandy, [46]
110     // -- and make corrections if not
111     for(TriSegment seg = PQ ; seg <= RP ; seg = TriSegment(seg + 1))
112       {
113         if(!areDoubleProductsConsistent(seg))
114           {
115             LOG(4, "inconsistent! ");
116             for(TetraCorner corner = O ; corner <= Z ; corner = TetraCorner(corner + 1))
117               {
118                 // calculate distance corner - segment axis
119                 const double dist = calculateDistanceCornerSegment(corner, seg);
120                 distances.insert( std::make_pair( dist, corner ) );
121               }
122
123             // first element -> minimum distance
124             const TetraCorner minCorner = distances.begin()->second;
125             resetDoubleProducts(seg, minCorner);
126             distances.clear();
127           }
128       }
129   
130     // -- (2) check that each double product satisfies Grandy, [47], else set to 0
131     for(TriSegment seg = PQ ; seg <= RP ; seg = TriSegment(seg + 1))
132       {
133         for(DoubleProduct dp = C_YZ ; dp <=  C_10 ; dp = DoubleProduct(dp + 1))
134           {
135             // find the points of the triangle
136             // 0 -> P, 1 -> Q, 2 -> R 
137             const int pt1 = seg;
138             const int pt2 = (seg + 1) % 3;
139
140             // find offsets
141             const int off1 = DP_OFFSET_1[dp];
142             const int off2 = DP_OFFSET_2[dp];
143
144             const double term1 = _coords[5*pt1 + off1] * _coords[5*pt2 + off2]; 
145             const double term2 = _coords[5*pt1 + off2] * _coords[5*pt2 + off1];
146
147             const long double delta = MULT_PREC_F * ( std::fabs(term1) + std::fabs(term2) );
148          
149             if( epsilonEqual(_doubleProducts[8*seg + dp], 0.0, (double)(THRESHOLD_F * delta)))
150               {
151                 // debug output
152 #if LOG_LEVEL >= 5
153                 if(_doubleProducts[8*seg + dp] != 0.0)
154                   {
155                     LOG(5, "Double product for (seg,dp) = (" << seg << ", " << dp << ") = " );
156                     LOG(5, std::fabs(_doubleProducts[8*seg + dp]) << " is imprecise, reset to 0.0" );
157                   }
158 #endif 
159
160
161                 _doubleProducts[8*seg + dp] = 0.0;
162                 
163               }
164           }
165       }
166     
167     _is_double_products_calculated = true;
168   }
169
170   /**
171    * Checks if the double products for a given segment are consistent, as defined by
172    * Grandy, [46]. 
173    *
174    * @param   seg Segment for which to check consistency of double products
175    * @return  true if the double products are consistent, false if not
176    */
177   bool TransformedTriangle::areDoubleProductsConsistent(const TriSegment seg) const
178   {
179     const double term1 = _doubleProducts[8*seg + C_YZ] * _doubleProducts[8*seg + C_XH];
180     const double term2 = _doubleProducts[8*seg + C_ZX] * _doubleProducts[8*seg + C_YH];
181     const double term3 = _doubleProducts[8*seg + C_XY] * _doubleProducts[8*seg + C_ZH];
182
183     LOG(2, "for seg " << seg << " consistency " << term1 + term2 + term3 );
184     LOG(2, "term1 :" << term1 << " term2 :" << term2 << " term3: " << term3 );
185
186     const int num_zero = (term1 == 0.0 ? 1 : 0) + (term2 == 0.0 ? 1 : 0) + (term3 == 0.0 ? 1 : 0);
187     const int num_neg = (term1 < 0.0 ? 1 : 0) + (term2 < 0.0 ? 1 : 0) + (term3 < 0.0 ? 1 : 0);
188     const int num_pos = (term1 > 0.0 ? 1 : 0) + (term2 > 0.0 ? 1 : 0) + (term3 > 0.0 ? 1 : 0);
189
190     assert( num_zero + num_neg + num_pos == 3 );
191
192     // calculated geometry is inconsistent if we have one of the following cases
193     // * one term zero and the other two of the same sign
194     // * two terms zero
195     // * all terms positive
196     // * all terms negative
197     if(((num_zero == 1 && num_neg != 1) || num_zero == 2 || (num_neg == 0 && num_zero != 3) || num_neg == 3 ))
198       {
199         LOG(4, "inconsistent dp found" );
200       }
201     return !((num_zero == 1 && num_neg != 1) || num_zero == 2 || (num_neg == 0 && num_zero != 3) || num_neg == 3 );
202
203   }
204
205   /**
206    * Calculate the shortest distance between a tetrahedron corner and a triangle segment.
207    * 
208    * @param  corner corner of the tetrahedron
209    * @param  seg    segment of the triangle
210    * @return shortest distance from the corner to the segment
211    */
212   double TransformedTriangle::calculateDistanceCornerSegment(const TetraCorner corner, const TriSegment seg) const
213   {
214     // NB uses fact that TriSegment <=> TriCorner that is first point of segment (PQ <=> P)
215     const TriCorner ptP_idx = TriCorner(seg);
216     const TriCorner ptQ_idx = TriCorner( (seg + 1) % 3);
217     
218     const double ptP[3] = { _coords[5*ptP_idx], _coords[5*ptP_idx + 1], _coords[5*ptP_idx + 2]  };
219     const double ptQ[3] = { _coords[5*ptQ_idx], _coords[5*ptQ_idx + 1], _coords[5*ptQ_idx + 2]  };
220     
221     // coordinates of corner
222     const double ptTetCorner[3] = 
223       { 
224         COORDS_TET_CORNER[3*corner    ],
225         COORDS_TET_CORNER[3*corner + 1],
226         COORDS_TET_CORNER[3*corner + 2]
227       };
228     
229     // dist^2 = ( PQ x CP )^2 / |PQ|^2 where C is the corner point
230     
231     // difference vectors
232     const double diffPQ[3] = { ptQ[0] - ptP[0], ptQ[1] - ptP[1], ptQ[2] - ptP[2] };
233     const double diffCornerP[3] = { ptP[0] - ptTetCorner[0], ptP[1] - ptTetCorner[1], ptP[2] - ptTetCorner[2] };
234     
235     // cross product of difference vectors
236     double crossProd[3];
237     cross(diffPQ, diffCornerP, crossProd);
238     
239     const double cross_squared = dot(crossProd, crossProd);
240     const double norm_diffPQ_squared = dot(diffPQ, diffPQ);
241     
242     assert(norm_diffPQ_squared != 0.0);
243     
244     return cross_squared / norm_diffPQ_squared;
245   }
246
247   /**
248    * Pre-calculates all triple products for the tetrahedron with respect to
249    * this triangle, and stores them internally. This method takes into account
250    * the problem of errors due to cancellation.
251    *
252    */
253   void TransformedTriangle::preCalculateTripleProducts(void)
254   {
255     if(_is_triple_products_calculated)
256       {
257         return;
258       }
259
260     // find edge / row to use -> that whose edge makes the smallest angle to the triangle
261     // use a map to find the minimum
262     std::map<double, int> anglesForRows;
263
264     LOG(4, "Precalculating triple products" );
265     for(TetraCorner corner = O ; corner <= Z ; corner = TetraCorner(corner + 1))
266       {
267         LOG(6, "- Triple product for corner " << corner );
268
269         for(int row = 1 ; row < 4 ; ++row) 
270           {
271             const DoubleProduct dp = DP_FOR_DETERMINANT_EXPANSION[3*corner + (row - 1)];
272
273             // get edge by using correspondence between Double Product and Edge
274             TetraEdge edge = TetraEdge(dp);
275            
276             // use edge only if it is surrounded by the surface
277             if( _triangleSurroundsEdgeCache[edge] )
278                 {
279                   // -- calculate angle between edge and PQR
280                   const double angle = calculateAngleEdgeTriangle(edge);
281                   anglesForRows.insert(std::make_pair(angle, row));              
282                 }
283           }
284        
285         if(anglesForRows.size() != 0) // we have found a good row
286           {
287             const double minAngle = anglesForRows.begin()->first;
288             const int minRow = anglesForRows.begin()->second;
289
290             if(minAngle < TRIPLE_PRODUCT_ANGLE_THRESHOLD)
291               {
292                 _tripleProducts[corner] = calcTByDevelopingRow(corner, minRow, true);
293               } 
294             else 
295               {
296                 _tripleProducts[corner] = calcTByDevelopingRow(corner, minRow, false);
297               }
298             _validTP[corner] = true;
299           }
300         else
301           {
302             // this value will not be used
303             // we set it to whatever
304             LOG(6, "Triple product not calculated for corner " << corner );
305             _tripleProducts[corner] = -3.14159265;
306             _validTP[corner] = false;
307
308           }
309         anglesForRows.clear();
310
311       }
312
313     _is_triple_products_calculated = true;
314   }
315
316   /**
317    * Calculates the angle between an edge of the tetrahedron and the triangle
318    *
319    * @param  edge edge of the tetrahedron
320    * @return angle between triangle and edge
321    */
322   double TransformedTriangle::calculateAngleEdgeTriangle(const TetraEdge edge) const
323   {
324     // find normal to PQR - cross PQ and PR
325     const double pq[3] = 
326       { 
327         _coords[5*Q]     - _coords[5*P], 
328         _coords[5*Q + 1] - _coords[5*P + 1],
329         _coords[5*Q + 2] - _coords[5*P + 2]
330       };
331     
332     const double pr[3] = 
333       { 
334         _coords[5*R]     - _coords[5*P], 
335         _coords[5*R + 1] - _coords[5*P + 1],
336         _coords[5*R + 2] - _coords[5*P + 2]
337       };
338     
339     double normal[3];
340
341     cross(pq, pr, normal);
342     
343     static const double EDGE_VECTORS[18] =
344       {
345         1.0, 0.0, 0.0, // OX
346         0.0, 1.0, 0.0, // OY
347         0.0, 0.0, 1.0, // OZ
348         -1.0, 1.0, 0.0, // XY
349         0.0,-1.0, 1.0, // YZ
350         1.0, 0.0,-1.0 // ZX
351       };
352     
353     const double edgeVec[3] = { 
354       EDGE_VECTORS[3*edge],
355       EDGE_VECTORS[3*edge + 1],
356       EDGE_VECTORS[3*edge + 2],
357     };
358
359     //return angleBetweenVectors(normal, edgeVec);
360
361     const double lenNormal = norm(normal);
362     const double lenEdgeVec = norm(edgeVec);
363     const double dotProd = dot(normal, edgeVec);
364     
365     //? is this more stable? -> no subtraction
366     //    return asin( dotProd / ( lenNormal * lenEdgeVec ) ) + 3.141592625358979 / 2.0;
367     double tmp=dotProd / ( lenNormal * lenEdgeVec );
368     tmp=std::max(tmp,-1.);
369     tmp=std::min(tmp,1.);
370     return atan(1.0)*4.0 - acos(tmp);
371
372   }
373
374   /**
375    * Calculates triple product associated with the given corner of tetrahedron, developing 
376    * the determinant by the given row. The triple product gives the signed volume of 
377    * the tetrahedron between this corner and the triangle PQR. If the flag project is true, 
378    * one coordinate is projected out in order to eliminate errors in the intersection point
379    * calculation due to cancellation.
380    * 
381    * @pre            double products have already been calculated
382    * @param corner   corner for which the triple product is calculated
383    * @param row      row (1 <= row <= 3) used to calculate the determinant
384    * @param project  indicates whether or not to perform projection as inidicated in Grandy, p.446
385    * @return        triple product associated with corner (see Grandy, [50]-[52])
386    */
387   double TransformedTriangle::calcTByDevelopingRow(const TetraCorner corner, const int row, const bool project) const
388   {
389     
390     // OVERVIEW OF CALCULATION
391     // --- sign before the determinant
392     // the sign used depends on the sign in front of the triple product (Grandy, [15]),
393     // and the convention used in the definition of the double products
394   
395     // the sign in front of the determinant gives the following schema for the three terms (I): 
396     // corner/row    1    2   3
397     // O (sign:+)    +    -   +
398     // X (sign:-)    -    +   -
399     // Y (sign:-)    -    +   -
400     // Z (sign:-)    -    +   -
401
402     // the 2x2 determinants are the following (C_AB <=> A_p*B_q - B_p*A_q, etc)
403     // corner/row    1       2     3
404     // O (sign:+)   C_YZ   C_XZ  C_XY
405     // X (sign:-)   C_YZ   C_HZ  C_HY
406     // Y (sign:-)   C_HZ   C_XZ  C_XH
407     // Z (sign:-)   C_YH   C_XH  C_XY
408
409     // these are represented in DP_FOR_DETERMINANT_EXPANSION,
410     // except for the fact that certain double products are inversed (C_AB <-> C_BA)
411
412     // comparing with the DOUBLE_PRODUCTS and using the fact that C_AB = -C_BA
413     // we deduce the following schema (II) :
414     // corner/row    1    2   3
415     // O (sign:+)    +    -   +
416     // X (sign:-)    +    -   -
417     // Y (sign:-)    -    -   +
418     // Z (sign:-)    +    +   +
419
420     // comparing the two schemas (I) and (II) gives us the following matrix of signs,
421     // putting 1 when the signs in (I) and (II) are equal and -1 when they are different :
422
423     static const int SIGNS[12] = 
424       {
425         1, 1, 1,
426         -1,-1, 1,
427         1,-1,-1,
428         -1, 1,-1
429       };
430
431     // find the offsets of the rows of the determinant
432     const int offset = COORDINATE_FOR_DETERMINANT_EXPANSION[3 * corner + (row - 1)];
433   
434     const DoubleProduct dp = DP_FOR_DETERMINANT_EXPANSION[3 * corner + (row - 1)];
435
436     const int sign = SIGNS[3 * corner + (row - 1)];
437
438     const double cQR = calcStableC(QR, dp);
439     const double cRP = calcStableC(RP, dp);
440     const double cPQ = calcStableC(PQ, dp);
441
442     double alpha = 0.0;
443     
444     // coordinate to use for projection (Grandy, [57]) with edges
445     // OX, OY, OZ, XY, YZ, ZX in order : 
446     // (y, z, x, h, h, h)
447     // for the first three we could also use {2, 0, 1}
448     static const int PROJECTION_COORDS[6] = { 1, 2, 0, 3, 3, 3 } ;
449     
450     const int coord = PROJECTION_COORDS[ dp ];
451     
452     // coordinate values for P, Q and R
453     const double coordValues[3] = { _coords[5*P + coord], _coords[5*Q + coord], _coords[5*R + coord] };
454     
455     if(project)
456       {
457         // products coordinate values with corresponding double product
458         const double coordDPProd[3] = { coordValues[0] * cQR, coordValues[1] * cRP, coordValues[2] * cPQ };
459        
460         const double sumDPProd = coordDPProd[0] + coordDPProd[1] + coordDPProd[2];
461         const double sumDPProdSq = dot(coordDPProd, coordDPProd);
462
463         //       alpha = sumDPProd / sumDPProdSq;
464         alpha = (sumDPProdSq != 0.0) ? sumDPProd / sumDPProdSq : 0.0;
465       }
466
467     const double cQRbar = cQR * (1.0 - alpha * coordValues[0] * cQR);
468     const double cRPbar = cRP * (1.0 - alpha * coordValues[1] * cRP);
469     const double cPQbar = cPQ * (1.0 - alpha * coordValues[2] * cPQ);
470
471     // check sign of barred products - should not change
472     //    assert(cQRbar * cQR >= 0.0);
473     //assert(cRPbar * cRP >= 0.0);
474     //assert(cPQbar * cPQ >= 0.0);
475
476     const double p_term = _coords[5*P + offset] * cQRbar;
477     const double q_term = _coords[5*Q + offset] * cRPbar;
478     const double r_term = _coords[5*R + offset] * cPQbar;
479
480     // check that we are not so close to zero that numerical errors could take over, 
481     // otherwise reset to zero (cf Grandy, p. 446)
482 #ifdef FIXED_DELTA
483     const double delta = FIXED_DELTA;
484 #else
485     const long double delta = MULT_PREC_F * (std::fabs(p_term) + std::fabs(q_term) + std::fabs(r_term));
486 #endif
487
488     if( epsilonEqual( p_term + q_term + r_term, 0.0, (double)(THRESHOLD_F * delta)) )
489       {
490         LOG(4, "Reset imprecise triple product for corner " << corner << " to zero" ); 
491         return 0.0;
492       }
493     else
494       {
495         // NB : using plus also for the middle term compensates for a double product
496         // which is inversely ordered
497         LOG(6, "Triple product for corner " << corner << ", row " << row << " = " << sign*( p_term + q_term + r_term ) );
498         return sign*( p_term + q_term + r_term );
499       }
500
501   }
502
503 }