// Copyright (c) 2025 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#ifndef _GeomLProp_SurfaceUtils_HeaderFile
#define _GeomLProp_SurfaceUtils_HeaderFile

#include <Adaptor3d_Surface.hxx>
#include <CSLib.hxx>
#include <CSLib_DerivativeStatus.hxx>
#include <Geom_Surface.hxx>
#include <LProp_NotDefined.hxx>
#include <LProp_Status.hxx>
#include <Standard_Handle.hxx>
#include <gp_Dir.hxx>
#include <gp_Pnt.hxx>
#include <gp_Vec.hxx>
#include <math_DirectPolynomialRoots.hxx>

#include <cmath>

//! Template utility functions for SLProps surface local property computation.
//! Provides access policies (DirectAccess, ToolAccess), surface bounds helpers,
//! and shared algorithms for evaluating derivatives, tangents, normals, and curvatures.
namespace LProp_SurfaceUtils
{

// ==================== Deref helpers ====================

//! Dereference by-value or reference types (identity).
template <typename T>
T& Deref(T& theObj)
{
  return theObj;
}

//! Dereference occ::handle types.
template <typename T>
T& Deref(occ::handle<T>& theHandle)
{
  return *theHandle;
}

//! Dereference const occ::handle types.
template <typename T>
const T& Deref(const occ::handle<T>& theHandle)
{
  return *theHandle;
}

// ==================== Surface Bounds Helpers ====================

//! Get bounds from Geom_Surface (uses Bounds method with U1, U2, V1, V2 order).
inline void GetSurfBounds(const Geom_Surface& theSurf,
                          double&             theU1,
                          double&             theV1,
                          double&             theU2,
                          double&             theV2)
{
  theSurf.Bounds(theU1, theU2, theV1, theV2);
}

//! Get bounds from Adaptor3d_Surface (uses individual parameter methods).
//! Also works for BRepAdaptor_Surface which inherits from Adaptor3d_Surface.
inline void GetSurfBounds(const Adaptor3d_Surface& theSurf,
                          double&                  theU1,
                          double&                  theV1,
                          double&                  theU2,
                          double&                  theV2)
{
  theU1 = theSurf.FirstUParameter();
  theV1 = theSurf.FirstVParameter();
  theU2 = theSurf.LastUParameter();
  theV2 = theSurf.LastVParameter();
}

// ==================== Access Policies ====================

//! Direct access policy: calls D0/D1/D2 methods on the surface object.
//! Works with occ::handle<T> and by-value surface types.
struct DirectAccess
{
  template <typename S>
  static void D0(S& theSurf, double theU, double theV, gp_Pnt& thePnt)
  {
    Deref(theSurf).D0(theU, theV, thePnt);
  }

  template <typename S>
  static void D1(S&      theSurf,
                 double  theU,
                 double  theV,
                 gp_Pnt& thePnt,
                 gp_Vec& theD1u,
                 gp_Vec& theD1v)
  {
    Deref(theSurf).D1(theU, theV, thePnt, theD1u, theD1v);
  }

  template <typename S>
  static void D2(S&      theSurf,
                 double  theU,
                 double  theV,
                 gp_Pnt& thePnt,
                 gp_Vec& theD1u,
                 gp_Vec& theD1v,
                 gp_Vec& theD2u,
                 gp_Vec& theD2v,
                 gp_Vec& theDuv)
  {
    Deref(theSurf).D2(theU, theV, thePnt, theD1u, theD1v, theD2u, theD2v, theDuv);
  }

  template <typename S>
  static void Bounds(S& theSurf, double& theU1, double& theV1, double& theU2, double& theV2)
  {
    GetSurfBounds(Deref(theSurf), theU1, theV1, theU2, theV2);
  }
};

//! Tool-based access policy: delegates to static Tool methods.
//! Used for HLRBRep types where Tool class provides the interface.
template <typename Tool>
struct ToolAccess
{
  template <typename S>
  static void D0(S& theSurf, double theU, double theV, gp_Pnt& thePnt)
  {
    Tool::Value(theSurf, theU, theV, thePnt);
  }

