// Gmsh - Copyright (C) 1997-2014 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to the public mailing list <gmsh@geuz.org>.
//
// Contributor(s):
//   Gaetan Bricteux

#ifndef _GMSH_LEVELSET_H_
#define _GMSH_LEVELSET_H_

#include <string>
#include <math.h>
#include <stdio.h>
#include <stdlib.h> // for abs()
#include <vector>
#include "GmshMessage.h"
#include "fullMatrix.h"
#include "GModel.h"
#include "MVertex.h"
#include "GmshConfig.h"
#include "mathEvaluator.h"
#include "cartesian.h"
#include "simpleFunction.h"

#if defined(HAVE_ANN)
class ANNkd_tree;
#endif
#if defined(HAVE_POST)
#include "PView.h"
#include "OctreePost.h"
#endif

// PRIMITIVE LEVELSET
#define UNKNOWN      0
#define SPHERE       1
#define PLANE        2
#define GENCYLINDER  3
#define ELLIPS       4
#define CONE         5
#define QUADRIC      6
#define BOX          7
#define CYLINDER     8
#define CONROD       9
#define LSMESH      10
#define LSPOINTS    11 // don't define 'POINTS' as it's reserved by win32

// TOOLS
#define CUT       12
#define UNION     13
#define INTER     14
#define CRACK     15
#define DISK     16

class gLevelset : public simpleFunction<double>
{
protected:
  // negative values of the levelset are inside the domain.
  static const short insideDomain = -1;
  int tag_; // must be greater than 0
public:
  gLevelset() : tag_(-1) {}
  gLevelset(const gLevelset &);
  virtual ~gLevelset(){}
  virtual gLevelset *clone() const
  {
    Msg::Error("Virtual fct called gLevelset::clone()"); return 0;
  }
  virtual double operator() (double x, double y, double z) const = 0;
  bool isInsideDomain(const double &x, const double &y, const double &z) const
  {
    return this->operator()(x,y,z) * insideDomain > 0.;
  }
  bool isOutsideDomain(const double &x, const double &y, const double &z) const
  {
    return this->operator()(x,y,z) * insideDomain < 0.;
  }
  bool isOnBorder(const double &x, const double &y, const double &z) const
  {
    return this->operator()(x,y,z) == 0.;
  }
  virtual std::vector<gLevelset *> getChildren() const = 0;
  virtual double choose (double d1, double d2) const = 0;
  virtual int type() const = 0;
  virtual bool isPrimitive() const = 0;
  void setTag(int t) { tag_ = t; }
  virtual int getTag() const { return tag_; }
  void getPrimitives(std::vector<gLevelset *> &primitives) ;
  void getPrimitivesPO(std::vector<gLevelset *> &primitives) ;
  void getRPN(std::vector<gLevelset *> &gLsRPN) ;
  double H (const double &x, const double &y, const double &z) const
  {
    if (isInsideDomain(x,y,z) || isOnBorder(x,y,z)) return 1.0;
    return 0.0;
  }
  void print() const
  {
    printf("LS : ");
    switch(type()) {
    case SPHERE :      printf("SPHERE"); break;
    case PLANE  :      printf("PLANE"); break;
    case GENCYLINDER : printf("GENCYLINDER"); break;
    case ELLIPS :      printf("ELLIPS"); break;
    case CONE :        printf("CONE"); break;
    case QUADRIC :     printf("QUADRIC"); break;
    case BOX :         printf("BOX"); break;
    case CYLINDER :    printf("CYLINDER"); break;
    case CONROD :      printf("CONROD"); break;
    case CUT :         printf("CUT"); break;
    case UNION :       printf("UNION"); break;
    case INTER :       printf("INTER"); break;
    case LSMESH:       printf("LSMESH"); break;
    case LSPOINTS:     printf("LSPOINTS"); break;
    }
    printf(" Tag=%d\n", getTag());
  }
};

