//
// This file is part of MorphoGraphX - http://www.MorphoGraphX.org
// Copyright (C) 2012-2015 Richard S. Smith and collaborators.
//
// If you use MorphoGraphX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoGraphX is free software, and is licensed under under the terms of the
// GNU General (GPL) Public License version 2.0, http://www.gnu.org/licenses.
//
#ifndef MESH_PROCESSS_CELLAXIS_HPP
#define MESH_PROCESSS_CELLAXIS_HPP

#include <Process.hpp>

#include <MeshProcessCellMesh.hpp>

namespace mgx
{

  //mgx_EXPORT SymmetricTensor customDirectionTensor(SymmetricTensor& origTensor, Point3f customX, Point3f customY, bool shear);

  ///\addtogroup MeshProcess
  ///@{
  /**
   * \class SaveCellAxis <ProcessCellAxis.hpp>
   */
  class mgxBase_EXPORT SaveCellAxis : public Process
  {
  public:
    SaveCellAxis(const Process& process) : Process(process)
    {
      setName("Mesh/Cell Axis/Cell Axis Save");
      setDesc("Save 3D Cell Axis to a csv file.");
	    setIcon(QIcon(":/images/CellAxisSave.png"));

      addParm("Output File","Path to output file","");
      addParm("Data","Data to be saved","Original Tensor", QStringList() << "Original Tensor" << "Custom Tensor" << "Visualized Axis");
	  }

    bool initialize(QWidget* parent);

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Output File"), parm("Data"));
    }

    bool run(Mesh* mesh, const QString& filename, const QString& type);
  };

  /**
   * \class LoadCellAxis <ProcessCellAxis.hpp>
   */
  class mgxBase_EXPORT LoadCellAxis : public Process {
  public:
    LoadCellAxis(const Process& process) : Process(process)
    {
      setName("Mesh/Cell Axis/Cell Axis Load");
	  setDesc("Load cell axis from a spreadsheet file.");
	  setIcon(QIcon(":/images/CellAxisOpen.png"));

	  addParm("Type","Load PDG, Fibril Orientations or Curvature from a spreadsheet file.","PDG", QStringList() << "PDG" << "PCA" << "fibril" << "curvature" << "polarization");
	  addParm("Input File","Path to input file. If empty, a browser will open.","");
	}

    bool initialize(QWidget* parent);

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Type"), parm("Input File"));
    }

    bool run(Mesh* mesh, const QString& type, const QString& filename);

  };

  /**
   * \class ClearCellAxis <ProcessCellAxis.hpp>
   *
   * Clear the cell axis on the mesh
   */
  class mgxBase_EXPORT ClearCellAxis : public Process
  {
  public:
    ClearCellAxis(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Cell Axis Clear");
      setDesc("Remove any cell axis information from the current mesh.");
	  setIcon(QIcon(":/images/CellAxisClear.png"));
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh());
    }

    bool run(Mesh* mesh);

  };
  typedef std::map<int, Point3d> IntP3dMap;
  typedef std::map<Point2i, Point3d> P2iP3dMap;
  /**
   * \class CreateCustomOrthogHeat <MeshProcessCellAxis.hpp>
   *
   * Create an orthogonal coordinate system based on a heatmap, by solving a Poisson equation
   * Brendan's implementation based on geodesics in heat was used as a reference
   */
