// Gnurbs - A curve and surface library
// Copyright (C) 2008-2026 Eric Bechet
//
// See the LICENSE file for contributions and license information.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//

#ifndef __NSURFACE_H
#define __NSURFACE_H

#include "nutil.h"
#include "nentity.h"
#include "ncurve.h"


/// Generic surface.
class nsurface : public nentity
{
public:
  virtual int dim(void) const { return 2;}
};


/// Generic parametric surface.
class nparametricsurface : public nsurface
{
public:
  /// \brief Returns the degree in u and v or -1 if non polynomial.
  /// \return Degree.
  virtual int degree(void) const {return -1;}

  /// \brief Returns the degree in u.
  /// \return Degree.
  virtual int degree_u(void) const {return -1;}

  /// \brief Returns the degree in v.
  /// \return Degree.
  virtual int degree_v(void) const {return -1;}

  /// \brief Number of control points in u.
  /// \return Number of control points.
  virtual int nb_CP_u(int iv=0) const {return 0;}

  /// \brief Number of control points in v.
  /// \return Number of control points.
  virtual int nb_CP_v(int iu=0) const {return 0;}

  /// \brief Minimal allowed value for u.
  /// \return Minimal value.
  virtual double min_u() const =0;

  /// \brief Maximal allowed value for u.
  /// \return Maximal value.
  virtual double max_u() const =0;

  /// \brief Minimal allowed value for v.
  /// \return Minimal value.
  virtual double min_v() const =0;

  /// \brief Maximal allowed value for v.
  /// \return Maximal value.
  virtual double max_v() const =0;

  /// \brief Point on the surface S(u,v).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret point on (u, v).
  virtual void P(double u,double v, npoint& ret) const =0;

  
  /// \brief Point on the surface S(u,v) + 1st derivatives
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] p point on (u, v).
  /// \param[out] dpdu derivative wrt. u.
  /// \param[out] dpdv derivative wrt. v.
  virtual void dP(double u,double v, npoint& p,npoint& dpdu,npoint& dpdv) const
  {
    P(u,v,p);
    dPdu(u,v,dpdu);
    dPdv(u,v,dpdv);
  }

  /// \brief Point on the surface S(u,v) + 1st derivatives + 2nd derivatives
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] p point on (u, v).
  /// \param[out] dpdu derivative wrt. u.
  /// \param[out] dpdv derivative wrt. v.
  /// \param[out] d2pdu2 2nd derivative wrt. u.
  /// \param[out] d2pdv2 2nd derivative wrt. v.
  /// \param[out] d2pdudv cross derivative wrt. u & v.
  virtual void d2P(double u,double v, npoint& p,npoint& dpdu,npoint& dpdv,npoint& d2pdu2,npoint& d2pdv2,npoint& d2pdudv) const
  {
    P(u,v,p);
    dPdu(u,v,dpdu);
    dPdv(u,v,dpdv);
    d2Pdu2(u,v,d2pdu2);
    d2Pdv2(u,v,d2pdv2);
    d2Pdudv(u,v,d2pdudv);
  }  
  
  /// \brief First derivative u (generic implementation).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret u first derivative on (u, v).
  virtual void dPdu(double u,double v, npoint& ret) const;

  /// \brief First derivative v (generic implementation).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret v first derivative on (u, v).
  virtual void dPdv(double u,double v, npoint& ret) const ;

  /// \brief Second derivative u (generic implementation).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret u second derivative on (u, v).
  virtual void d2Pdu2(double u,double v, npoint& ret) const ;

  /// \brief Second derivative v (generic implementation).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret v second derivative on (u, v).
  virtual void d2Pdv2(double u,double v, npoint& ret) const ;

  /// \brief Second derivative u and v (generic implementation).
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] ret u and v second derivative on (u, v).
  virtual void d2Pdudv(double u,double v, npoint& ret) const ;

  /// \brief Computes the various differentials w.r. to u,v
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] M1 the 1st fundamental form.
  /// \param[out] M2 the second fundamental form.
  /// \param[out] G the Christoffel symbols. 
  /// \param[out] Minv inverse of the 1st fundamental form.
  /// \f$ G[i][j][k] = \Sigma_{ij}^k. \ J[0] = P_u, \ J[1]=P_v, \ J[2]=N; \f$
  virtual void fundamental_forms(double u, double v, double M1[2][2], npoint3 J[3]=0, double M2[2][2]=0, double G[2][2][2] = 0, double Minv[2][2]=0) const ;

  /// \brief Rotates a vector in u-v so that the corresponding vector rotates by the prescribed value in cartsian coordinates.
  virtual void rotate(double u, double v, npoint3 V,double angle,npoint3 &Vrot,double M1[2][2]=0);

  /// \brief Computes principal curvatures and directions.
  /// \param[in] u parameter.
  /// \param[in] v parameter.
  /// \param[out] kappa principal curvatures.
  /// \param[out] V vector of principal directions (in columns).
  /// \param[out] M1 the 1st fundamental form.
  /// \param[out] M2 the second fundamental form.
  virtual void principal_curvatures(double u, double v,double kappa[2],double V[2][2], double M1[2][2]=0, double M2[2][2]=0,double thr=1e-5) const ;

  /// \brief Computes normal and geodesic curvatures of an embedded curve.
  /// \param[in] c considered curve.
  /// \param[in] t curve parameter.
  /// \param[out] kappa principal curvatures.
  /// \param[out] M1 the 1st fundamental form.
  /// \param[out] M2 the second fundamental form.
  virtual void relative_curvatures(const nparametriccurve &c, double t,double kappa[2], double M1[2][2]=0, double M2[2][2]=0) const;

  /// \brief Finds the local second derivatives of a curve that have a vanishing geodesic curvature.
  virtual void find_geodesic(double u,double v, npoint3 dpdt, npoint3 &d2pdt2) const;

  /// \brief Walks along a geodesic.
  virtual bool walk(double u, double v, const npoint3 dir,double length,npoint3 &newpos,npoint3 &newdir,int steps=1);

  /// \brief Discretizes the surface in triangle or quads.
  /// \param[out] data data to fill-in.
  virtual void Display(data_container& data) const;

  /// \brief Clones the surface (allocated on the heap).
  /// \return Cloned surface.
  virtual nsurface* clone() const =0 ;
};


#endif //__NSURFACE_H