// PRIMITIVES

class gLevelsetPrimitive : public gLevelset
{
public:
  gLevelsetPrimitive() : gLevelset() {}
  gLevelsetPrimitive(const gLevelsetPrimitive &lv) : gLevelset(lv) {}
  gLevelsetPrimitive(int tag) {
    if (tag < 1) {
      printf("Tag of the levelset (%d) must be greater than 0.\n", tag);
      tag = abs(tag);
    }
    tag_ = tag;
  }
  virtual double operator () (double x, double y, double z) const = 0;
  std::vector<gLevelset *> getChildren() const
  {
    std::vector<gLevelset *> p; return p;
  }
  double choose (double d1, double d2) const
  {
    Msg::Error("Cannot use function \"choose\" with a primitive!");
    return d1;
  }
  virtual int type() const = 0;
  bool isPrimitive() const { return true; }
};

class gLevelsetSphere : public gLevelsetPrimitive
{
protected:
  double xc, yc, zc, r;
public:
  gLevelsetSphere (const double &x, const double &y, const double &z,
                   const double &R, int tag=1);
  virtual double operator () (double x, double y, double z) const
  {
    if(r >= 0.)
      return sqrt((xc - x) * (xc - x) + (yc - y) * (yc - y) +
                  (zc - z) * (zc - z)) - r;
    return (- r - sqrt((xc - x) * (xc - x) + (yc - y) * (yc - y) +
                       (zc - z) * (zc - z)));
  }
  void gradient (double x, double y, double z,
                 double & dfdx, double & dfdy, double & dfdz) const;
  void hessian (double x, double y, double z,
                double & dfdxx, double & dfdxy, double & dfdxz,
                double & dfdyx, double & dfdyy, double & dfdyz,
                double & dfdzx, double & dfdzy, double & dfdzz  ) const;
  int type() const { return SPHERE; }
};

class gLevelsetPlane : public gLevelsetPrimitive
{
 protected:
  double a, b, c, d;
public:
  // define the plane _a*x+_b*y+_c*z+_d, with outward normal (a,b,c)
  gLevelsetPlane (const double _a, const double _b, const double _c,
                  const double _d, int tag=1)
    : gLevelsetPrimitive(tag), a(_a), b(_b), c(_c), d(_d) {}
  // define the plane passing through the point pt and with outward normal norm
  gLevelsetPlane (const std::vector<double> &pt, const std::vector<double> &norm,
                  int tag=1);
  gLevelsetPlane (const double *pt, const double *norm, int tag=1);
  // define the plane passing through the 3 points pt1,pt2,pt3 and with outward
  // normal (pt1,pt2)x(pt1,pt3)
  gLevelsetPlane (const double *pt1, const double *pt2, const double *pt3,
                  int tag=1);
  // copy constructor
  gLevelsetPlane(const gLevelsetPlane &lv);
  virtual gLevelset * clone() const{ return new gLevelsetPlane(*this); }
  // return negative value inward and positive value outward
  virtual double operator() (double x, double y, double z) const
  {
    return a * x + b * y + c * z + d;
  }
  int type() const { return PLANE; }
};