  template <typename S>
  static void D1(S&      theSurf,
                 double  theU,
                 double  theV,
                 gp_Pnt& thePnt,
                 gp_Vec& theD1u,
                 gp_Vec& theD1v)
  {
    Tool::D1(theSurf, theU, theV, thePnt, theD1u, theD1v);
  }

  template <typename S>
  static void D2(S&      theSurf,
                 double  theU,
                 double  theV,
                 gp_Pnt& thePnt,
                 gp_Vec& theD1u,
                 gp_Vec& theD1v,
                 gp_Vec& theD2u,
                 gp_Vec& theD2v,
                 gp_Vec& theDuv)
  {
    Tool::D2(theSurf, theU, theV, thePnt, theD1u, theD1v, theD2u, theD2v, theDuv);
  }

  template <typename S>
  static void Bounds(S& theSurf, double& theU1, double& theV1, double& theU2, double& theV2)
  {
    Tool::Bounds(theSurf, theU1, theV1, theU2, theV2);
  }
};

// ==================== Algorithm Utilities ====================

//! Evaluate surface derivatives at parameters (theU, theV) up to specified order.
//! @param[in,out] theSurf   surface object
//! @param[in]     theU      U parameter value
//! @param[in]     theV      V parameter value
//! @param[in]     theOrder  maximum derivative order (0-2)
//! @param[out]    thePnt    evaluated point
//! @param[out]    theD1u    first U derivative
//! @param[out]    theD1v    first V derivative
//! @param[out]    theD2u    second U derivative
//! @param[out]    theD2v    second V derivative
//! @param[out]    theDuv    mixed UV derivative
template <typename Access, typename Surface>
void EvalSurfDerivatives(Surface& theSurf,
                         double   theU,
                         double   theV,
                         int      theOrder,
                         gp_Pnt&  thePnt,
                         gp_Vec&  theD1u,
                         gp_Vec&  theD1v,
                         gp_Vec&  theD2u,
                         gp_Vec&  theD2v,
                         gp_Vec&  theDuv)
{
  switch (theOrder)
  {
    case 0:
      Access::D0(theSurf, theU, theV, thePnt);
      break;
    case 1:
      Access::D1(theSurf, theU, theV, thePnt, theD1u, theD1v);
      break;
    case 2:
      Access::D2(theSurf, theU, theV, thePnt, theD1u, theD1v, theD2u, theD2v, theDuv);
      break;
  }
}

//! Check if the surface tangent is defined for a given derivative direction.
//! Searches for the first non-null derivative in either U or V direction.
//! @param[in]  theD1        first derivative (D1U for U direction, D1V for V direction)
//! @param[in]  theD2        second derivative (D2U for U direction, D2V for V direction)
//! @param[in]  theCN        continuity order
//! @param[in]  theTolSq     squared linear tolerance
//! @param[out] theOrder     order of first significant derivative
//! @param[out] theStatus    resulting tangent status
//! @return true if tangent is defined
inline bool FindSurfTangentOrder(const gp_Vec& theD1,
                                 const gp_Vec& theD2,
                                 int           theCN,
                                 double        theTolSq,
                                 int&          theOrder,
                                 LProp_Status& theStatus)
{
  const gp_Vec* aDerivs[2] = {&theD1, &theD2};
  theOrder                 = 0;

  while (theOrder < 3)
  {
    theOrder++;
    if (theCN >= theOrder)
    {
      if (theOrder <= 2 && aDerivs[theOrder - 1]->SquareMagnitude() > theTolSq)
      {
        theStatus = LProp_Defined;
        return true;
      }
    }
    else
    {
      theStatus = LProp_Undefined;
      return false;
    }
  }

  theStatus = LProp_Undefined;
  return false;
}

//! Compute surface tangent direction with sign correction for higher-order derivatives.
//! When the first significant derivative has order > 1, the sign is determined
//! by comparing with the chord direction near the point.
//! @param[in,out] theSurf       surface object
//! @param[in]     theU          current U parameter
//! @param[in]     theV          current V parameter
//! @param[in]     theFirstDeriv first derivative in this direction (D1U or D1V)
//! @param[in]     theSecDeriv   second derivative in this direction (D2U or D2V)
//! @param[in]     theSigOrder   order of first significant derivative
//! @param[in]     theIsU        true for U direction, false for V direction
//! @param[out]    theDir        computed tangent direction
template <typename Access, typename Surface>
void ComputeSurfTangent(Surface&      theSurf,
                        double        theU,
                        double        theV,
                        const gp_Vec& theFirstDeriv,
                        const gp_Vec& theSecDeriv,
                        int           theSigOrder,
                        bool          theIsU,
                        gp_Dir&       theDir)
{
  if (theSigOrder == 1)
  {
    theDir = gp_Dir(theFirstDeriv);
    return;
  }

  constexpr double THE_DIVISION_FACTOR = 1.0e-3;
  constexpr double THE_MIN_STEP        = 1.0e-7;

  double anUinfimum, anVinfimum, anUsupremum, anVsupremum;
  Access::Bounds(theSurf, anUinfimum, anVinfimum, anUsupremum, anVsupremum);

  if (theIsU)
  {
    double aDu;
    if ((anUsupremum >= RealLast()) || (anUinfimum <= RealFirst()))
      aDu = 0.0;
    else
      aDu = anUsupremum - anUinfimum;

    const double aDelta = std::max(aDu * THE_DIVISION_FACTOR, THE_MIN_STEP);

    gp_Vec aV = theSecDeriv;

    double anOtherU;
    if (theU - anUinfimum < aDelta)
      anOtherU = theU + aDelta;
    else
      anOtherU = theU - aDelta;

    gp_Pnt aP1, aP2;
    Access::D0(theSurf, std::min(theU, anOtherU), theV, aP1);
    Access::D0(theSurf, std::max(theU, anOtherU), theV, aP2);

    gp_Vec aChord(aP1, aP2);
    if (aV.Dot(aChord) < 0.0)
      aV = -aV;

    theDir = gp_Dir(aV);
  }
  else
  {
    double aDv;
    if ((anVsupremum >= RealLast()) || (anVinfimum <= RealFirst()))
      aDv = 0.0;
    else
      aDv = anVsupremum - anVinfimum;

    const double aDelta = std::max(aDv * THE_DIVISION_FACTOR, THE_MIN_STEP);

    gp_Vec aV = theSecDeriv;

    double anOtherV;
    if (theV - anVinfimum < aDelta)
      anOtherV = theV + aDelta;
    else
      anOtherV = theV - aDelta;

    gp_Pnt aP1, aP2;
    Access::D0(theSurf, theU, std::min(theV, anOtherV), aP1);
    Access::D0(theSurf, theU, std::max(theV, anOtherV), aP2);

    gp_Vec aChord(aP1, aP2);
    if (aV.Dot(aChord) < 0.0)
      aV = -aV;

    theDir = gp_Dir(aV);
  }
}

//! Check if surface normal is defined, and compute it via CSLib::Normal.
//! @param[in]  theD1u     first U derivative
//! @param[in]  theD1v     first V derivative
//! @param[in]  theLinTol  linear tolerance
//! @param[out] theNormal  computed normal direction (if defined)
//! @return true if normal is defined
inline bool ComputeSurfNormal(const gp_Vec& theD1u,
                              const gp_Vec& theD1v,
                              double        theLinTol,
                              gp_Dir&       theNormal)
{
  CSLib_DerivativeStatus aStatus = CSLib_Done;
  CSLib::Normal(theD1u, theD1v, theLinTol, aStatus, theNormal);
  return aStatus == CSLib_Done;
}

//! Compute principal curvatures and directions via fundamental forms.
//! Solves the eigenvalue problem for the shape operator using
//! first and second fundamental form coefficients.
//! @param[in]  theD1u      first U derivative
//! @param[in]  theD1v      first V derivative
//! @param[in]  theD2u      second U derivative
//! @param[in]  theD2v      second V derivative
//! @param[in]  theDuv      mixed UV derivative
//! @param[in]  theNormal   surface normal direction
//! @param[out] theMinCurv  minimum principal curvature
//! @param[out] theMaxCurv  maximum principal curvature
//! @param[out] theDirMin   direction of minimum curvature
//! @param[out] theDirMax   direction of maximum curvature
//! @param[out] theMeanCurv mean curvature
//! @param[out] theGausCurv Gaussian curvature
//! @return true if curvatures are successfully computed
inline bool ComputeSurfCurvatures(const gp_Vec& theD1u,
                                  const gp_Vec& theD1v,
                                  const gp_Vec& theD2u,
                                  const gp_Vec& theD2v,
                                  const gp_Vec& theDuv,
                                  const gp_Dir& theNormal,
                                  double&       theMinCurv,
                                  double&       theMaxCurv,
                                  gp_Dir&       theDirMin,
                                  gp_Dir&       theDirMax,
                                  double&       theMeanCurv,
                                  double&       theGausCurv)
{
  const gp_Vec aNorm(theNormal);

  const double anE = theD1u.SquareMagnitude();
  const double anF = theD1u.Dot(theD1v);
  const double aG  = theD1v.SquareMagnitude();

  const double aL = aNorm.Dot(theD2u);
  const double aM = aNorm.Dot(theDuv);
  const double aN = aNorm.Dot(theD2v);

  const double anA0 = anE * aM - anF * aL;
  const double aB0  = anE * aN - aG * aL;
  const double aC0  = anF * aN - aG * aM;

  const double aMaxABC = std::max(std::max(std::abs(anA0), std::abs(aB0)), std::abs(aC0));
  if (aMaxABC < RealEpsilon())
  {
    // Umbilic point
    if (aG < RealEpsilon())
    {
      return false;
    }
    theMinCurv  = aN / aG;
    theMaxCurv  = theMinCurv;
    theDirMin   = gp_Dir(theD1u);
    theDirMax   = gp_Dir(theD1u.Crossed(aNorm));
    theMeanCurv = theMinCurv;
    theGausCurv = theMinCurv * theMinCurv;
    return true;
  }

  const double anA = anA0 / aMaxABC;
  const double aB  = aB0 / aMaxABC;
  const double aC  = aC0 / aMaxABC;

  double aCurv1, aCurv2;
  gp_Vec aVectCurv1, aVectCurv2;

  if (std::abs(anA) > RealEpsilon())
  {
    math_DirectPolynomialRoots aRoot(anA, aB, aC);
    if (aRoot.NbSolutions() != 2)
      return false;

    const double aRoot1 = aRoot.Value(1);
    const double aRoot2 = aRoot.Value(2);
    aCurv1 = ((aL * aRoot1 + 2.0 * aM) * aRoot1 + aN) / ((anE * aRoot1 + 2.0 * anF) * aRoot1 + aG);
    aCurv2 = ((aL * aRoot2 + 2.0 * aM) * aRoot2 + aN) / ((anE * aRoot2 + 2.0 * anF) * aRoot2 + aG);
    aVectCurv1 = aRoot1 * theD1u + theD1v;
    aVectCurv2 = aRoot2 * theD1u + theD1v;
  }
  else if (std::abs(aC) > RealEpsilon())
  {
    math_DirectPolynomialRoots aRoot(aC, aB, anA);
    if (aRoot.NbSolutions() != 2)
      return false;

    const double aRoot1 = aRoot.Value(1);
    const double aRoot2 = aRoot.Value(2);
    aCurv1 = ((aN * aRoot1 + 2.0 * aM) * aRoot1 + aL) / ((aG * aRoot1 + 2.0 * anF) * aRoot1 + anE);
    aCurv2 = ((aN * aRoot2 + 2.0 * aM) * aRoot2 + aL) / ((aG * aRoot2 + 2.0 * anF) * aRoot2 + anE);
    aVectCurv1 = theD1u + aRoot1 * theD1v;
    aVectCurv2 = theD1u + aRoot2 * theD1v;
  }
  else
  {
    aCurv1     = aL / anE;
    aCurv2     = aN / aG;
    aVectCurv1 = theD1u;
    aVectCurv2 = theD1v;
  }

  if (aCurv1 < aCurv2)
  {
    theMinCurv = aCurv1;
    theMaxCurv = aCurv2;
    theDirMin  = gp_Dir(aVectCurv1);
    theDirMax  = gp_Dir(aVectCurv2);
  }
  else
  {
    theMinCurv = aCurv2;
    theMaxCurv = aCurv1;
    theDirMin  = gp_Dir(aVectCurv2);
    theDirMax  = gp_Dir(aVectCurv1);
  }

  const double anEG_FF = (anE * aG) - (anF * anF);
  theMeanCurv          = ((aN * anE) - (2.0 * aM * anF) + (aL * aG)) / (2.0 * anEG_FF);
  theGausCurv          = ((aL * aN) - (aM * aM)) / anEG_FF;
  return true;
}

// ==================== Higher-Level Method Wrappers ====================

//! SetParameters: sets parameters, evaluates derivatives, resets all statuses.
template <typename Access, typename Surface>
void SetParameters(Surface&      theSurf,
                   double        theU,
                   double        theV,
                   double&       theStoredU,
                   double&       theStoredV,
                   int           theDerOrder,
                   gp_Pnt&       thePnt,
                   gp_Vec&       theD1u,
                   gp_Vec&       theD1v,
                   gp_Vec&       theD2u,
                   gp_Vec&       theD2v,
                   gp_Vec&       theDuv,
                   LProp_Status& theUTanSt,
                   LProp_Status& theVTanSt,
                   LProp_Status& theNormSt,
                   LProp_Status& theCurvSt)
{
  theStoredU = theU;
  theStoredV = theV;
  EvalSurfDerivatives<
    Access>(theSurf, theU, theV, theDerOrder, thePnt, theD1u, theD1v, theD2u, theD2v, theDuv);
  theUTanSt = LProp_Undecided;
  theVTanSt = LProp_Undecided;
  theNormSt = LProp_Undecided;
  theCurvSt = LProp_Undecided;
}

//! Ensure surface derivatives up to the required order. Returns the specified result field.
template <typename Access, typename Surface>
const gp_Vec& EnsureSurfDeriv(Surface&      theSurf,
                              double        theU,
                              double        theV,
                              int&          theDerOrder,
                              int           theRequired,
                              gp_Pnt&       thePnt,
                              gp_Vec&       theD1u,
                              gp_Vec&       theD1v,
                              gp_Vec&       theD2u,
                              gp_Vec&       theD2v,
                              gp_Vec&       theDuv,
                              const gp_Vec& theResult)
{
  if (theDerOrder < theRequired)
  {
    theDerOrder = theRequired;
    EvalSurfDerivatives<
      Access>(theSurf, theU, theV, theDerOrder, thePnt, theD1u, theD1v, theD2u, theD2v, theDuv);
  }
  return theResult;
}

//! IsTangentUDefined: checks U tangent status, then searches via D1U/D2U.
template <typename Props>
bool IsTangentUDefined(Props&        theProps,
                       int           theCN,
                       double        theLinTol,
                       int&          theSigOrder,
                       LProp_Status& theTanStatus)
{
  if (theTanStatus == LProp_Undefined)
    return false;
  if (theTanStatus >= LProp_Defined)
    return true;
  return FindSurfTangentOrder(theProps.D1U(),
                              theProps.D2U(),
                              theCN,
                              theLinTol * theLinTol,
                              theSigOrder,
                              theTanStatus);
}

//! IsTangentVDefined: checks V tangent status, then searches via D1V/D2V.
template <typename Props>
bool IsTangentVDefined(Props&        theProps,
                       int           theCN,
                       double        theLinTol,
                       int&          theSigOrder,
                       LProp_Status& theTanStatus)
{
  if (theTanStatus == LProp_Undefined)
    return false;
  if (theTanStatus >= LProp_Defined)
    return true;
  return FindSurfTangentOrder(theProps.D1V(),
                              theProps.D2V(),
                              theCN,
                              theLinTol * theLinTol,
                              theSigOrder,
                              theTanStatus);
}

//! TangentU: checks IsTangentUDefined, then computes U tangent direction.
template <typename Access, typename Props, typename Surface>
void TangentU(Props&        theProps,
              Surface&      theSurf,
              double        theU,
              double        theV,
              const gp_Vec& theD1u,
              const gp_Vec& theD2u,
              int           theSigOrder,
              gp_Dir&       theDir)
{
  if (!theProps.IsTangentUDefined())
    throw LProp_NotDefined();
  ComputeSurfTangent<Access>(theSurf, theU, theV, theD1u, theD2u, theSigOrder, true, theDir);
}

//! TangentV: checks IsTangentVDefined, then computes V tangent direction.
template <typename Access, typename Props, typename Surface>
void TangentV(Props&        theProps,
              Surface&      theSurf,
              double        theU,
              double        theV,
              const gp_Vec& theD1v,
              const gp_Vec& theD2v,
              int           theSigOrder,
              gp_Dir&       theDir)
{
  if (!theProps.IsTangentVDefined())
    throw LProp_NotDefined();
  ComputeSurfTangent<Access>(theSurf, theU, theV, theD1v, theD2v, theSigOrder, false, theDir);
}

//! IsNormalDefined: checks normal status, then computes via CSLib.
inline bool IsNormalDefined(const gp_Vec& theD1u,
                            const gp_Vec& theD1v,
                            double        theLinTol,
                            gp_Dir&       theNormal,
                            LProp_Status& theNormStatus)
{
  if (theNormStatus == LProp_Undefined)
    return false;
  if (theNormStatus >= LProp_Defined)
    return true;
  if (ComputeSurfNormal(theD1u, theD1v, theLinTol, theNormal))
  {
    theNormStatus = LProp_Computed;
    return true;
  }
  theNormStatus = LProp_Undefined;
  return false;
}

//! Normal: checks IsNormalDefined (via theProps), then returns normal.
template <typename Props>
const gp_Dir& Normal(Props& theProps, const gp_Dir& theNormal)
{
  if (!theProps.IsNormalDefined())
    throw LProp_NotDefined();
  return theNormal;
}

//! IsCurvatureDefined: full check including continuity, normal, tangents, and curvature
//! computation.
template <typename Props>
bool IsCurvatureDefined(Props&        theProps,
                        int           theCN,
                        int&          theDerOrder,
                        const gp_Vec& theD1u,
                        const gp_Vec& theD1v,
                        const gp_Vec& theD2u,
                        const gp_Vec& theD2v,
                        const gp_Vec& theDuv,
                        const gp_Dir& theNormal,
                        double&       theMinCurv,
                        double&       theMaxCurv,
                        gp_Dir&       theDirMin,
                        gp_Dir&       theDirMax,
                        double&       theMeanCurv,
                        double&       theGausCurv,
                        LProp_Status& theCurvStatus)
{
  if (theCurvStatus == LProp_Undefined)
    return false;
  if (theCurvStatus >= LProp_Defined)
    return true;
  if (theCN < 2)
  {
    theCurvStatus = LProp_Undefined;
    return false;
  }
  if (!theProps.IsNormalDefined())
  {
    theCurvStatus = LProp_Undefined;
    return false;
  }
  if (!theProps.IsTangentUDefined() || !theProps.IsTangentVDefined())
  {
    theCurvStatus = LProp_Undefined;
    return false;
  }
  if (theDerOrder < 2)
    theProps.D2U();
  if (ComputeSurfCurvatures(theD1u,
                            theD1v,
                            theD2u,
                            theD2v,
                            theDuv,
                            theNormal,
                            theMinCurv,
                            theMaxCurv,
                            theDirMin,
                            theDirMax,
                            theMeanCurv,
                            theGausCurv))
  {
    theCurvStatus = LProp_Computed;
    return true;
  }
  theCurvStatus = LProp_Undefined;
  return false;
}

//! Require curvature to be defined, throw if not. Returns the given value.
template <typename Props>
double RequireCurvature(Props& theProps, double theValue)
{
  if (!theProps.IsCurvatureDefined())
    throw LProp_NotDefined();
  return theValue;
}

//! IsUmbilic: checks curvature, then compares min/max.
template <typename Props>
bool IsUmbilic(Props& theProps, double theMaxCurv, double theMinCurv)
{
  if (!theProps.IsCurvatureDefined())
    throw LProp_NotDefined();
  return std::abs(theMaxCurv - theMinCurv) < std::abs(Epsilon(theMaxCurv));
}

//! CurvatureDirections: checks curvature, then returns max/min directions.
template <typename Props>
void CurvatureDirections(Props&        theProps,
                         const gp_Dir& theDirMax,
                         const gp_Dir& theDirMin,
                         gp_Dir&       theMax,
                         gp_Dir&       theMin)
{
  if (!theProps.IsCurvatureDefined())
    throw LProp_NotDefined();
  theMax = theDirMax;
  theMin = theDirMin;
}

} // namespace LProp_SurfaceUtils

#endif // _GeomLProp_SurfaceUtils_HeaderFile
