Salome HOME
[TetraIntersect] Avoid multiple divisions in calculatePolygonBarycenter
[tools/medcoupling.git] / src / INTERP_KERNEL / TransformedTriangle.hxx
index b0e771f7e9ddc572013a67d61927e837063218c1..14d3965971dcb9acac94607b62b5fa67de3fff79 100644 (file)
@@ -1,4 +1,4 @@
-// Copyright (C) 2007-2016  CEA/DEN, EDF R&D
+// Copyright (C) 2007-2024  CEA, EDF
 //
 // This library is free software; you can redistribute it and/or
 // modify it under the terms of the GNU Lesser General Public
@@ -21,6 +21,8 @@
 #define __TRANSFORMED_TRIANGLE_HXX__
 
 #include "INTERPKERNELDefines.hxx"
+#include "VectorUtils.hxx"
+#include "assert.h"
 
 #include <vector>
 
@@ -60,8 +62,7 @@ namespace INTERP_KERNEL
    *
    */
 
-  /** \file TransformedTriangle.hxx
-   * 
+  /**
    * OVERVIEW of how the class works : (details can be found in the documentation of each method)
    * 
    * Constructor : 
@@ -69,6 +70,9 @@ namespace INTERP_KERNEL
    * coordinates of the corners of the triangle. It copies their coordinates and then proceeds to pre-calculating certain
    * entities used in the intersection calculation : the double products, triple products and the values of the function E
    * (Grandy, [53]).
+   * It is also at this point in constructor that:
+   *  - the special case of PQR included in the XYZ plane is treated
+   *  - the inconsistencies between double products/triple products computation is handled
    *
    * calculateIntersectionVolume() : 
    * This is the only method in the public interface. It calculates the volume under the intersection polygons
@@ -94,18 +98,14 @@ namespace INTERP_KERNEL
    *    When an intersection point has been detected it is calculated with a corresponding calc* - method in the cases where it
    * is not known directly. It is then added to the polygon A and/or B as necessary.
    *
-   * OPTIMIZE : 
-   *    If OPTIMIZE is defined, a large number of methods will be prefixed with inline and some optimizations concerning the tests 
-   * with zero double products will be used.
    */
   class INTERPKERNEL_EXPORT TransformedTriangle
   {
  
 
   public:
-
-    friend class INTERP_TEST::TransformedTriangleTest;
     friend class INTERP_TEST::TransformedTriangleIntersectTest;
+    friend class INTERP_TEST::TransformedTriangleTest;
     /*
      * Enumerations representing the different geometric elements of the unit tetrahedron
      * and the triangle. The end element, NO_* gives the number of elements in the enumeration
@@ -139,126 +139,90 @@ namespace INTERP_KERNEL
 
     double calculateIntersectionVolume(); 
     double calculateIntersectionSurface(TetraAffineTransform* tat);
-
     void dumpCoords() const;
 
     // Queries of member values used by UnitTetraIntersectionBary
-
     const double* getCorner(TriCorner corner) const { return _coords + 5*corner; }
-
     const std::vector<double*>& getPolygonA() const { return _polygonA; }
-
     double getVolume() const { return _volume; }
 
   protected:
-
     TransformedTriangle() { }
 
     // ----------------------------------------------------------------------------------
     //  High-level methods called directly by calculateIntersectionVolume()     
     // ----------------------------------------------------------------------------------
     void calculateIntersectionAndProjectionPolygons();
-
     void calculatePolygonBarycenter(const IntersectionPolygon poly, double* barycenter); 
-
     void sortIntersectionPolygon(const IntersectionPolygon poly, const double* barycenter); 
-
     double calculateVolumeUnderPolygon(IntersectionPolygon poly, const double* barycenter); 
 
     // ----------------------------------------------------------------------------------
     //  High-level methods called directly by calculateIntersectionSurface()
     // ----------------------------------------------------------------------------------
     void calculateIntersectionPolygon();
-
     double calculateSurfacePolygon();
 
     // ----------------------------------------------------------------------------------
     //  Detection of degenerate triangles  
     // ----------------------------------------------------------------------------------
-
     bool isTriangleInPlaneOfFacet(const TetraFacet facet) const;
-    
     bool isTriangleParallelToFacet(const TetraFacet facet) const;
-
     int isTriangleInclinedToFacet(const TetraFacet facet) const;
-
     bool isTriangleBelowTetraeder() const;
 
     // ----------------------------------------------------------------------------------
     //  Intersection test methods and intersection point calculations           
     // ----------------------------------------------------------------------------------
     inline bool testSurfaceEdgeIntersection(const TetraEdge edge) const; 
-
     void calcIntersectionPtSurfaceEdge(const TetraEdge edge, double* pt) const;  
-
     inline bool testSegmentFacetIntersection(const TriSegment seg, const TetraFacet facet) const; 
-
     void calcIntersectionPtSegmentFacet(const TriSegment seg, const TetraFacet facet, double* pt) const;  
-
     bool testSegmentEdgeIntersection(const TriSegment seg, const TetraEdge edge) const; 
     void calcIntersectionPtSegmentEdge(const TriSegment seg, const TetraEdge edge, double* pt) const ; 
-
     bool testSegmentCornerIntersection(const TriSegment seg, const TetraCorner corner) const ;
-
     inline bool testSurfaceRayIntersection(const TetraCorner corner) const;
-
     bool testSegmentHalfstripIntersection(const TriSegment seg, const TetraEdge edg);
-
     void calcIntersectionPtSegmentHalfstrip(const TriSegment seg, const TetraEdge edge, double* pt) const;
-    
     bool testSegmentRayIntersection(const TriSegment seg, const TetraCorner corner) const;
-
     inline bool testCornerInTetrahedron(const TriCorner corner) const;
-
     inline bool testCornerOnXYZFacet(const TriCorner corner) const;
-
     inline bool testCornerAboveXYZFacet(const TriCorner corner) const;
 
     // ----------------------------------------------------------------------------------
     //  Utility methods used in intersection tests                       
     // ----------------------------------------------------------------------------------
-    
     bool testTriangleSurroundsEdge(const TetraEdge edge) const;
-
     inline bool testEdgeIntersectsTriangle(const TetraEdge edge) const;
-
     inline bool testFacetSurroundsSegment(const TriSegment seg, const TetraFacet facet) const;
-
     inline bool testSegmentIntersectsFacet(const TriSegment seg, const TetraFacet facet) const;
-
     bool testSegmentIntersectsHPlane(const TriSegment seg) const;
-
     bool testSurfaceAboveCorner(const TetraCorner corner) const;
-    
     bool testTriangleSurroundsRay(const TetraCorner corner) const;
 
     // ----------------------------------------------------------------------------------
     //  Double and triple product calculations                           
     // ----------------------------------------------------------------------------------
-    
-    void resetNearZeroCoordinates();
-
+    void handleDegenerateCases();
     bool areDoubleProductsConsistent(const TriSegment seg) const;
-
-    void preCalculateDoubleProducts(void);
-
+    void preCalculateDoubleProducts();
     inline void resetDoubleProducts(const TriSegment seg, const TetraCorner corner);
-
     double calculateDistanceCornerSegment(const TetraCorner corner, const TriSegment seg) const;
-    
-    void preCalculateTripleProducts(void);
-
+    void preCalculateTripleProducts();
     double calculateAngleEdgeTriangle(const TetraEdge edge) const;
-
     inline double calcStableC(const TriSegment seg, const DoubleProduct dp) const;
-
     inline double calcStableT(const TetraCorner corner) const;
+    inline double calcUnstableC(const TriSegment seg, const DoubleProduct dp, double & delta) const;
+    double calcTByDevelopingRow(const TetraCorner corner, const int row, const bool project) const;
 
-    inline double calcUnstableC(const TriSegment seg, const DoubleProduct dp) const;
-
-    double calcTByDevelopingRow(const TetraCorner corner, const int row = 1, const bool project = false) const;
+    // ----------------------------------------------------------------------------------
+    // Debug
+    // ----------------------------------------------------------------------------------
+    inline const std::string& strTC(TetraCorner tc) const;
+    inline const std::string& strTE(TetraEdge te) const;
+    inline const std::string& strTF(TetraFacet tf) const;
+    inline const std::string& strTriC(TriCorner tc) const;
+    inline const std::string& strTriS(TriSegment tc) const;
 
     // ----------------------------------------------------------------------------------
     //  Member variables                                                 
@@ -281,8 +245,12 @@ namespace INTERP_KERNEL
     /// following order in enumeration DoubleProduct
     double _doubleProducts[24];
 
+    double _deltas[24];
+
     /// Array containing the 4 triple products.
     /// order : t_O, t_X, t_Y, t_Z
+    /// For example t_O represent the signed volume of the tetrahedron OPQR, and is positive if PQR is oriented clockwise
+    //  when seen from the vertex O.
     double _tripleProducts[4];
 
     /// Vector holding the points of the intersection polygon A.
@@ -339,11 +307,11 @@ namespace INTERP_KERNEL
     // by a given row
     static const DoubleProduct DP_FOR_DETERMINANT_EXPANSION[12];
     
-    // values used to decide how imprecise the double products 
+    // values used to decide how/when imprecise the double products
     // should be to set them to 0.0
-    static const long double MACH_EPS;    // machine epsilon
-    static const long double MULT_PREC_F; // precision of multiplications (Grandy : f)
-    static const long double THRESHOLD_F; // threshold for zeroing (Grandy : F/f)
+    static const double MACH_EPS;    // machine epsilon
+    static const double MULT_PREC_F; // precision of multiplications (Grandy : f)
+    static const double THRESHOLD_F; // threshold for zeroing (Grandy : F/f)
 
     static const double TRIPLE_PRODUCT_ANGLE_THRESHOLD;
 
@@ -353,10 +321,10 @@ namespace INTERP_KERNEL
 
     // signs associated with entries in DP_FOR_SEGMENT_FACET_INTERSECTION
     static const double SIGN_FOR_SEG_FACET_INTERSECTION[12];
-    
+
     // coordinates of corners of tetrahedron
     static const double COORDS_TET_CORNER[12];
-    
+
     // indices to use in tables DP_FOR_SEG_FACET_INTERSECTION and SIGN_FOR_SEG_FACET_INTERSECTION
     // for the calculation of the coordinates (x,y,z) of the intersection points
     // for Segment-Facet and Segment-Edge intersections
@@ -380,10 +348,285 @@ namespace INTERP_KERNEL
 
   };
 
-  // include definitions of inline methods
+  inline void TransformedTriangle::preCalculateTriangleSurroundsEdge()
+  {
+    for(TetraEdge edge = OX ; edge <= ZX ; edge = TetraEdge(edge + 1))
+      {
+        _triangleSurroundsEdgeCache[edge] = testTriangleSurroundsEdge(edge);
+      }
+  }
+
 
-#include "TransformedTriangleInline.hxx"
-}
+  // ----------------------------------------------------------------------------------
+  //   TransformedTriangle_math.cxx
+  // ----------------------------------------------------------------------------------
+
+  inline void TransformedTriangle::resetDoubleProducts(const TriSegment seg, const TetraCorner corner)
+  {
+    // set the three corresponding double products to 0.0
+    static const DoubleProduct DOUBLE_PRODUCTS[12] =
+    {
+      C_YZ, C_ZX, C_XY, // O
+      C_YZ, C_ZH, C_YH, // X
+      C_ZX, C_ZH, C_XH, // Y
+      C_XY, C_YH, C_XH  // Z
+    };
+
+    for(int i = 0 ; i < 3 ; ++i) {
+        const DoubleProduct dp = DOUBLE_PRODUCTS[3*corner + i];
+
+        LOG(6, std::endl << "resetting inconsistent dp :" << dp << " for corner " << corner);
+        _doubleProducts[8*seg + dp] = 0.0;
+    };
+  }
+
+  inline double TransformedTriangle::calcStableC(const TriSegment seg, const DoubleProduct dp) const
+  {
+    return _doubleProducts[8*seg + dp];
+  }
+
+  inline double TransformedTriangle::calcStableT(const TetraCorner corner) const
+  {
+    assert(_validTP[corner]);
+    return _tripleProducts[corner];
+  }
+
+  inline double TransformedTriangle::calcUnstableC(const TriSegment seg, const DoubleProduct dp, double& delta) const
+  {
+    // find the points of the triangle
+    // 0 -> P, 1 -> Q, 2 -> R
+    const int pt1 = seg;
+    const int pt2 = (seg + 1) % 3;
+
+    // find offsets
+    const int off1 = DP_OFFSET_1[dp];
+    const int off2 = DP_OFFSET_2[dp];
+
+    const double prd1 = _coords[5*pt1 + off1] * _coords[5*pt2 + off2],
+                 prd2 = _coords[5*pt1 + off2] * _coords[5*pt2 + off1];
+    delta = std::fabs(prd1) + std::fabs(prd2);
+    return  prd1 - prd2;
+  }
+
+  // ----------------------------------------------------------------------------------
+  //  TransformedTriangle_intersect.cxx
+  // ----------------------------------------------------------------------------------
+  inline bool TransformedTriangle::testSurfaceEdgeIntersection(const TetraEdge edge) const
+  {
+    return _triangleSurroundsEdgeCache[edge] && testEdgeIntersectsTriangle(edge);
+  }
+
+  inline bool TransformedTriangle::testSegmentFacetIntersection(const TriSegment seg, const TetraFacet facet) const
+  {
+    return testFacetSurroundsSegment(seg, facet) && testSegmentIntersectsFacet(seg, facet);
+  }
+
+  inline bool TransformedTriangle::testSurfaceRayIntersection(const TetraCorner corner) const
+  {
+    return testTriangleSurroundsRay( corner ) && testSurfaceAboveCorner( corner );
+  }
+
+  inline bool TransformedTriangle::testCornerInTetrahedron(const TriCorner corner) const
+  {
+    const double pt[4] =
+    {
+      _coords[5*corner],     // x
+      _coords[5*corner + 1], // y
+      _coords[5*corner + 2], // z
+      _coords[5*corner + 3]  // z
+    };
+
+    for(int i = 0 ; i < 4 ; ++i)
+      {
+        if(pt[i] < 0.0 || pt[i] > 1.0)
+          {
+            return false;
+          }
+      }
+    return true;
+  }
+
+  inline  bool TransformedTriangle::testCornerOnXYZFacet(const TriCorner corner) const
+  {
+#if 0
+    const double pt[4] =
+    {
+      _coords[5*corner],     // x
+      _coords[5*corner + 1], // y
+      _coords[5*corner + 2], // z
+      _coords[5*corner + 3]  // h
+    };
+#endif
+    const double* pt = &_coords[5*corner];
+
+    if(pt[3] != 0.0)
+      {
+        return false;
+      }
+
+    for(int i = 0 ; i < 3 ; ++i)
+      {
+        if(pt[i] < 0.0 || pt[i] > 1.0)
+          {
+            return false;
+          }
+      }
+    return true;
+  }
+
+  inline  bool TransformedTriangle::testCornerAboveXYZFacet(const TriCorner corner) const
+  {
+    const double x = _coords[5*corner];
+    const double y = _coords[5*corner + 1];
+    const double h = _coords[5*corner + 3];
+    const double H = _coords[5*corner + 4];
+
+    return h < 0.0 && H >= 0.0 && x >= 0.0 && y >= 0.0;
+
+  }
+
+  inline bool TransformedTriangle::testEdgeIntersectsTriangle(const TetraEdge edge) const
+  {
+    // correspondence edge - triple products for edges OX, ..., ZX (Grandy, table III)
+    static const TetraCorner TRIPLE_PRODUCTS[12] =
+    {
+      X, O, // OX
+      Y, O, // OY
+      Z, O, // OZ
+      X, Y, // XY
+      Y, Z, // YZ
+      Z, X, // ZX
+    };
+
+    // Grandy, [16]
+    const double t1 = calcStableT(TRIPLE_PRODUCTS[2*edge]);
+    const double t2 = calcStableT(TRIPLE_PRODUCTS[2*edge + 1]);
+
+    // [ABN] Okayyy: if either t1 or t2 exactly equal zero, then it can mean two things:
+    //   - either PQR is very close to the corner -> this is OK, further computation of intersection point between
+    // surface and edge will produce a correct result
+    //   - 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
+    // and leads to weird intersection point computation -> we avoid this.
+    // PS : here was written "// tuleap26461" -> whoo this helps :-)
+    if (t1 == 0.0 || t2 == 0.0)
+      if (std::fabs(t1+t2) < THRESHOLD_F*MULT_PREC_F)
+        return false;
+
+    return (t1*t2 <= 0.0) && !epsilonEqual(t1,t2, MULT_PREC_F);
+  }
+
+  inline bool TransformedTriangle::testFacetSurroundsSegment(const TriSegment seg, const TetraFacet facet) const
+  {
+#if 0
+    const double signs[3] =
+    {
+      SIGN_FOR_SEG_FACET_INTERSECTION[3*facet],
+      SIGN_FOR_SEG_FACET_INTERSECTION[3*facet + 1],
+      SIGN_FOR_SEG_FACET_INTERSECTION[3*facet + 2]
+    };
+#endif
 
+    const double* signs = &SIGN_FOR_SEG_FACET_INTERSECTION[3*facet];
+    const double c1 = signs[0]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet]);
+    const double c2 = signs[1]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet + 1]);
+    const double c3 = signs[2]*calcStableC(seg, DP_FOR_SEG_FACET_INTERSECTION[3*facet + 2]);
+
+    return (c1*c3 > 0.0) && (c2*c3 > 0.0);
+  }
+
+  inline bool TransformedTriangle::testSegmentIntersectsFacet(const TriSegment seg, const TetraFacet facet) const
+  {
+    // use correspondence facet a = 0 <=> offset for coordinate a in _coords
+    // and also correspondence segment AB => corner A
+    const double coord1 = _coords[5*seg + facet];
+    const double coord2 = _coords[5*( (seg + 1) % 3) + facet];
+
+    //? should we use epsilon-equality here in second test?
+    LOG(5, "coord1 : " << coord1 << " coord2 : " << coord2 );
+
+    return (coord1*coord2 <= 0.0) && (coord1 != coord2);
+  }
+
+  inline bool TransformedTriangle::testSegmentIntersectsHPlane(const TriSegment seg) const
+  {
+    // get the H - coordinates
+    const double coord1 = _coords[5*seg + 4];
+    const double coord2 = _coords[5*( (seg + 1) % 3) + 4];
+    //? should we use epsilon-equality here in second test?
+    LOG(5, "coord1 : " << coord1 << " coord2 : " << coord2 );
+
+    return (coord1*coord2 <= 0.0) && (coord1 != coord2);
+  }
+
+  inline bool TransformedTriangle::testSurfaceAboveCorner(const TetraCorner corner) const
+  {
+    // There is an error in Grandy -> it should be C_XY instead of C_YZ in [28].
+    //
+    // Idea: the nz value (Grandy [28] corrected!) can be interpreted as a special variant of the triple product t_O
+    // 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
+    // projection of P, Q, R on the z=1 plane.
+    // Comparing the sign of this triple product with t_X (or t_Y, ... dep on the corner) indicates whether the corner is in the
+    // half-space above or below the PQR triangle, similarly to what is explained in [16].
+    // (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).
+    const double nz = calcStableC(PQ, C_XY) + calcStableC(QR, C_XY) + calcStableC(RP, C_XY);
+
+    // Triple product might have not been computed, but here we need one:
+    const double tp = _validTP[corner] ? calcStableT(corner) : calcTByDevelopingRow(corner, 1, false);
+
+    return tp*nz >= 0.0;
+  }
+
+  inline bool TransformedTriangle::testTriangleSurroundsRay(const TetraCorner corner) const
+  {
+    // double products to use for the possible corners
+    static const DoubleProduct DP_FOR_RAY_INTERSECTION[4] =
+    {
+      DoubleProduct(0),        // O - only here to fill out and make indices match
+      C_10,     // X
+      C_01,     // Y
+      C_XY      // Z
+    };
+
+    const DoubleProduct dp = DP_FOR_RAY_INTERSECTION[corner];
+
+    const double cPQ = calcStableC(PQ, dp);
+    const double cQR = calcStableC(QR, dp);
+    const double cRP = calcStableC(RP, dp);
+
+    return ( cPQ*cQR > 0.0 ) && ( cPQ*cRP > 0.0 );
+  }
+
+  inline const std::string& TransformedTriangle::strTC(TetraCorner tc) const
+  {
+    static const std::map<TetraCorner, std::string> m = {{O, "O"}, {X, "X"}, {Y, "Y"}, {Z, "Z"}};
+    return m.at(tc);
+  }
+
+  inline const std::string& TransformedTriangle::strTE(TetraEdge te) const
+  {
+    static const std::map<TetraEdge, std::string> m = {{OX, "OX"}, {OY, "OY"}, {OZ, "OZ"}, {XY, "XY"}, {YZ, "YZ"},
+      {ZX, "ZX"}, {H01, "H01"}, {H10, "H10"}};
+    return m.at(te);
+  }
+
+  inline const std::string& TransformedTriangle::strTF(TetraFacet tf) const
+  {
+    static const std::map<TetraFacet, std::string> m = {{OYZ, "OYZ"}, {OZX, "OZX"}, {OXY, "OXY"}, {XYZ, "XYZ"}};
+    return m.at(tf);
+  }
+
+  inline const std::string& TransformedTriangle::strTriC(TriCorner tc) const
+  {
+    static const std::map<TriCorner, std::string> m = {{P, "P"}, {Q, "Q"}, {R, "R"}};
+    return m.at(tc);
+  }
+
+  inline const std::string& TransformedTriangle::strTriS(TriSegment ts) const
+  {
+    static const std::map<TriSegment, std::string> m = {{PQ, "PQ"}, {QR, "QR"}, {RP, "RP"}};
+    return m.at(ts);
+  }
+
+}
 
 #endif