class gLevelsetPoints : public gLevelsetPrimitive
{
protected:
  fullMatrix<double> points;
  fullMatrix<double> surf;
  fullMatrix<double> matAInv;
  double delta;
  std::map<SPoint3,double> mapP;
  fullMatrix<double> generateRbfMat(int p, int index,
				    const fullMatrix<double> &nodes1,
				    const fullMatrix<double> &nodes2) const;
  void RbfOp(int p, int index,
	     const fullMatrix<double> &cntrs,
	     const fullMatrix<double> &nodes,
	     fullMatrix<double> &D,
	     bool isLocal = false) const;
  void evalRbfDer(int p, int index,
		  const fullMatrix<double> &cntrs,
		  const fullMatrix<double> &nodes,
		  const fullMatrix<double> &fValues,
		fullMatrix<double> &fApprox, bool isLocal = false) const;
  void setup_level_set(const fullMatrix<double> &cntrs,
		       fullMatrix<double> &level_set_nodes,
		       fullMatrix<double> &level_set_funvals);

public:
  // define the data points
  gLevelsetPoints(fullMatrix<double> &_centers, int tag=1);
  // copy constructor
  gLevelsetPoints(const gLevelsetPoints &lv);
  virtual gLevelset * clone() const{return new gLevelsetPoints(*this);}
  // return negative value inward and positive value outward
  virtual double operator() (double x, double y, double z) const;
  void computeLS(std::vector<MVertex*> &vert);
  int type() const {return LSPOINTS;}
};

class gLevelsetQuadric : public gLevelsetPrimitive
{
protected:
  double A[3][3], B[3], C;
  void translate (const double transl[3]);
  void rotate (const double rotate[3][3]);
  void computeRotationMatrix (const double dir[3], double t[3][3]);
  void computeRotationMatrix (const double dir1[3], const double dir2[3] ,
                              double t[3][3]);
  void Ax (const double x[3], double res[3], double fact=1.0);
  void xAx (const double x[3], double &res, double fact=1.0);
  void init ();
public:
  gLevelsetQuadric(int tag=1) : gLevelsetPrimitive(tag) {init(); }
  gLevelsetQuadric(const gLevelsetQuadric &);
  virtual ~gLevelsetQuadric() {}
  double operator () (double x, double y, double z) const;
  virtual int type() const = 0;
};

class gLevelsetGenCylinder : public gLevelsetQuadric
{
public:
  gLevelsetGenCylinder (const double *pt, const double *dir, const double &R,
                        int tag=1);
  gLevelsetGenCylinder (const gLevelsetGenCylinder& );
  virtual gLevelset * clone() const{ return new gLevelsetGenCylinder(*this); }
  int type() const {return GENCYLINDER;}
};

class gLevelsetEllipsoid : public gLevelsetQuadric
{
public:
  gLevelsetEllipsoid (const double *pt, const double *dir, const double &a,
                      const double &b, const double &c, int tag=1);
  gLevelsetEllipsoid (const gLevelsetEllipsoid& );
  virtual gLevelset * clone() const{ return new gLevelsetEllipsoid(*this); }
  int type() const { return ELLIPS; }
};

class gLevelsetCone : public gLevelsetQuadric
{
public:
  gLevelsetCone (const double *pt, const double *dir, const double &angle, int tag=1);
  gLevelsetCone (const gLevelsetCone& );
  virtual gLevelset * clone() const{ return new gLevelsetCone(*this); }
  int type() const { return CONE; }
};

class gLevelsetGeneralQuadric : public gLevelsetQuadric
{
public:
  gLevelsetGeneralQuadric (const double *pt, const double *dir, const double &x2,
                           const double &y2, const double &z2, const double &z,
                           const double &c, int tag=1);
  gLevelsetGeneralQuadric (const gLevelsetGeneralQuadric& );
  virtual gLevelset * clone() const{ return new gLevelsetGeneralQuadric(*this); }
  int type() const {return QUADRIC;}
};

class gLevelsetPopcorn: public gLevelsetPrimitive
{
  double A;
  double sigma;
  double r0;
  double xc, yc, zc;
public:
  gLevelsetPopcorn(double xc, double yc, double zc, double r0, double A,
                   double sigma, int tag=1);
  ~gLevelsetPopcorn(){}
  double operator () (double x, double y, double z) const;
  int type() const { return UNKNOWN; }
};

