Salome HOME
[TetraIntersect] Avoid multiple divisions in calculatePolygonBarycenter
[tools/medcoupling.git] / src / INTERP_KERNEL / TransformedTriangle.hxx
1 // Copyright (C) 2007-2024  CEA, EDF
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 #ifndef __TRANSFORMED_TRIANGLE_HXX__
21 #define __TRANSFORMED_TRIANGLE_HXX__
22
23 #include "INTERPKERNELDefines.hxx"
24 #include "VectorUtils.hxx"
25 #include "assert.h"
26
27 #include <vector>
28
29 // Levels : 
30 // 1 - overview of algorithm + volume result
31 // 2 - algorithm detail
32 // 3 - intersection polygon results detail
33 // 4 - intersection polygon search detail
34 // higher -> misc. gory details of calculation
35
36 #include "Log.hxx"
37
38 #ifdef WIN32
39 #pragma warning(disable:4251)
40 #endif
41
42 namespace INTERP_TEST
43 {
44   class TransformedTriangleTest;
45   class TransformedTriangleIntersectTest;
46 }
47
48
49 namespace INTERP_KERNEL
50 {
51   class TetraAffineTransform;
52
53   /** \class TransformedTriangle
54    * \brief Class representing one of the faces of the triangulated source polyhedron after having been transformed
55    * with the affine transform that takes the target tetrahedron to the unit tetrahedron. It contains the
56    * logic for calculating the volume of intersection between the triangle and the unit tetrahedron.
57    * 
58    * \see TransformedTriangle.hxx 
59    *
60    * Reference : J. Grandy, "Conservative Remapping and Region Overlays by Intersecting Arbitrary Polyhedra", 
61    *             Journal of Computational Physics (1999)
62    *
63    */
64
65   /**
66    * OVERVIEW of how the class works : (details can be found in the documentation of each method)
67    * 
68    * Constructor : 
69    * The constructor takes as arguments three pointers to double[3] vectors holding the transformed
70    * coordinates of the corners of the triangle. It copies their coordinates and then proceeds to pre-calculating certain
71    * entities used in the intersection calculation : the double products, triple products and the values of the function E
72    * (Grandy, [53]).
73    * It is also at this point in constructor that:
74    *  - the special case of PQR included in the XYZ plane is treated
75    *  - the inconsistencies between double products/triple products computation is handled
76    *
77    * calculateIntersectionVolume() : 
78    * This is the only method in the public interface. It calculates the volume under the intersection polygons
79    * between the triangle and the unit tetrahedron, as described in Grandy, pp. 435-447. It does this by first calculating the
80    * intersection polygons A and B, with the method calculateIntersectionPolygons(). It then calculates the barycenter of each
81    * polygon in calculatePolygonBarycenter(), and sorts their points in a circular order around the barycenter in 
82    * sortIntersecionPolygon(). The sorting is done with STL sort, using the order defined in the class 
83    * ProjectedCentralCircularSortOrder. The volume under each polygon is then calculated with calculateVolumeUnderPolygon(), which
84    * implements formula [34] in Grandy.
85    *
86    * calculateIntersectionPolygons() :
87    * This method goes through all the possible ways in which the triangle can intersect the tetrahedron and tests for these 
88    * types of intersections in accordance with the formulas described in Grandy. These tests are implemented in the test* - methods.
89    *    The formulas in the article are stated for one case each only, while the calculation must take into account all cases. 
90    * To this end, a number of tables, implemented as static const arrays of different types, are used. The tables 
91    * mainly contain values of the different enumeration types described at the beginning of the class interface. For example, 
92    * the formula Grandy gives for the segment-halfstrip intersection tests ([30]) is for use with the halfstrip above the zx edge. 
93    * For the other two halfstrips (above the xy and yz edges), other double products are used, which 
94    * are stored in the table DP_FOR_HALFSTRIP_INTERSECTION. This allows us to treat
95    * all the edges equally, avoiding switch() - statements. It is the careful choice of order of the enumeration types that makes this
96    * possible. Notably, there is a correspondence between the TetraEdge type and the DoubleProduct type (see Grandy, table III) that
97    * is used throughout the code, permitting statements such as DoubleProduct(some_edge) to work.
98    *    When an intersection point has been detected it is calculated with a corresponding calc* - method in the cases where it
99    * is not known directly. It is then added to the polygon A and/or B as necessary.
100    *
101    */
102   class INTERPKERNEL_EXPORT TransformedTriangle
103   {
104  
105
106   public:
107     friend class INTERP_TEST::TransformedTriangleIntersectTest;
108     friend class INTERP_TEST::TransformedTriangleTest;
109     /*
110      * Enumerations representing the different geometric elements of the unit tetrahedron
111      * and the triangle. The end element, NO_* gives the number of elements in the enumeration
112      * and can be used as end element in loops.
113      */
114
115     /// Corners of tetrahedron
116     enum TetraCorner { O = 0, X, Y, Z, NO_TET_CORNER };
117
118     /// Edges of tetrahedron
119     enum TetraEdge { OX = 0, OY, OZ, XY, YZ, ZX, H01, H10, NO_TET_EDGE };
120
121     /// Facets (faces) of tetrahedron
122     enum TetraFacet { OYZ = 0, OZX, OXY, XYZ, NO_TET_FACET };
123
124     /// Corners of triangle
125     enum TriCorner { P = 0, Q, R, NO_TRI_CORNER };
126     
127     /// Segments (edges) of triangle
128     enum TriSegment { PQ = 0, QR, RP, NO_TRI_SEGMENT };
129     
130     /// Intersection polygons
131     enum IntersectionPolygon{ A = 0, B, NO_INTERSECTION_POLYGONS };
132
133     /// Double products
134     /// NB : order corresponds to TetraEdges (Grandy, table III)
135     enum DoubleProduct { C_YZ = 0, C_ZX, C_XY, C_ZH, C_XH, C_YH, C_01, C_10, NO_DP };
136
137     TransformedTriangle(double* p, double* q, double* r); 
138     ~TransformedTriangle();
139
140     double calculateIntersectionVolume(); 
141     double calculateIntersectionSurface(TetraAffineTransform* tat);
142     void dumpCoords() const;
143
144     // Queries of member values used by UnitTetraIntersectionBary
145     const double* getCorner(TriCorner corner) const { return _coords + 5*corner; }
146     const std::vector<double*>& getPolygonA() const { return _polygonA; }
147     double getVolume() const { return _volume; }
148
149   protected:
150     TransformedTriangle() { }
151
152     // ----------------------------------------------------------------------------------
153     //  High-level methods called directly by calculateIntersectionVolume()     
154     // ----------------------------------------------------------------------------------
155     void calculateIntersectionAndProjectionPolygons();
156     void calculatePolygonBarycenter(const IntersectionPolygon poly, double* barycenter); 
157     void sortIntersectionPolygon(const IntersectionPolygon poly, const double* barycenter); 
158     double calculateVolumeUnderPolygon(IntersectionPolygon poly, const double* barycenter); 
159
160     // ----------------------------------------------------------------------------------
161     //  High-level methods called directly by calculateIntersectionSurface()
162     // ----------------------------------------------------------------------------------
163     void calculateIntersectionPolygon();
164     double calculateSurfacePolygon();
165
166     // ----------------------------------------------------------------------------------
167     //  Detection of degenerate triangles  
168     // ----------------------------------------------------------------------------------
169     bool isTriangleInPlaneOfFacet(const TetraFacet facet) const;
170     bool isTriangleParallelToFacet(const TetraFacet facet) const;
171     int isTriangleInclinedToFacet(const TetraFacet facet) const;
172     bool isTriangleBelowTetraeder() const;
173
174     // ----------------------------------------------------------------------------------
175     //  Intersection test methods and intersection point calculations           
176     // ----------------------------------------------------------------------------------
177     inline bool testSurfaceEdgeIntersection(const TetraEdge edge) const; 
178     void calcIntersectionPtSurfaceEdge(const TetraEdge edge, double* pt) const;  
179     inline bool testSegmentFacetIntersection(const TriSegment seg, const TetraFacet facet) const; 
180     void calcIntersectionPtSegmentFacet(const TriSegment seg, const TetraFacet facet, double* pt) const;  
181     bool testSegmentEdgeIntersection(const TriSegment seg, const TetraEdge edge) const; 
182     void calcIntersectionPtSegmentEdge(const TriSegment seg, const TetraEdge edge, double* pt) const ; 
183     bool testSegmentCornerIntersection(const TriSegment seg, const TetraCorner corner) const ;
184     inline bool testSurfaceRayIntersection(const TetraCorner corner) const;
185     bool testSegmentHalfstripIntersection(const TriSegment seg, const TetraEdge edg);
186     void calcIntersectionPtSegmentHalfstrip(const TriSegment seg, const TetraEdge edge, double* pt) const;
187     bool testSegmentRayIntersection(const TriSegment seg, const TetraCorner corner) const;
188     inline bool testCornerInTetrahedron(const TriCorner corner) const;
189     inline bool testCornerOnXYZFacet(const TriCorner corner) const;
190     inline bool testCornerAboveXYZFacet(const TriCorner corner) const;
191
192     // ----------------------------------------------------------------------------------
193     //  Utility methods used in intersection tests                       
194     // ----------------------------------------------------------------------------------
195     bool testTriangleSurroundsEdge(const TetraEdge edge) const;
196     inline bool testEdgeIntersectsTriangle(const TetraEdge edge) const;
197     inline bool testFacetSurroundsSegment(const TriSegment seg, const TetraFacet facet) const;
198     inline bool testSegmentIntersectsFacet(const TriSegment seg, const TetraFacet facet) const;
199     bool testSegmentIntersectsHPlane(const TriSegment seg) const;
200     bool testSurfaceAboveCorner(const TetraCorner corner) const;
201     bool testTriangleSurroundsRay(const TetraCorner corner) const;
202
203     // ----------------------------------------------------------------------------------
204     //  Double and triple product calculations                           
205     // ----------------------------------------------------------------------------------
206     void handleDegenerateCases();
207     bool areDoubleProductsConsistent(const TriSegment seg) const;
208     void preCalculateDoubleProducts();
209     inline void resetDoubleProducts(const TriSegment seg, const TetraCorner corner);
210     double calculateDistanceCornerSegment(const TetraCorner corner, const TriSegment seg) const;
211     void preCalculateTripleProducts();
212     double calculateAngleEdgeTriangle(const TetraEdge edge) const;
213     inline double calcStableC(const TriSegment seg, const DoubleProduct dp) const;
214     inline double calcStableT(const TetraCorner corner) const;
215     inline double calcUnstableC(const TriSegment seg, const DoubleProduct dp, double & delta) const;
216     double calcTByDevelopingRow(const TetraCorner corner, const int row, const bool project) const;
217
218     // ----------------------------------------------------------------------------------
219     // Debug
220     // ----------------------------------------------------------------------------------
221     inline const std::string& strTC(TetraCorner tc) const;
222     inline const std::string& strTE(TetraEdge te) const;
223     inline const std::string& strTF(TetraFacet tf) const;
224     inline const std::string& strTriC(TriCorner tc) const;
225     inline const std::string& strTriS(TriSegment tc) const;
226
227     // ----------------------------------------------------------------------------------
228     //  Member variables                                                 
229     // ----------------------------------------------------------------------------------
230   protected:
231
232     /// Array holding the coordinates of the triangle's three corners
233     /// order : 
234     /// [ p_x, p_y, p_z, p_h, p_H, q_x, q_y, q_z, q_h, q_H, r_x, r_y, r_z, r_h, r_H ]
235     double _coords[15];
236     
237     /// Flag showing whether the double products have been calculated yet
238     bool _is_double_products_calculated;
239
240     /// Flag showing whether the triple products have been calculated yet
241     bool _is_triple_products_calculated; 
242
243     /// Array containing the 24 double products.
244     /// order : c^PQ_YZ, ... ,cPQ_10, ... c^QR_YZ, ... c^RP_YZ
245     /// following order in enumeration DoubleProduct
246     double _doubleProducts[24];
247
248     double _deltas[24];
249
250     /// Array containing the 4 triple products.
251     /// order : t_O, t_X, t_Y, t_Z
252     /// For example t_O represent the signed volume of the tetrahedron OPQR, and is positive if PQR is oriented clockwise
253     //  when seen from the vertex O.
254     double _tripleProducts[4];
255
256     /// Vector holding the points of the intersection polygon A.
257     /// these points are allocated in calculateIntersectionPolygons() and liberated in the destructor
258     std::vector<double*> _polygonA;
259     
260     /// Vector holding the points of the intersection polygon B.
261     /// These points are allocated in calculateIntersectionPolygons() and liberated in the destructor
262     std::vector<double*> _polygonB;
263     
264     /// Array holding the coordinates of the barycenter of the polygon A
265     /// This point is calculated in calculatePolygonBarycenter
266     double _barycenterA[3];
267
268     /// Array holding the coordinates of the barycenter of the polygon B
269     /// This point is calculated in calculatePolygonBarycenter
270     //double _barycenterB[3];
271
272     /// Array of flags indicating which of the four triple products have been correctly calculated.
273     /// Used for asserts in debug mode
274     bool _validTP[4];
275
276     /// calculated volume for use of UnitTetraIntersectionBary
277     double _volume;
278     
279     /**
280      * Calls TransformedTriangle::testTriangleSurroundsEdge for edges OX to ZX and stores the result in
281      * member variable array_triangleSurroundsEdgeCache. 
282      *
283      */
284     void preCalculateTriangleSurroundsEdge();
285
286     /// Array holding results of the test testTriangleSurroundsEdge() for all the edges. 
287     /// These are calculated in preCalculateTriangleSurroundsEdge().
288     bool _triangleSurroundsEdgeCache[NO_TET_EDGE];
289
290     // ----------------------------------------------------------------------------------
291     //  Constants                                                    
292     // ----------------------------------------------------------------------------------
293
294     // offsets : 0 -> x, 1 -> y, 2 -> z, 3 -> h, 4 -> H
295     // corresponds to order of double products in DoubleProduct
296     // so that offset[C_*] gives the right coordinate
297     static const int DP_OFFSET_1[8];
298     static const int DP_OFFSET_2[8];
299
300     // the coordinates used in the expansion of triple products by a given row
301     // in constellation (corner, row-1)
302     // (0,1,2,3) <=> (x,y,z,h)
303     static const int COORDINATE_FOR_DETERMINANT_EXPANSION[12];
304
305     // contains the edge of the double product used when 
306     // expanding the triple product determinant associated with each corner
307     // by a given row
308     static const DoubleProduct DP_FOR_DETERMINANT_EXPANSION[12];
309     
310     // values used to decide how/when imprecise the double products
311     // should be to set them to 0.0
312     static const double MACH_EPS;    // machine epsilon
313     static const double MULT_PREC_F; // precision of multiplications (Grandy : f)
314     static const double THRESHOLD_F; // threshold for zeroing (Grandy : F/f)
315
316     static const double TRIPLE_PRODUCT_ANGLE_THRESHOLD;
317
318     // correspondence facet - double product
319     // Grandy, table IV
320     static const DoubleProduct DP_FOR_SEG_FACET_INTERSECTION[12];
321
322     // signs associated with entries in DP_FOR_SEGMENT_FACET_INTERSECTION
323     static const double SIGN_FOR_SEG_FACET_INTERSECTION[12];
324
325     // coordinates of corners of tetrahedron
326     static const double COORDS_TET_CORNER[12];
327
328     // indices to use in tables DP_FOR_SEG_FACET_INTERSECTION and SIGN_FOR_SEG_FACET_INTERSECTION
329     // for the calculation of the coordinates (x,y,z) of the intersection points
330     // for Segment-Facet and Segment-Edge intersections
331     static const int DP_INDEX[12];
332
333     // correspondence edge - corners
334     static const TetraCorner CORNERS_FOR_EDGE[12];
335
336     // correspondence edge - facets
337     // facets shared by each edge
338     static const TetraFacet FACET_FOR_EDGE[12];
339
340     // correspondence edge - corners
341     static const TetraEdge EDGES_FOR_CORNER[12];
342    
343     // double products used in segment-halfstrip test
344     static const DoubleProduct DP_FOR_HALFSTRIP_INTERSECTION[12];
345
346     // double products used in segment - ray test
347     static const DoubleProduct DP_SEGMENT_RAY_INTERSECTION[21];
348
349   };
350
351   inline void TransformedTriangle::preCalculateTriangleSurroundsEdge()
352   {
353     for(TetraEdge edge = OX ; edge <= ZX ; edge = TetraEdge(edge + 1))
354       {
355         _triangleSurroundsEdgeCache[edge] = testTriangleSurroundsEdge(edge);
356       }
357   }
358
359
360   // ----------------------------------------------------------------------------------
361   //   TransformedTriangle_math.cxx
362   // ----------------------------------------------------------------------------------
363
364   inline void TransformedTriangle::resetDoubleProducts(const TriSegment seg, const TetraCorner corner)
365   {
366     // set the three corresponding double products to 0.0
367     static const DoubleProduct DOUBLE_PRODUCTS[12] =
368     {
369       C_YZ, C_ZX, C_XY, // O
370       C_YZ, C_ZH, C_YH, // X
371       C_ZX, C_ZH, C_XH, // Y
372       C_XY, C_YH, C_XH  // Z
373     };
374
375     for(int i = 0 ; i < 3 ; ++i) {
376         const DoubleProduct dp = DOUBLE_PRODUCTS[3*corner + i];
377
378         LOG(6, std::endl << "resetting inconsistent dp :" << dp << " for corner " << corner);
379         _doubleProducts[8*seg + dp] = 0.0;
380     };
381   }
382
383   inline double TransformedTriangle::calcStableC(const TriSegment seg, const DoubleProduct dp) const
384   {
385     return _doubleProducts[8*seg + dp];
386   }
387
388   inline double TransformedTriangle::calcStableT(const TetraCorner corner) const
389   {
390     assert(_validTP[corner]);
391     return _tripleProducts[corner];
392   }
393
394   inline double TransformedTriangle::calcUnstableC(const TriSegment seg, const DoubleProduct dp, double& delta) const
395   {
396     // find the points of the triangle
397     // 0 -> P, 1 -> Q, 2 -> R
398     const int pt1 = seg;
399     const int pt2 = (seg + 1) % 3;
400
401     // find offsets
402     const int off1 = DP_OFFSET_1[dp];
403     const int off2 = DP_OFFSET_2[dp];
404
405     const double prd1 = _coords[5*pt1 + off1] * _coords[5*pt2 + off2],
406                  prd2 = _coords[5*pt1 + off2] * _coords[5*pt2 + off1];
407     delta = std::fabs(prd1) + std::fabs(prd2);
408     return  prd1 - prd2;
409   }
410
411   // ----------------------------------------------------------------------------------
412   //  TransformedTriangle_intersect.cxx
413   // ----------------------------------------------------------------------------------
414   inline bool TransformedTriangle::testSurfaceEdgeIntersection(const TetraEdge edge) const
415   {
416     return _triangleSurroundsEdgeCache[edge] && testEdgeIntersectsTriangle(edge);
417   }
418
419   inline bool TransformedTriangle::testSegmentFacetIntersection(const TriSegment seg, const TetraFacet facet) const
420   {
421     return testFacetSurroundsSegment(seg, facet) && testSegmentIntersectsFacet(seg, facet);
422   }
423
424   inline bool TransformedTriangle::testSurfaceRayIntersection(const TetraCorner corner) const
425   {
426     return testTriangleSurroundsRay( corner ) && testSurfaceAboveCorner( corner );
427   }
428
429   inline bool TransformedTriangle::testCornerInTetrahedron(const TriCorner corner) const
430   {
431     const double pt[4] =
432     {
433       _coords[5*corner],     // x
434       _coords[5*corner + 1], // y
435       _coords[5*corner + 2], // z
436       _coords[5*corner + 3]  // z
437     };
438
439     for(int i = 0 ; i < 4 ; ++i)
440       {
441         if(pt[i] < 0.0 || pt[i] > 1.0)
442           {
443             return false;
444           }
445       }
446     return true;
447   }
448
449   inline  bool TransformedTriangle::testCornerOnXYZFacet(const TriCorner corner) const
450   {
451 #if 0
452     const double pt[4] =
453     {
454       _coords[5*corner],     // x
455       _coords[5*corner + 1], // y
456       _coords[5*corner + 2], // z
457       _coords[5*corner + 3]  // h
458     };
459 #endif
460     const double* pt = &_coords[5*corner];
461
462     if(pt[3] != 0.0)
463       {
464         return false;
465       }
466
467     for(int i = 0 ; i < 3 ; ++i)
468       {
469         if(pt[i] < 0.0 || pt[i] > 1.0)
470           {
471             return false;
472           }
473       }
474     return true;
475   }
476
477   inline  bool TransformedTriangle::testCornerAboveXYZFacet(const TriCorner corner) const
478   {
479     const double x = _coords[5*corner];
480     const double y = _coords[5*corner + 1];
481     const double h = _coords[5*corner + 3];
482     const double H = _coords[5*corner + 4];
483
484     return h < 0.0 && H >= 0.0 && x >= 0.0 && y >= 0.0;
485
486   }
487
488   inline bool TransformedTriangle::testEdgeIntersectsTriangle(const TetraEdge edge) const
489   {
490     // correspondence edge - triple products for edges OX, ..., ZX (Grandy, table III)
491     static const TetraCorner TRIPLE_PRODUCTS[12] =
492     {
493       X, O, // OX
494       Y, O, // OY
495       Z, O, // OZ
496       X, Y, // XY
497       Y, Z, // YZ
498       Z, X, // ZX
499     };
500
501     // Grandy, [16]
502     const double t1 = calcStableT(TRIPLE_PRODUCTS[2*edge]);
503     const double t2 = calcStableT(TRIPLE_PRODUCTS[2*edge + 1]);
504
505     // [ABN] Okayyy: if either t1 or t2 exactly equal zero, then it can mean two things:
506     //   - either PQR is very close to the corner -> this is OK, further computation of intersection point between
507     // surface and edge will produce a correct result
508     //   - or, if the other triple prod is also very small, then this is a degenerate case: the edge is almost in PQR -> this is bad
509     // and leads to weird intersection point computation -> we avoid this.
510     // PS : here was written "// tuleap26461" -> whoo this helps :-)
511     if (t1 == 0.0 || t2 == 0.0)
512       if (std::fabs(t1+t2) < THRESHOLD_F*MULT_PREC_F)
513         return false;
514
515     return (t1*t2 <= 0.0) && !epsilonEqual(t1,t2, MULT_PREC_F);
516   }
517
518   inline bool TransformedTriangle::testFacetSurroundsSegment(const TriSegment seg, const TetraFacet facet) const
519   {
520 #if 0
521     const double signs[3] =
522     {
523       SIGN_FOR_SEG_FACET_INTERSECTION[3*facet],
524       SIGN_FOR_SEG_FACET_INTERSECTION[3*facet + 1],
525       SIGN_FOR_SEG_FACET_INTERSECTION[3*facet + 2]
526     };
527 #endif
528
529     const double* signs = &SIGN_FOR_SEG_FACET_INTERSECTION[3*facet];
530     const double c1 = signs[0]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet]);
531     const double c2 = signs[1]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet + 1]);
532     const double c3 = signs[2]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet + 2]);
533
534     return (c1*c3 > 0.0) && (c2*c3 > 0.0);
535   }
536
537   inline bool TransformedTriangle::testSegmentIntersectsFacet(const TriSegment seg, const TetraFacet facet) const
538   {
539     // use correspondence facet a = 0 <=> offset for coordinate a in _coords
540     // and also correspondence segment AB => corner A
541     const double coord1 = _coords[5*seg + facet];
542     const double coord2 = _coords[5*( (seg + 1) % 3) + facet];
543
544     //? should we use epsilon-equality here in second test?
545     LOG(5, "coord1 : " << coord1 << " coord2 : " << coord2 );
546
547     return (coord1*coord2 <= 0.0) && (coord1 != coord2);
548   }
549
550   inline bool TransformedTriangle::testSegmentIntersectsHPlane(const TriSegment seg) const
551   {
552     // get the H - coordinates
553     const double coord1 = _coords[5*seg + 4];
554     const double coord2 = _coords[5*( (seg + 1) % 3) + 4];
555     //? should we use epsilon-equality here in second test?
556     LOG(5, "coord1 : " << coord1 << " coord2 : " << coord2 );
557
558     return (coord1*coord2 <= 0.0) && (coord1 != coord2);
559   }
560
561   inline bool TransformedTriangle::testSurfaceAboveCorner(const TetraCorner corner) const
562   {
563     // There is an error in Grandy -> it should be C_XY instead of C_YZ in [28].
564     //
565     // Idea: the nz value (Grandy [28] corrected!) can be interpreted as a special variant of the triple product t_O
566     // where the z coordinate has been set to 1. It represents the signed volume of the tet OP'Q'R' where P', Q' and R' are the
567     // projection of P, Q, R on the z=1 plane.
568     // Comparing the sign of this triple product with t_X (or t_Y, ... dep on the corner) indicates whether the corner is in the
569     // half-space above or below the PQR triangle, similarly to what is explained in [16].
570     // (this works even for the Z corner, since we could have chosen z=24 (instead of z=1) this would not have changed the final sign test).
571     const double nz = calcStableC(PQ, C_XY) + calcStableC(QR, C_XY) + calcStableC(RP, C_XY);
572
573     // Triple product might have not been computed, but here we need one:
574     const double tp = _validTP[corner] ? calcStableT(corner) : calcTByDevelopingRow(corner, 1, false);
575
576     return tp*nz >= 0.0;
577   }
578
579   inline bool TransformedTriangle::testTriangleSurroundsRay(const TetraCorner corner) const
580   {
581     // double products to use for the possible corners
582     static const DoubleProduct DP_FOR_RAY_INTERSECTION[4] =
583     {
584       DoubleProduct(0),        // O - only here to fill out and make indices match
585       C_10,     // X
586       C_01,     // Y
587       C_XY      // Z
588     };
589
590     const DoubleProduct dp = DP_FOR_RAY_INTERSECTION[corner];
591
592     const double cPQ = calcStableC(PQ, dp);
593     const double cQR = calcStableC(QR, dp);
594     const double cRP = calcStableC(RP, dp);
595
596     return ( cPQ*cQR > 0.0 ) && ( cPQ*cRP > 0.0 );
597   }
598
599   inline const std::string& TransformedTriangle::strTC(TetraCorner tc) const
600   {
601     static const std::map<TetraCorner, std::string> m = {{O, "O"}, {X, "X"}, {Y, "Y"}, {Z, "Z"}};
602     return m.at(tc);
603   }
604
605   inline const std::string& TransformedTriangle::strTE(TetraEdge te) const
606   {
607     static const std::map<TetraEdge, std::string> m = {{OX, "OX"}, {OY, "OY"}, {OZ, "OZ"}, {XY, "XY"}, {YZ, "YZ"},
608       {ZX, "ZX"}, {H01, "H01"}, {H10, "H10"}};
609     return m.at(te);
610   }
611
612   inline const std::string& TransformedTriangle::strTF(TetraFacet tf) const
613   {
614     static const std::map<TetraFacet, std::string> m = {{OYZ, "OYZ"}, {OZX, "OZX"}, {OXY, "OXY"}, {XYZ, "XYZ"}};
615     return m.at(tf);
616   }
617
618   inline const std::string& TransformedTriangle::strTriC(TriCorner tc) const
619   {
620     static const std::map<TriCorner, std::string> m = {{P, "P"}, {Q, "Q"}, {R, "R"}};
621     return m.at(tc);
622   }
623
624   inline const std::string& TransformedTriangle::strTriS(TriSegment ts) const
625   {
626     static const std::map<TriSegment, std::string> m = {{PQ, "PQ"}, {QR, "QR"}, {RP, "RP"}};
627     return m.at(ts);
628   }
629
630 }
631
632 #endif