/*
  class mgxBase_EXPORT CreateCustomOrthogHeat : public Process
  {
  public:
  	CreateCustomOrthogHeat(const Process& process) : Process(process) {}

    bool run(const QStringList& parms)
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parms[0]), parms[1], true);
    }
    bool run(Mesh *m, bool projectOnSurface, QString normalize, bool display);

    QString name() const { return "Mesh/Cell Axis/Custom/Experimental/Create Orthogonal Heatmap"; }
    QString description() const { return
      "Create Orthogonal Heatmap"; }
    QStringList parmNames() const { return QStringList()
      << "Project Directions on Surface"
      << "Normalize" ; }
    QStringList parmDefaults() const { return QStringList()
      << "Yes"
      << "no" ; }
    QIcon icon() const { return QIcon(":/images/PDG.png"); }

    ParmChoiceMap parmChoice() const {
      ParmChoiceMap map;
      map[0] = booleanChoice();
      map[1] = QStringList() << "no" << "norm" << "square norm";
      return map;
    }


  };
*/

  /**
   * \class CreateIntrinsicHeat <MeshProcessCellAxis.hpp>
   *
   * Create an intrinsic polarity heatmap
   * Brendan's implementation based on geodesics in heat was used as a reference
   */
  class mgxBase_EXPORT CreateIntrinsicHeat : public Process
  {
  public:
  	CreateIntrinsicHeat(const Process& process) : Process(process)
  	{
	    setName("Mesh/Cell Axis/Custom/Create Intrinsic Heatmap");
      setDesc("Create Intrinsic Heatmap");
	    setIcon(QIcon(":/images/PDG.png"));

      addParm("Project Directions on Surface","","Yes",booleanChoice());
      addParm("Normalize","","no", QStringList() << "no" << "norm" << "square norm");
	  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parm("Project Directions on Surface")), parm("Normalize"), true);
    }
    bool run(Mesh *m, bool projectOnSurface, QString normalize, bool display);

  };


  class mgxBase_EXPORT CreateCustomDirectionsHeat : public Process
  {
  public:
    CreateCustomDirectionsHeat(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Custom/Create Heatmap Directions");
	  setDesc("Create Custom Heatmap Directions");
	  setIcon(QIcon(":/images/PDG.png"));

	  addParm("Project Directions on Surface","","Yes",booleanChoice());
	  addParm("Normalize","","no", QStringList() << "no" << "norm" << "square norm");
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parm("Project Directions on Surface")), parm("Normalize"), true);
    }

    bool run(Mesh *m, bool projectOnSurface, QString normalize, bool display);

  };

  class mgxBase_EXPORT SmoothCustomDirections : public Process
  {
  public:
    SmoothCustomDirections(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Custom/Smooth Custom Directions");
    setDesc("Smooth Custom Directions");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Weight by Cell Area","Weight by Cell Area","Yes",booleanChoice());
    addParm("Project Directions on Surface","Project Directions on cell surfaces after smoothing","Yes",booleanChoice());
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parm("Weight by Cell Area")), stringToBool(parm("Project Directions on Surface")), true);
    }

    bool run(Mesh *m, bool weight, bool project, bool display);

  };

  class mgxBase_EXPORT CopyCustomDirections : public Process
  {
  public:
    CopyCustomDirections(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Custom/Copy Custom Directions to Other Mesh");
    setDesc("Copy Custom Directions");
    setIcon(QIcon(":/images/PDG.png"));
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), otherMesh());
    }

    bool run(Mesh *m1, Mesh *m2);

  };

  class mgxBase_EXPORT ImportCustomDirections : public Process
  {
  public:
    ImportCustomDirections(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Custom/Import Tensor from Attr Map");
    setDesc("Copy Custom Directions");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Prefix","Prefix","Measure Label Tensor ");
    addParm("Name","Name","Custom Directions");
    addParm("Mesh","Mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh");
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), otherMesh(), parm("Prefix"), parm("Name"), parm("Mesh"));
    }

    bool run(Mesh *m1, Mesh *m2, QString prefix, QString name, QString nameMesh);

  };

  class mgxBase_EXPORT ImportCustomDirectionsVector : public Process
  {
  public:
    ImportCustomDirectionsVector(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Custom/Import Vector from Attr Map");
    setDesc("Copy Custom Directions");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Prefix","Prefix","Measure Label Vector ");
    addParm("Name","Name","Custom Directions");
    addParm("Dimension","Dimension","X",QStringList() << "X" << "Y" << "Z");
    addParm("Mesh","Mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh");
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), otherMesh(), parm("Prefix"), parm("Name"), parm("Dimension"), parm("Mesh"));
    }

    bool run(Mesh *m1, Mesh *m2, QString prefix, QString name, QString dimension, QString nameMesh);

  };

  /**
   * \class CreateCustomDirectionsSurface <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CreateCustomDirectionsSurface : public Process
  {
  public:
    CreateCustomDirectionsSurface(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis 3D/Custom/Create Surface Directions");
    setDesc("Create Custom Bezier Directions");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Custom X","Custom X","Surface Direction",QStringList() << "None" << "Surface Direction" << "Surface Normal");
    addParm("Custom Y","Custom Y","None",QStringList() << "None" << "Surface Direction" << "Surface Normal");
    addParm("Custom Z","Custom Z","None",QStringList() << "None" << "Surface Direction" << "Surface Normal");
    }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      Mesh* m = currentMesh();
      Mesh* m2 = otherMesh();
      return run(m, m2, parm("Custom X"), parm("Custom Y"), parm("Custom Z"), true);
    }
    bool run(Mesh *m, Mesh *m2, QString customX, QString customY, QString customZ, bool display);


  };

  /**
   * \class CustomDirectionsEnforceOrthogonality <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CustomDirectionsEnforceOrthogonality : public Process
  {
  public:
    CustomDirectionsEnforceOrthogonality(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis 3D/Custom/Create Orthogonal Direction");
    setDesc("Creates a orthogonal direction based on the two specified directions and overwrites the third direction with it.\n"
      "Optional: edit the secondary direction to be orthogonal two the primary and new third direction.");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Primary Axis","Primary Axis","X",QStringList() << "X" << "Y" << "Z");
    addParm("Secondary Axis","Secondary Axis","Y",QStringList() << "X" << "Y" << "Z");
    addParm("Change Secondary Axis","Change Secondary Axis","Yes",booleanChoice());
    }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      Mesh* m = currentMesh();
      return run(m, parm("Primary Axis"), parm("Secondary Axis"), stringToBool(parm("Change Secondary Axis")), true);
    }
    bool run(Mesh *m, QString primX, QString secX, bool changeSecondary, bool display);


  };

  /**
   * \class CustomDirectionsClear <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CustomDirectionsClear : public Process
  {
  public:
    CustomDirectionsClear(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis 3D/Custom/Clear Custom Directions");
    setDesc("Clear Custom Directions");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Clear Custom X","Clear Custom X","Yes",booleanChoice());
    addParm("Clear Custom Y","Clear Custom Y","Yes",booleanChoice());
    addParm("Clear Custom Z","Clear Custom Z","Yes",booleanChoice());
    }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      Mesh* m = currentMesh();
      return run(m, stringToBool(parm("Clear Custom X")), stringToBool(parm("Clear Custom Y")), stringToBool(parm("Clear Custom Z")), true);
    }
    bool run(Mesh *m, bool clearX, bool clearY, bool clearZ, bool display);


  };



  /**
   * \class CreateCustomDirectionsBezier <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CreateCustomDirectionsBezier : public Process
  {
  public:
    CreateCustomDirectionsBezier(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Custom/Create Bezier Grid Directions");
	  setDesc("Create Custom Bezier Directions");
	  setIcon(QIcon(":/images/PDG.png"));

    addParm("Custom X","Custom X","Bezier X",QStringList() << "None" << "Bezier X" << "Bezier Y" << "Bezier Normal");
    addParm("Custom Y","Custom Y","Bezier Y",QStringList() << "None" << "Bezier X" << "Bezier Y" << "Bezier Normal");

	  addParm("Project Directions on Surface","","Yes",booleanChoice());



	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Custom X"), parm("Custom Y"), stringToBool(parm("Project Directions on Surface")), true);
    }

    bool run(Mesh *m, QString customX, QString customY, bool projectOnSurface, bool display);
  };


  /**
   * \class ProjectCustomDirections <MeshProcessCellAxis.hpp>
   *
   * Projects custom directions on the cell plane
   */
  class mgxBase_EXPORT ProjectCustomDirections : public Process
  {
  public:
    ProjectCustomDirections(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Custom/Project Directions");
	  setDesc("Project custom directions on the cell plane");
	  setIcon(QIcon(":/images/PDG.png"));
      addParm("Make Orthogonal","Makes custom axes orthogonal","None",QStringList() << "None" << "Y from X" << "X from Y");

	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(),parm("Make Orthogonal"));
    }

    bool run(Mesh *m,QString makeOrthog);
  };


  /**
   * \class CreateCustomDirectionsBezier <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CreateCustomDirectionsBezierLine : public Process
  {
  public:
    CreateCustomDirectionsBezierLine(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Custom/Create Bezier Line Directions");
    setDesc("Create Custom Directions from a Bezier Line\n"
      "Longitudinal - Along the Bezier line\n"
      "Radial - Towards the Bezier line\n"
      "Circumferential - Around the Bezier line\n"
      "(always from the cell center)");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Custom X","Custom X","Radial",QStringList() << "None" << "Radial" << "Circumferential" << "Longitudinal");
    addParm("Custom Y","Custom Y","Circumferential",QStringList() << "None" << "Radial" << "Circumferential" << "Longitudinal");
    
    addParm("Project Directions on Surface","","Yes",booleanChoice());

  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Custom X"), parm("Custom Y"), stringToBool(parm("Project Directions on Surface")), true);
    }

    bool run(Mesh *m, QString customX, QString customY, bool projectOnSurface, bool display);
  };


  /**
   * \class CreateCustomDirectionsBezier <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CreateCustomDirectionsBezierLine3D : public Process
  {
  public:
    CreateCustomDirectionsBezierLine3D(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis 3D/Custom/Create Bezier Line Directions");
    setDesc("Create Custom Directions from a Bezier Line\n"
      "Longitudinal - Along the Bezier line\n"
      "Radial - Towards the Bezier line\n"
      "Circumferential - Around the Bezier line\n"
      "(always from the cell center)");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Custom X","Custom X","Radial",QStringList() << "None" << "Radial" << "Circumferential" << "Longitudinal");
    addParm("Custom Y","Custom Y","Circumferential",QStringList() << "None" << "Radial" << "Circumferential" << "Longitudinal");
    addParm("Custom Z","Custom Z","Longitudinal",QStringList() << "None" << "Radial" << "Circumferential" << "Longitudinal");


  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Custom X"), parm("Custom Y"), parm("Custom Z"), true);
      
    }
    bool run(Mesh *m, QString customX, QString customY, QString customZ, bool display);
  };

  /**
   * \class CreateCustomDirectionsBezier <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CreateCustomDirectionsBezier3D : public Process
  {
  public:
    CreateCustomDirectionsBezier3D(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis 3D/Custom/Create Bezier Grid Directions");
      setDesc("Create Custom Bezier Directions for volumetric cells");
	  setIcon(QIcon(":/images/PDG.png"));

    addParm("Custom X","Custom X","Bezier X",QStringList() << "None" << "Bezier X" << "Bezier Y" << "Bezier Normal");
    addParm("Custom Y","Custom Y","Bezier Y",QStringList() << "None" << "Bezier X" << "Bezier Y" << "Bezier Normal");
    addParm("Custom Z","Custom Z","Bezier Normal",QStringList() << "None" << "Bezier X" << "Bezier Y" << "Bezier Normal");

	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Custom X"), parm("Custom Y"), parm("Custom Z"), true);
    }

    bool run(Mesh *m, QString customX, QString customY, QString customZ, bool display);

  };


  /**
   * \class CustomDirectionsAngle <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CustomDirectionsAngle : public Process
  {
  public:
    CustomDirectionsAngle(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Custom/Custom Direction Angle");
	  setDesc("Generate a heatmap of the angle (deg) between the specified active cell axis and a previously created custom direction.");
	  setIcon(QIcon(":/images/PDG.png"));

	  addParm("Direction Axis","Direction Axis","Max", QStringList() << "Max" << "Min");
	  addParm("Direction Custom","Direction Custom","X", QStringList() << "X" << "Y");
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Direction Axis"), parm("Direction Custom"));
    }

    bool run(Mesh *m, QString dirAxis, QString dirCustom);

  };

  /**
   * \class CustomDirectionsAngle3D <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CustomDirectionsAngle3D : public Process
  {
  public:
    CustomDirectionsAngle3D(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis 3D/Custom/Custom Direction Angle 3D");
    setDesc("Generate a heatmap of the angle (deg) between the specified active cell axis and a previously created custom direction.");
    setIcon(QIcon(":/images/PDG.png"));

    addParm("Direction Axis","","Max", QStringList() << "Max" << "Mid" << "Min");
    addParm("Direction Custom","","X", QStringList() << "X" << "Y" << "Z");
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Direction Axis"), parm("Direction Custom"));
    }

    bool run(Mesh *m, QString dirAxis, QString dirCustom);

  };


  /**
   * \class DisplayCustomOrientations <ProcessFibril.hpp>
   *
   * Change the representation of the fibril orientation after it has been
   * computed.
   */
  class mgxBase_EXPORT DisplayCustomOrientations : public Process
  {
  public:
    DisplayCustomOrientations(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Custom/Display Custom Orientations");
	  setDesc("Display the orientations of the custom orientations attribute map");
	  setIcon(QIcon(":/images/PrincipalOrientations.png"));

	  addParm("Directions","Directions to display","XY", QStringList() << "X" << "Y" << "Z" << "XY" << "XYZ");
	  addParm("Line Color","Line Color","aliceblue", QColor::colorNames());
	  addParm("Line Width","Line Width","3.0");
	  addParm("Line Scale","Length of the vectors = Scale * orientation strength.","3.0");
	  addParm("Line Offset","Draw the vector ends a bit tilted up for proper display on surfaces.","0.1");
	  addParm("Threshold","Minimal value of orientation strength required for drawing main direction.","0.0");
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;

      return run(currentMesh(), parm("Directions"), QColor(parm("Line Color")), parm("Line Width").toFloat(),
                            parm("Line Scale").toFloat(), parm("Line Offset").toFloat(), parm("Threshold").toFloat());
    }

    bool run(Mesh* mesh, QString directions, const QColor& ColorMax,
        float AxisLineWidth, float ScaleAxisLength, float AxisOffset, float OrientationThreshold);

  };

  // /**
  //  * \class DisplayCustomOrientations <ProcessFibril.hpp>
  //  *
  //  * Change the representation of the fibril orientation after it has been
  //  * computed.
  //  */
  // class mgxBase_EXPORT DisplayCustomOrientations : public Process
  // {
  // public:
  //   DisplayCustomOrientations(const Process& process) : Process(process)
  //   {
  //   setName("Mesh/Cell Axis/Custom/Display Custom Orientations");
  //   setDesc("Display the orientations of the custom orientations attribute map");
  //   setIcon(QIcon(":/images/PrincipalOrientations.png"));

  //   addParm("Directions","Directions to display","XY", QStringList() << "X" << "Y" << "Z" << "XY" << "XYZ");
  //   addParm("Line Color","Line Color","aliceblue", QColor::colorNames());
  //   addParm("Line Width","Line Width","3.0");
  //   addParm("Line Scale","Length of the vectors = Scale * orientation strength.","3.0");
  //   addParm("Line Offset","Draw the vector ends a bit tilted up for proper display on surfaces.","0.1");
  // }

  //   bool run()
  //   {
  //     if(!checkState().mesh(MESH_NON_EMPTY))
  //       return false;

  //     return run(currentMesh(), parm("Directions"), QColor(parm("Line Color")), parm("Line Width").toFloat(),
  //                           parm("Line Scale").toFloat(), parm("Line Offset").toFloat());
  //   }

  //   bool run(Mesh* mesh, QString directions, const QColor& ColorMax,
  //       float AxisLineWidth, float ScaleAxisLength, float AxisOffset);

  // };


  /**
   * \class DisplayCustomOrientations <ProcessFibril.hpp>
   *
   * Change the representation of the fibril orientation after it has been
   * computed.
   */
  class mgxBase_EXPORT DisplayCellAxisFromTensor : public Process
  {
  public:
    DisplayCellAxisFromTensor(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Display Template");
    setDesc("Process to draw cell axis used by other processes");
    setIcon(QIcon(":/images/PrincipalOrientations.png"));

  }

    bool run()
    {
      //if(!checkState().mesh(MESH_NON_EMPTY))
      return true;

      // return run(currentMesh(), parm("Directions"), QColor(parm("Line Color")), parm("Line Width").toFloat(),
      //                       parm("Line Scale").toFloat(), parm("Line Offset").toFloat(), parm("Threshold").toFloat());
    }

    bool run(Mesh *mesh, IntSymTensorAttr &cellAxis,
    QString mode, bool custom, const QColor& qaxisColorX, const QColor& qaxisColorY, const QColor& qaxisColorZ,const QColor& qaxisColorShrink,
    double axisLineWidth, double axisLineScale, double axisOffset, double orientationThreshold, bool strain);

  };



  /**
   * \class DisplayCustomOrientations3D <ProcessFibril.hpp>
   *
   * TBD
   */
  class mgxBase_EXPORT DisplayCustomOrientations3D : public Process
  {
  public:
    DisplayCustomOrientations3D(const Process& process) : Process(process)
    {

	  setName("Mesh/Cell Axis 3D/Custom/Display Custom Orientations");
	  setDesc("Display the orientations of the custom orientations attribute map");
	  setIcon(QIcon(":/images/PrincipalOrientations.png"));

	  addParm("Directions","Directions to display","XYZ", QStringList() << "X" << "Y" << "Z" << "XY" << "XZ" << "YZ" << "XYZ");
	  addParm("Line Color","Line Color","aliceblue", QColor::colorNames());
	  addParm("Line Width","Line Width","3.0");
	  addParm("Line Scale","Length of the vectors = Scale * orientation strength.","3.0");
	  addParm("Threshold","Minimal value of orientation strength required for drawing main direction.","0.0");
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;

      return run(currentMesh(), parm("Directions"), QColor(parm("Line Color")), parm("Line Width").toFloat(),
                            parm("Line Scale").toFloat(), parm("Threshold").toFloat());
    }

    bool run(Mesh* mesh, QString directions, const QColor& ColorMax,
        float AxisLineWidth, float ScaleAxisLength, float OrientationThreshold);

  };

  /**
   * \class CellAxisAttrMapExport <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CellAxisAttrMapExport : public Process
  {
  public:
    CellAxisAttrMapExport(const Process& process) : Process(process)
    {
	  setName("Mesh/Cell Axis/Cell Axis Export To Attr Map");
	  setDesc("");
	  setIcon(QIcon(":/images/PDG.png"));

	  addParm("Prefix","","Measure Label Tensor");
	  addParm("Name","","AxisAttr");
    addParm("Key","Keys of Attr Map","Label", QStringList() << "Label" << "Parent");
    addParm("Value","Type of value to be saved","Label Axis", QStringList() << "Label Axis" << "Parent Axis");  
    addParm("Save to","Save to which mesh","Active Mesh", QStringList() << "Active Mesh" << "Other Mesh");
    addParm("Clean","Remove entries for non-existent cells","No",booleanChoice());
	}

    bool run()
    {
      if(!checkState().mesh(MESH_ANY))
        return false;
      Mesh *m = currentMesh();
      Mesh *m2;
      if(currentMesh() == mesh(0))
        m2 = mesh(1);
      else
        m2 = mesh(0);
      return run(m,m2, parm("Prefix"), parm("Name"), parm("Key"), parm("Value"), parm("Save to"),stringToBool(parm("Clean")));
    }

    bool run(Mesh *m, Mesh *m2, QString prefix, QString name, QString key, QString value, QString meshNumber, bool cleanMesh);

  };

  /**
   * \class CellAxisAttrMapImport <MeshProcessCellAxis.hpp>
   *
   * align the cell axis to customly chosen directions of a bezier grid
   */
  class mgxBase_EXPORT CellAxisAttrMapImport : public Process
  {
  public:
    CellAxisAttrMapImport(const Process& process) : Process(process)
    {

	  setName("Mesh/Cell Axis/Cell Axis Import From Attr Map");
	  setDesc("");
	  setIcon(QIcon(":/images/PDG.png"));

	  addParm("Type","","PDG", QStringList() << "PDG" << "PCA" << "fibril" << "curvature" << "polarization");
	  addParm("Prefix","Prefix","Measure Label Tensor");
    addParm("Name","Name","AxisAttr");
    addParm("Project on cell plane","","No",booleanChoice());
	}

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Type"), parm("Prefix"), parm("Name"), stringToBool(parm("Project on cell plane")));
    }

    bool run(Mesh *m, QString type, QString prefix, QString name, bool reproject);

  };

 //  /**
 //   * \class CellAxisPDGAngle <MeshProcessCellAxis.hpp>
 //   *
 //   *
 //   */
 //  class mgxBase_EXPORT CellAxisPDGAngle : public Process
 //  {
 //  public:
 //    CellAxisPDGAngle(const Process& process) : Process(process)
 //    {

	//   setName("Mesh/Cell Axis/ToBeDeleted/PDG Angle");
	//   setDesc("");
	//   setIcon(QIcon(":/images/PDG.png"));

	//   addParm("T1 mesh","","");
	//   addParm("T2 mesh","","");
	//   addParm("T3 mesh","","");
	//   addParm("T1-T2 parents","","");
	//   addParm("T1-T3 parents","","");
	//   addParm("Isotropy threshold","","1.2");
	// }

 //    bool initialize(QWidget *parent);

 //    bool run();

 //    bool run(QString meshNameT1, QString meshNameT2, QString meshNameT3,QString T1T2,QString T1T3,QString isoThresh);

 //  };


  class mgxBase_EXPORT CellAxisAngles : public Process
  {
    QString filename;

  public:
    CellAxisAngles(const Process& process) : Process(process)
    {
    setName("Mesh/Cell Axis/Compute Angles");
      setDesc("Calculate angles between different cell axis");
    setIcon(QIcon(":/images/MeasureAngles.png"));

      addParm("Cell Axis Attr 1","Cell Axis Attr 1","Shape2D",
              QStringList() << "Curvature" << "CustomDirections" << "DeformationGradient" << "FibrilOrientations" << "PDGs" << "Polarity" << "Shape2D");
      addParm("Direction 1","Direction","Max/X",QStringList() << "Max/X" << "Min/Y");
      addParm("Isotropy Threshold 1","Ignore cells when their cell axis are close to isotropic, i.e. when their anisotropy is below the threshold","0");
      addParm("Cell Axis Attr 2","Cell Axis Attr 2","FibrilOrientations",
              QStringList() << "Curvature" << "CustomDirections" << "DeformationGradient" << "FibrilOrientations" << "PDGs" << "Polarity" << "Shape2D");
      addParm("Direction 2","Direction","Max/X",QStringList() << "Max/X" << "Min/Y");
      addParm("Isotropy Threshold 2","Ignore cells when their cell axis are close to isotropic, i.e. when their anisotropy is below the threshold","0");
      
      addParm("Use Measure Prefix","Use Measure Prefix Measure Label Tensor","Yes", booleanChoice());
      addParm("Display Axis","Display Axis","Yes", booleanChoice());
      addParm("Color 1","Color 1","aqua", QColor::colorNames());
      addParm("Color 2","Color 2","red", QColor::colorNames());
      addParm("Line Width","Line width","2.0");
      addParm("Line Scale","Line Scale","1.0");
      addParm("Line Offset","Draw the vector ends a bit tilted up for proper display on surfaces.","0.1");
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parm("Use Measure Prefix")),
       parm("Cell Axis Attr 1"), parm("Direction 1"), parm("Isotropy Threshold 1").toDouble(),
       parm("Cell Axis Attr 2"), parm("Direction 2"), parm("Isotropy Threshold 2").toDouble(), stringToBool(parm("Display Axis")),
       QColor(parm("Color 1")),QColor(parm("Color 2")),parm("Line Width").toDouble(),parm("Line Scale").toDouble(),parm("Line Offset").toDouble());
    }
    
    // Standard call interface for all processes
    bool run(Mesh* m, bool usePrefix, QString attr1, QString dir1, double isoT1, QString attr2, QString dir2, double isoT2, 
      bool displayAxis,const QColor& color1,const QColor& color2,double lineWidth, double lineScale, double lineOffset);

  };


  class mgxBase_EXPORT CellAxisAngles3D : public Process
  {
    QString filename;

  public:
    CellAxisAngles3D(const Process& process) : Process(process)
    {
      setName("Mesh/Cell Axis 3D/Compute Angles");
      setDesc("Calculate angles between different cell axis");
      setIcon(QIcon(":/images/MeasureAngles.png"));

      addParm("Cell Axis Attr 1","Cell Axis Attr 1","Polarity3D",
              QStringList() << "CustomDirections" << "DeformationGradient3D" << "Polarity3D" << "Shape3D");
      addParm("Direction 1","Direction","Max",QStringList() << "Max" << "Mid" << "Min");
      addParm("Isotropy Threshold 1","Ignore cells when their cell axis are close to isotropic, i.e. when their anisotropy is below the threshold","1.");
      addParm("Cell Axis Attr 2","Cell Axis Attr 2","FibrilOrientations",
              QStringList() << "CustomDirections" << "DeformationGradient3D" << "Polarity3D" << "Shape3D");
      addParm("Direction 2","Direction","Max",QStringList() << "Max" << "Mid" << "Min");
      addParm("Isotropy Threshold 2","Ignore cells when their cell axis are close to isotropic, i.e. when their anisotropy is below the threshold","1.");
      
      addParm("Use Measure Prefix","Use Measure Prefix Measure Label Tensor","Yes", booleanChoice());
      addParm("Display Axis","Display Axis","Yes", booleanChoice());
  }

    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), stringToBool(parm("Use Measure Prefix")),
       parm("Cell Axis Attr 1"), parm("Direction 1"), parm("Isotropy Threshold 1").toDouble(),
       parm("Cell Axis Attr 2"), parm("Direction 2"), parm("Isotropy Threshold 2").toDouble(), stringToBool(parm("Display Axis")));
    }
    
    // Standard call interface for all processes
    bool run(Mesh* m, bool usePrefix, QString attr1, QString dir1, double isoT1, QString attr2, QString dir2, double isoT2, bool displayAxis);

  };



  class mgxBase_EXPORT CellTypeRecognitionAllMeasures : public Process
  {
    public:
      CellTypeRecognitionAllMeasures(const Process &process): Process(process)
      {
		setName("Mesh/Cell Type Classification/Measure Map 2D Computation");
		setDesc("Calculates ALL 2D measure maps. (Might take a bit depending on the mesh).");
		setIcon(QIcon(":/images/CellTypeRecognitionSpecification.jpeg"));
	  }

      bool run();

  };

  ///@}
}
#endif