// creates the 2D (-approximate- signed distance !) level set corresponding to
// the "shamrock-like" iso-zero from Dobrzynski and Frey, "Anisotropic delaunay
// mesh adaptation for unsteady simulations", 17th International Meshing
// Rountable (2008)(177–194)
class gLevelsetShamrock: public gLevelsetPrimitive
{
  double xmid, a, b;
  int c;
  std::vector<double> iso_x, iso_y;
public:
  gLevelsetShamrock(double xmid, double ymid, double zmid, double a, double b,
                    int c=3, int tag=1);
  ~gLevelsetShamrock(){}
  double operator () (double x, double y, double z) const;
  int type() const { return UNKNOWN; }
};

class gLevelsetMathEval: public gLevelsetPrimitive
{
  mathEvaluator *_expr;
public:
  gLevelsetMathEval(std::string f, int tag=1);
  ~gLevelsetMathEval(){ if (_expr) delete _expr; }
  double operator () (double x, double y, double z) const;
  int type() const { return UNKNOWN; }
};

class gLevelsetMathEvalAll: public gLevelsetPrimitive
{
  mathEvaluator *_expr;
public:
  gLevelsetMathEvalAll(std::vector<std::string> f, int tag=1);
  ~gLevelsetMathEvalAll(){ if (_expr) delete _expr; }
  double operator() (double x, double y, double z) const;
  void gradient (double x, double y, double z,
		double & dfdx, double & dfdy, double & dfdz) const;
  void hessian (double x, double y, double z,
		double & dfdxx, double & dfdxy, double & dfdxz,
		double & dfdyx, double & dfdyy, double & dfdyz,
		double & dfdzx, double & dfdzy, double & dfdzz	) const;
  int type() const { return UNKNOWN; }
};

class gLevelsetSimpleFunction: public gLevelsetPrimitive
{
  simpleFunction<double> *_f;
public:
  gLevelsetSimpleFunction(simpleFunction<double> *f, int tag=1){
    _f = f;
  }
  ~gLevelsetSimpleFunction(){}
  double operator () (double x, double y, double z) const
  {
    return (*_f)(x,y,z);
  }
  int type() const { return UNKNOWN; }
};

#if defined(HAVE_ANN)
class gLevelsetDistMesh: public gLevelsetPrimitive
{
  const int _nbClose;
  std::vector<GEntity*> _entities;
  std::vector<MVertex*> _vertices;
  std::multimap<MVertex*,MElement*> _v2e;
  ANNkd_tree *_kdtree;
public :
  gLevelsetDistMesh(GModel *gm, std::string physical, int nbClose = 5, int tag=1);
  double operator () (double x, double y, double z) const;
  ~gLevelsetDistMesh();
  int type() const { return UNKNOWN; }
};
#endif

#if defined(HAVE_POST)
class gLevelsetPostView : public gLevelsetPrimitive
{
  int _viewIndex;
  OctreePost *_octree;
public:
  gLevelsetPostView(int index, int tag=1) ;
  ~gLevelsetPostView(){ if(_octree) delete _octree;}
  double operator () (double x, double y, double z) const;
  int type() const { return UNKNOWN; }
};
#endif

// TOOLS

class gLevelsetTools : public gLevelset
{
protected:
  std::vector<gLevelset *> children;
  bool _delChildren;//flag to delete only if called from gmsh Parser
public:
  gLevelsetTools () {}
  gLevelsetTools (const std::vector<gLevelset *> &p, bool delC=false)
  {
    children = p; _delChildren=delC;
  }
  gLevelsetTools (const gLevelsetTools &);
  virtual ~gLevelsetTools () {
    if (_delChildren){
      for(int i = 0; i < (int)children.size(); i++)
	delete children[i];
    }
  }
  double operator () (double x, double y, double z) const {
    double d = (*children[0])(x, y, z);
    for (int i = 1; i < (int)children.size(); i++){
      double dt = (*children[i])(x, y, z);
      d = choose(d, dt);
    }
    return d;
  }
  std::vector<gLevelset *> getChildren() const {
    if(children.size() != 1) return children;
    return children[0]->getChildren();
  }
  virtual double choose (double d1, double d2) const = 0;
  virtual int type2() const = 0;
  virtual int type() const
  {
    if(children.size() != 1) return type2();
    return children[0]->type();
  }
  bool isPrimitive() const
  {
    if(children.size() != 1) return false;
    return children[0]->isPrimitive();
  }
  int getTag() const
  {
    if(children.size() != 1) return tag_;
    return children[0]->getTag();
  }
};

class gLevelsetReverse : public gLevelset
{
protected:
  gLevelset *ls;
public:
  gLevelsetReverse (gLevelset *p) : ls(p){}
  double operator () (double x, double y, double z) const
  {
    return -(*ls)(x, y, z);
  }
  std::vector<gLevelset *> getChildren() const { return ls->getChildren(); }
  bool isPrimitive() const {return ls->isPrimitive();}
  virtual double choose (double d1, double d2) const { return -ls->choose(d1,d2); }
  virtual int type() const { return ls->type(); }
  int getTag() const { return ls->getTag(); }
};

// This levelset takes the first levelset in the list as the object and the
// others as tools that cut it
class gLevelsetCut : public gLevelsetTools
{
public:
  gLevelsetCut (std::vector<gLevelset *> p, bool delC=false)
    : gLevelsetTools(p,delC) {}
  double choose (double d1, double d2) const
  {
    return (d1 > -d2) ? d1 : -d2; // greater of d1 and -d2
  }
  gLevelsetCut(const gLevelsetCut &lv) : gLevelsetTools(lv){}
  virtual gLevelset * clone() const { return new gLevelsetCut(*this); }
  int type2() const { return CUT; }
};

// This levelset takes the minimum
class gLevelsetUnion : public gLevelsetTools
{
public:
  gLevelsetUnion (std::vector<gLevelset *> p, bool delC=false)
    : gLevelsetTools(p,delC) { }
  gLevelsetUnion(const gLevelsetUnion &lv):gLevelsetTools(lv){}
  virtual gLevelset * clone() const{return new gLevelsetUnion(*this);}
  double choose (double d1, double d2) const
  {
    return (d1 < d2) ? d1 : d2; // lesser of d1 and d2
  }
  int type2() const { return UNION; }
};

// This levelset takes the maximum
class gLevelsetIntersection : public gLevelsetTools
{
public:
  gLevelsetIntersection (std::vector<gLevelset *> p, bool delC=false)
  : gLevelsetTools(p,delC) { }
  gLevelsetIntersection(const gLevelsetIntersection &lv):gLevelsetTools(lv) { }
  virtual gLevelset *clone() const { return new gLevelsetIntersection(*this); }
  double choose (double d1, double d2) const
  {
    return (d1 > d2) ? d1 : d2; // greater of d1 and d2
  }
  int type2() const { return INTER; }
};

// Crack defined by a normal and a tangent levelset
class gLevelsetCrack : public gLevelsetTools
{
public:
  gLevelsetCrack (std::vector<gLevelset *> p, bool delC=false)
  {
    if (p.size() != 2)
      printf("Error : gLevelsetCrack needs 2 levelsets\n");
    children.push_back(p[0]);
    children.push_back(new gLevelsetReverse(p[0]));
    if(p[1]) children.push_back(p[1]);
    _delChildren = delC;
  }
  double choose (double d1, double d2) const
  {
    return (d1 > d2) ? d1 : d2; // greater of d1 and d2
  }
  int type2() const {return CRACK;}
};


// IMPROVED LEVELSET

class gLevelsetImproved : public gLevelset
{
protected:
  gLevelset *Ls;
public:
  gLevelsetImproved(){}
  gLevelsetImproved(const gLevelsetImproved &lv);
  double operator() (double x, double y, double z) const { return (*Ls)(x, y, z); }
  std::vector<gLevelset *> getChildren() const { return Ls->getChildren(); }
  double choose (double d1, double d2) const { return Ls->choose(d1, d2); }
  virtual int type() const = 0;
  bool isPrimitive() const {return Ls->isPrimitive();}
};

class gLevelsetBox : public gLevelsetImproved
{
public:
  // create a box with parallel faces :
  //    pt is a corner of the box,
  //    dir1 is the direction of the first edge starting from pt,
  //    dir2 is the direction of the second edge starting from pt,
  //    dir3 is the direction of the third edge starting from pt,
  //    a is the length of the first edge starting from pt,
  //    b is the length of the second edge starting from pt,
  //    c is the length of the third edge starting from pt.
  // tags of the faces are : face normal to dir3 and not including pt : tag+0
  //                         face normal to dir3 and     including pt : tag+1
  //                         face normal to dir2 and     including pt : tag+2
  //                         face normal to dir2 and not including pt : tag+3
  //                         face normal to dir1 and not including pt : tag+4
  //                         face normal to dir1 and     including pt : tag+5
  gLevelsetBox(const double *pt, const double *dir1, const double *dir2,
               const double *dir3, const double &a, const double &b,
               const double &c, int tag=1);
  // create a box with the 8 vertices (pt1,...,pt8).
  // check if the faces are planar.
  // tags of the faces are : face(pt5,pt6,pt7,pt8) : tag+0
  //                         face(pt1,pt4,pt3,pt2) : tag+1
  //                         face(pt1,pt2,pt6,pt5) : tag+2
  //                         face(pt3,pt4,pt8,pt7) : tag+3
  //                         face(pt2,pt3,pt7,pt6) : tag+4
  //                         face(pt1,pt5,pt8,pt4) : tag+5
  gLevelsetBox(const double *pt1, const double *pt2, const double *pt3,
               const double *pt4, const double *pt5, const double *pt6,
               const double *pt7, const double *pt8, int tag=1);
  gLevelsetBox(const gLevelsetBox &);
  virtual gLevelset * clone() const{return new gLevelsetBox(*this);}
  int type() const { return BOX; }
};

class gLevelsetCylinder : public gLevelsetImproved
{
public:
  // create a cylinder : pt is the point in the middle of the cylinder base,
  //                     dir is the direction of the cylinder axis,
  //                     R is the outer radius of the cylinder,
  //                     H is the height of the cylinder.
  // tags of the faces are : exterior face :             tag+0
  //                         plane face including pt :   tag+1
  //                         plane face opposite to pt : tag+2
  gLevelsetCylinder (const std::vector<double> &pt,
                     const std::vector<double> &dir, const double &R,
                     const double &H, int tag=1);
  gLevelsetCylinder (const double *pt, const double *dir, const double &R,
                     const double &H, int tag=1);
  // create a cylinder : pt is the point in the middle of the cylinder base,
  //                     dir is the direction of the cylinder axis,
  //                     R is the outer radius of the cylinder,
  //                     r is the inner radius of the cylinder,
  //                     H is the height of the cylinder.
  // tags of the faces are : exterior face :             tag+0
  //                         plane face including pt :   tag+1
  //                         plane face opposite to pt : tag+2
  //                         interior face :             tag+3
  gLevelsetCylinder (const double *pt, const double *dir, const double &R,
                     const double &r, const double &H, int tag=1);
  gLevelsetCylinder(const gLevelsetCylinder &);
  virtual gLevelset * clone() const{return new gLevelsetCylinder(*this);}
  int type() const { return CYLINDER; }
};

class gLevelsetConrod : public gLevelsetImproved
{
public:
  // create a connecting rod :
  //    pt is the point in the middle of the first bore,
  //    dir1 is the direction of the rod,
  //    dir2 is the direction of the axis of the bore,
  //    H1 is height of the first cylinder,
  //    H2 is the height of the second cylinder,
  //    H3 is the height of the rod,
  //    R1 is the outer radius of the first cylinder,
  //    r1 is the inner radius of the first cylinder,
  //    R2 is the outer radius of the second cylinder,
  //    r2 is the inner radius of the second cylinder,
  //    L1 is the width of the rod in the plane passing through the middle
  //       of the first bore,
  //    L2 is the width of the rod in the plane passing through the middle
  //       of the second bore,
  //    E is the distance between the axis of the cylinders.
  // tags of the faces are : bottom face (+dir2) of the bore :      tag+2
  //                         top    face (-dir2) of the bore :      tag+3
  //                         rear   face (-dir1xdir2) of the bore : tag+4
  //                         front  face (+dir1xdir2) of the bore : tag+5
  //                         exterior face of the first cylinder :  tag+6
  //                         bottom   face of the first cylinder :  tag+7
  //                         top      face of the first cylinder :  tag+8
  //                         exterior face of the second cylinder : tag+9
  //                         bottom   face of the second cylinder : tag+10
  //                         top      face of the second cylinder : tag+11
  //                         interior face of the first  cylinder : tag+12
  //                         interior face of the second cylinder : tag+13
  gLevelsetConrod (const double *pt, const double *dir1, const double *dir2,
                   const double &H1, const double &H2, const double &H3,
                   const double &R1, const double &r1, const double &R2,
                   const double &r2, const double &L1, const double &L2,
                   const double &E, int tag=1);
  gLevelsetConrod(const gLevelsetConrod &);
  virtual gLevelset * clone() const{ return new gLevelsetConrod(*this); }
  int type() const { return CONROD; }
};

/*
class gLevelsetDisk : public gLevelsetTools
{
public:
   // define the disk of given radius centered at a point pt and with outward
   // normal norm
  gLevelsetDisk (std::vector<double> pt, std::vector<double> dir,
                 const double R, bool delC=false)
  {
    double *ptP, *normP;
    ptP[0] = pt[0]; ptP[1] = pt[1]; ptP[2] = pt[2];
    normP[0] = dir[0];normP[1] = dir[1]; normP[2] = dir[2];
    gLevelsetPlane *plane = new gLevelsetPlane(ptP, normP);
    children.push_back(plane);
    children.push_back(new gLevelsetReverse(plane));
    double H = 4.*R;
    double *ptC, *normC;
    double val = sqrt(dir[0]*dir[0]+dir[1]*dir[1]+dir[2]*dir[2]);
    normC[0] = dir[0]/val; normC[1] = dir[1]/val; normC[2] = dir[2]/val;
    ptC[0] = pt[0]-2.*R*normC[0];
    ptC[1] = pt[1]-2.*R*normC[1];
    ptC[2] = pt[2]-2.*R*normC[2];
    gLevelsetCylinder *cyl = new gLevelsetCylinder(ptC, normC, R, H);
    children.push_back(cyl);
    _delChildren = delC;
  }
  double choose (double d1, double d2) const
  {
    return (d1 > d2) ? d1 : d2; // greater of d1 and d2
  }
  int type2() const {return DISK;}
};
*/

class gLevelsetNACA00: public gLevelsetPrimitive
{
  double _x0, _y0, _c, _t;
public:
  gLevelsetNACA00(double x0, double y0, double c, double t);
  ~gLevelsetNACA00() {}
  double operator () (double x, double y, double z) const;
  void gradient (double x, double y, double z,
    double & dfdx, double & dfdy, double & dfdz) const;
  void hessian (double x, double y, double z,
    double & dfdxx, double & dfdxy, double & dfdxz,
    double & dfdyx, double & dfdyy, double & dfdyz,
    double & dfdzx, double & dfdzy, double & dfdzz  ) const;
  int type() const { return UNKNOWN; }
private:
  void getClosestBndPoint(const double x, const double y, const double z,
                          double &xb, double &yb, double &curvRad,
                          bool &in) const;
};

#endif
