//
// 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_PROCESS_SIGNAL_HPP
#define MESH_PROCESS_SIGNAL_HPP

#include <Process.hpp>

#include <Curvature.hpp>

class Ui_LoadHeatMap;

namespace mgx
{
  typedef std::map<int, HVec3U> IntHVec3UMap;

  Point2f calcSignalBounds(const vvGraph &S);
  
  ///\addtogroup MeshProcess
  ///@{
  /**
   * \class ViewProcess MeshProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * This process is used to change how the mesh is seen. Mostly useful to
   * write scripts taking screenshots.
   */
  class mgxBase_EXPORT ViewMeshProcess : public Process
  {
  public:
    ViewMeshProcess(const Process& process) : Process(process) 
    {
	    setName("Mesh/System/View");
	    setDesc("Modify how the current mesh is viewed. Useful for scripts.");
	    setIcon(QIcon(":/images/Palette.png"));

	    addParm("Show Surface","Draw mesh as a continuous surface","", QStringList() << "" << "Yes" << "No");
	    addParm("Use Parents","Use parent labels","", QStringList() << "" << "Yes" << "No");
	    addParm("Surface Type","Vertex: show projected signal, Tris: color by triangle, Cells: Color by Label)", "", 
              QStringList() << "" << "Vertex" << "Tris" << "Cells");
	    addParm("Vertex Signal","Signal: show projected signal, Stack Texture: Use 3D stack as texture, Image Texture: Use 2D texture (height map)", "",
              QStringList() << "" << "Signal" << "Stack Texture" << "Image Texture");
	    addParm("Cells Signal","Label: show labels, Label Heat: show heat map, Wall Heat: show wall heat map, Cell Color: show cell color", "",
              QStringList() << "" << "Label" << "Label Heat" << "Wall Heat" << "Cell Color");
	    addParm("Blend","Semi-transparent mesh, for example to superimpose to meshes or view the stack through the mesh.", "", 
              QStringList() << "" << "Yes" << "No");
	    addParm("Cull","Color the triangles (with signal or labels) only on the top of the mesh.", "", 
              QStringList() << "" << "Yes" << "No");
	    addParm("Show Mesh","Draw triangle edges and nodes","", QStringList() << "" << "Yes" << "No");	// 5
	    addParm("Mesh View","All: draw all triangles, Border: draw outside edge of the mesh only, Cells: draw cell outlines only, "
              "Selected: draw selected nodes only", "", QStringList() << "" << "All" << "Border" << "Cells" << "Selected");
	    addParm("Show Lines","Show connecting lines between nodes in the mesh.","", QStringList() << "" << "Yes" << "No");
	    addParm("Show Points","Show mesh nodes.","", QStringList() << "" << "Yes" << "No");
	    addParm("Show Map","Mapping of text on the labels (e.g. label number)","", QStringList() << "" << "Yes" << "No");
	    addParm("Scale","Change scaling of mesh/stacks, independently in 3 directions (x,y,z). NB: ","", QStringList() << "" << "Yes" << "No");
	    addParm("Transform","a stack saved with 'Scale' turned on will have a modified voxel size, while saved meshes are unaffected. ", "", 
              QStringList() << "" << "Yes" << "No");
	    addParm("BBox","Apply rotation and translation to the mesh/stack.","", QStringList() << "" << "Yes" << "No");
	    addParm("Brightness","Display the bounding box (i.e. total size) of a stack.","-1");
	    addParm("Opacity","Brightness of signal, labels or heat","-1");				
	  }
    bool run();
  };
  
  /**
   * \class ProjectSignal ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Project stack signal onto the current mesh.
   */
  class mgxBase_EXPORT ProjectSignal : public Process
  {
  public:
    ProjectSignal(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Project Signal");
	  setDesc("Project signal onto mesh, perpendicular to its curved surface.");
	  setIcon(QIcon(":/images/ProjectColor.png"));

	  addParm("Use absolute","Use absolute values of signal, instead of normalizing it, useful for signal quantification.","No",booleanChoice());	// 0
	  addParm("Min Dist (µm)","Distance (triangle-voxel) above which the signal is projected.","2.0");	// 1
	  addParm("Max Dist (µm)","Maximal distance (triangle-voxel) used for signal projection.","6.0");	// 2
	  addParm("Min Signal","Lower bound of signal value if 'Use absolute' is chosen","0.0");	// 3
	  addParm("Max Signal","Upper bound of projected signal value.","60000.0");	// 4	
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_LABEL).mesh(MESH_NON_EMPTY))
        return false;
      return run(currentStack()->currentStore(), currentMesh(), stringToBool(parm("Use absolute")), 
        parm("Min Dist (µm)").toFloat(), parm("Max Dist (µm)").toFloat(), parm("Min Signal").toFloat(), parm("Max Signal").toFloat());
    }
  
    bool run(const Store* store, Mesh* mesh, bool useAbsSignal, float minDist, 
      float maxDist, float absSignalMin, float absSignalMax);
    void projSignal(const Stack* stack, const HVecUS& data, vertex v, float mindist, float maxdist);

  };
  

  /**
   * \class ProjectSignal ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Project stack signal onto the current mesh.
   */
  class mgxBase_EXPORT ProjectLabel : public Process
  {
  public:
    ProjectLabel(const Process& process) : Process(process) 
    {
    setName("Mesh/Segmentation/Project 3D Labels on Mesh");
    setDesc("Project labels onto mesh, perpendicular to its curved surface.");
    setIcon(QIcon(":/images/ProjectColor.png"));

    addParm("Use absolute","Use absolute values of signal, instead of normalizing it, useful for signal quantification.","No",booleanChoice()); // 0
    addParm("Min Dist (µm)","Distance (triangle-voxel) above which the signal is projected.","2.0");  // 1
    addParm("Max Dist (µm)","Maximal distance (triangle-voxel) used for signal projection.","6.0"); // 2
    addParm("Labeling Method","Labeling Method","Majority",QStringList() << "Majority" << "Absolute Majority"); // 2
    addParm("Ignore Zero","Ignore Zero","No",booleanChoice()); // 2
  }
  
    bool run()
    {
      return run(currentStack()->currentStore(), currentMesh(), 
        parm("Min Dist (µm)").toFloat(), parm("Max Dist (µm)").toFloat(), parm("Labeling Method"), stringToBool(parm("Ignore Zero")));
    }
  
    bool run(const Store* store, Mesh* mesh, float minDist, 
      float maxDist, QString method, bool ignoreZero);
    void projLabel(const Stack* stack, const HVecUS& data, vertex v, float mindist, float maxdist, int zeroLabel, QString method, bool ignoreZero);

  };
  
  /**
   * \class ProjectLabelsOnMesh ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Project stack signal onto the current mesh.
   */
  class mgxBase_EXPORT ProjectLabelsOnMesh : public Process
  {
  public:
    ProjectLabelsOnMesh(const Process& process) : Process(process)
    {
    setName("Mesh/Segmentation/Project Nearest 3D Label on Mesh");
    setDesc("Find the stack labeled for the positions of each vertex. Might not be needed anymore!");
    setIcon(QIcon(":/images/MakeHeatMap.png"));

    addParm("Split Border","Split Border","Yes",booleanChoice());
    addParm("Search Radius","Search Radius","0");
    }

    bool run()
    {
      Stack* s1 = currentStack();
      Store* store1 = s1->main();
      Mesh* m = currentMesh();
      return run(s1, store1, m, stringToBool(parm("Split Border")), parm("Search Radius").toInt());
    }

    bool run(Stack* s1, Store* store1, Mesh* m, bool splitBorder, int searchNeighborhood);

  };



  /**
   * \class SmoothMeshSignal ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Smooth the mesh signal using local averaging.
   */
  class mgxBase_EXPORT SmoothMeshSignal : public Process
  {
  public:
    SmoothMeshSignal(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Smooth Mesh Signal");
	  setDesc("Averages the signal of each node, based on its immediate neighbors.");
	  setIcon(QIcon(":/images/SmoothColor.png"));
	  
	  addParm("Passes","Number of smoothing iterations.","3");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Passes").toUInt());
    }
  
    bool run(Mesh* mesh, uint passes);

  };

  /**
   * \class BrightenMesh ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Change the brightness of the signal.
   */
  class mgxBase_EXPORT MeshBrightness : public Process {
  public:
    MeshBrightness(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Mesh Brighness");
	  setDesc("Changes the brightness of the signal on a mesh.");
	  setIcon(QIcon(":/images/Brightness.png"));

	  addParm("Amount","Amount to multiply the signal, >1 is brighter","2.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Amount").toFloat());
    }
  
    bool run(Mesh* mesh, float amount);

  }; 

  /**
   * \class ClearMeshSignal ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Clear the mesh signal to a defined value.
   */
  class mgxBase_EXPORT ClearMeshSignal : public Process
  {
  public:
    ClearMeshSignal(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Clear Mesh Signal");
	  setDesc("Erase the signal on the mesh");
	  setIcon(QIcon(":/images/ClearSignal.png"));

	  addParm("Value","Assign this signal value to the mesh.","50000");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Value").toUInt());
    }
  
    bool run(Mesh* mesh, uint value);

  };
  
  /**
   * \class ProjectCurvature ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Set the mesh signal to be the curvature of the mesh. The process can also
   * output a CSV file with the full curvature tensor for each vertex.
   */
  class mgxBase_EXPORT ProjectCurvature : public Process
  {
  public:
    ProjectCurvature(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Project Mesh Curvature");
	  setDesc("Compute curvature at each node of the mesh, for a given neighborhood size. Curvature values are stored as signal.");
	  setIcon(QIcon(":/images/Curvature.png"));
	  
	  addParm("Output","Name of output file, if desired.","");
	  addParm("Type","Minimal = minCurv, Maximal = maxCurv, Gaussian = maxCurv * minCurv, SumSquare = maxCurv^2 + minCurv^2, Average = (maxCurv + minCurv)/2, SignedAverageAbs = sign(max or min) x (abs(maxCurv) + abs(minCurv))/2","Gaussian", QStringList() << "Minimal" << "Maximal" << "Gaussian" << "SumSquare" << "Average" << "SignedAverageAbs");
	  addParm("Neighborhood (µm)","Neighborhood (µm)","3.0");
	  addParm("AutoScale","Clip max and min signal range according to curvature distribution","Yes",booleanChoice());
	  addParm("Min Curv","Minimal curvature value displayed","-50.0");
	  addParm("Max Curv","Maximal curvature value displayed","50.0");
	  addParm("Autoscale percentile","Auto-scale signal range based on curvature percentile","85");
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      bool ok;
      float neighborhood = parm("Neighborhood (µm)").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Neighborhood' must be a number");
      float mincurv = parm("Min Curv").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Min Curv' must be a number");
      float maxcurv = parm("Max Curv").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Max Curv' must be a number");
      float percentile = parm("Autoscale percentile").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Error, parameter 'Percentile' must be a number");
      return run(currentMesh(), parm("Output"), parm("Type"), neighborhood, stringToBool(parm("AutoScale")), 
        mincurv, maxcurv, percentile);
    }
  
    bool run(Mesh* mesh, QString output, QString type, float neighborhood, 
      bool auto_scale, float mincurv, float maxcurv, int percentile);
  
  };


/**
   * \class MeshSignalGrad ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Smooth the mesh signal using a gaussian kernel.
   */
  class mgxBase_EXPORT MeshSignalGrad : public Process
  {
  public:
    MeshSignalGrad(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Signal Gradient");
	  setDesc("Gradient of mesh signal");
	  setIcon(QIcon(":/images/Blur.png"));
	  
	  addParm("Radius (µm)","Size of neighborhood used for Gaussian blur. The blur function standard deviation is given by sigma = radius/2. ","2.0");
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh());
    }
  
    bool run(Mesh* mesh);

  };



  /**
   * \class MeshGaussianBlur ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Smooth the mesh signal using a gaussian kernel.
   */
  class mgxBase_EXPORT MeshGaussianBlur : public Process
  {
  public:
    MeshGaussianBlur(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Gaussian Blur");
	  setDesc("Apply Gaussian Blur to mesh signal");
	  setIcon(QIcon(":/images/Blur.png"));

	  addParm("Radius (µm)","Size of neighborhood used for Gaussian blur. The blur function standard deviation is given by sigma = radius/2. ","2.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \class MeshDiffGaussians ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Calculate a difference of Gaussians on mesh.
   */
  class mgxBase_EXPORT MeshDiffGaussians : public Process
  {
  public:
    MeshDiffGaussians(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Difference of Gaussians");
	  setDesc("Calculate a difference of Gaussians for the mesh signal");
	  setIcon(QIcon(":/images/Blur.png"));
	
	  addParm("Radius1 (µm)","Size of first neighborhood","1.0");
	  addParm("Radius2 (µm)","Size of second neighborhood","5.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius1 (µm)").toFloat(), parm("Radius2 (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius1, float radius2);

  };
  
  /**
   * \class MeshLocalMinima ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Find the local minima in the current mesh.
   */
  class mgxBase_EXPORT MeshLocalMinima : public Process
  {
  public:
    MeshLocalMinima(const Process& process) : Process(process) 
    {
	  setName("Mesh/Segmentation/Auto Seeding");
	  setDesc("Put a seed at local minima of mesh signal.");
	  setIcon(QIcon(":/images/LocalMinima.png"));
	  
	  addParm("Radius (µm)","Size of neighborhood used for search of local minima. Typically, the radius of smallest cells in the sample.","3.0");	// 0	
    addParm("Consider existing Labels","Consider existing Labels","Yes",booleanChoice());  // 0  
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \claass MeshNormalize ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Local normalization of the mesh signal.
   */
  class mgxBase_EXPORT MeshNormalize : public Process
  {
  public:
    MeshNormalize(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Normalize Signal");
	  setDesc("Normalize mesh signal locally.");
	  setIcon(QIcon(":/images/Normalize.png"));
	  
	  addParm("Radius (µm)","Size of neighborhood used for normalization.","5.0");		
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
      
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \class MeshDilation ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological dilation on the mesh, using a spherical kernel. This process
   * replace the signal intensity on each vertex with the maximum within a
   * given distance.
   */
  class mgxBase_EXPORT MeshDilation : public Process
  {
  public:
    MeshDilation(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Dilate Signal");
	  setDesc("Morphological dilation of signal on mesh.");
	  setIcon(QIcon(":/images/Dilate.png"));

	  addParm("Radius (µm)","Size of neighborhood used for dilation.","1.0");	// 0	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \class MeshDilation ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological dilation on the mesh, using a spherical kernel. This process
   * replace the signal intensity on each vertex with the maximum within a
   * given distance.
   */
  class mgxBase_EXPORT MeshLabelDilation : public Process
  {
  public:
    MeshLabelDilation(const Process& process) : Process(process) 
    {
    setName("Mesh/Segmentation/Dilate Labels");
    setDesc("Morphological dilation of labels on mesh.");
    setIcon(QIcon(":/images/Dilate.png"));

    addParm("Radius (µm)","Size of neighborhood used for dilation.","1.0"); // 0  
  }
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  


  /**
   * \class MeshErosion ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological erosion on the mesh, using a spherical kernel. This process
   * replace the signal intensity on each vertex with the minimum within a
   * given distance.
   */
  class mgxBase_EXPORT MeshErosion : public Process
  {
  public:
    MeshErosion(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Erode Signal");
	  setDesc("Apply morphological erosion to mesh signal (opposite to Dilate Signal).");
	  setIcon(QIcon(":/images/Erode.png"));

	  addParm("Radius (µm)","Size of neighborhood used for erosion.","1.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \class MeshLabelErosion ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological erosion on the mesh, using a spherical kernel. This process
   * shrinks labelled regions by a
   * given distance.
   */
  class mgxBase_EXPORT MeshLabelErosion : public Process
  {
  public:
    MeshLabelErosion(const Process& process) : Process(process) 
    {
    setName("Mesh/Segmentation/Erode Labels");
    setDesc("Apply morphological erosion to mesh signal (opposite to Dilate Labels).");
    setIcon(QIcon(":/images/Erode.png"));

    addParm("Radius (µm)","Size of neighborhood used for erosion.","1.0");  
  }
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };


  /**
   * \class MeshClosing ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological closing on the mesh. This process is equivalent to call the
   * MeshDilation followed by MeshErosion with the same size.
   */
  class mgxBase_EXPORT MeshClosing : public Process
  {
  public:
    MeshClosing(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Close Signal");
	  setDesc("Apply morphological dilation followed by erosion to mesh signal.");
	  setIcon(QIcon(":/images/Closing.png"));
	  
	  addParm("Radius (µm)","Size of neighborhood used for dilation/erosion.","1.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  /**
   * \class MeshOpening ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Morphological opening on the mesh. This process is equivalent to call the
   * MeshErosion followed by MeshDilation with the same size.
   */
  class mgxBase_EXPORT MeshOpening : public Process
  {
  public:
    MeshOpening(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Open Signal");
	  setDesc("Apply morphological erosion followed by dilation to mesh signal.");
	  setIcon(QIcon(":/images/Opening.png"));

	  addParm("Radius (µm)","Size of neighborhood used for erosion/dilation.","1.0");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      return run(currentMesh(), parm("Radius (µm)").toFloat());
    }
  
    bool run(Mesh* mesh, float radius);

  };
  
  class mgxBase_EXPORT RescaleSignal : public Process
  {
  public:
    RescaleSignal(const Process& process) : Process(process)  
    {
	  setName("Mesh/Signal/Rescale Signal");
	  setDesc("Change the colorbar of the signal. \n"
	  "If percentile is set to 0, it uses the minimum and maximum arguments. \n"
	  "Only the visualization is affected, the signal projection remains unchanged");
	  setIcon(QIcon(":/images/Normalize.png"));

	  addParm("Zero as reference","If true, 0 will be used as a reference. \n"
	  "If the signal is all positive (resp. negative), 0 will be added as a minimum (resp. maximum). \n"
	  "If the signal is both positive and negative, 0 will be place at the center of the range","No",booleanChoice());
	  addParm("Percentile","Keep only this percentage of the signal to compute the range.","99");
	  addParm("Minimum","If the percentile specified is 0, uses this as the minimum value for the range","0");
	  addParm("Maximum","If the percentile specified is 0, uses this as the maximum value for the range","1");	
	}
  
    bool run()
    {
      if(!checkState().mesh(MESH_NON_EMPTY))
        return false;
      bool ok;
      float percentile = parm("Percentile").toFloat(&ok);
      if(!ok)
        return setErrorMessage("Error, argument 'Percentile' must be a number");
      float minimum = parm("Minimum").toFloat(&ok);
      if(!ok)
        return setErrorMessage("Error, argument 'Minimum' must be a number");
      float maximum = parm("Maximum").toFloat(&ok);
      if(!ok)
        return setErrorMessage("Error, argument 'Maximum' must be a number");
      return run(currentMesh(), stringToBool(parm("Zero as reference")), percentile, minimum, maximum);
    }
  
    bool run(Mesh* mesh, bool use_zero, float percentile, float minimum, float maximum);

  };
  
  /**
   * \class SetSignalProcess ProcessSignal.hpp <MeshProcessSignal.hpp>
   *
   * Set the signal to a given value for all currently active vertices.
   */
  class mgxBase_EXPORT SetSignalProcess : public Process
  {
  public:
    SetSignalProcess(const Process& process) : Process(process) 
    {
	  setName("Mesh/Signal/Set Signal");
	  setDesc("Set the signal for the whole mesh, or for the currently selected part of it.");
	  setIcon(QIcon(":/images/Sharpen.png"));

	  addParm("Value","New value for the signal","1");
	  addParm("Rescale","If true, the signal bounds will be rescaled","Yes",booleanChoice());
	  addParm("Percentile","If rescaling, which percentile to use?","100");
	  addParm("Use zero","If rescaling, should we use zero as reference?","No",booleanChoice());	
	}
  
    bool run()
    {
      if(not checkState().mesh(MESH_VISIBLE))
        return false;
      bool ok;
      float value = parm("Value").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Parameter 'Value' must be a number");
      float percentile = parm("Percentile").toFloat(&ok);
      if(not ok)
        return setErrorMessage("Parameter 'Percentile' must be a number");
      return run(currentMesh(), value, stringToBool(parm("Rescale")), percentile, 
        stringToBool(parm("Use zero")));
    }
  
    bool run(Mesh* m, float value, bool rescale, float percentile, bool use_zero);

  };
  
  ///@}
  
  class mgxBase_EXPORT ProjectCurvatureOp
  {
  public:
    ProjectCurvatureOp(const vvGraph& S, const VtxVec& vs, std::string type, float radius, 
      FloatVec& values, std::vector<Curvature>& curvs);
    void operator()(int i);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    std::string type;
    float radius;
    FloatVec& values;
    std::vector<Curvature>& curvs;
  };

 class mgxBase_EXPORT MeshSignalGradOp
  {
  public:
    MeshSignalGradOp(const vvGraph& S, const VtxVec& vs, VtxFloatAttr &result);
    void operator()(int i);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxFloatAttr& result;
  };

  
  class mgxBase_EXPORT MeshGaussianBlurOp
  {
  public:
    MeshGaussianBlurOp(const vvGraph& S, const VtxVec& vs, float radius, VtxFloatAttr &result);
    void operator()(int i);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxFloatAttr& result;
  };
  
  class mgxBase_EXPORT MeshLocalMinimaOp
  {
  public:
    MeshLocalMinimaOp(const vvGraph& S, const VtxVec& vs, float radius, int startLabel, bool overwrite);
    void operator()(int i);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    int label;
    bool overwrite;
  };
  
  class mgxBase_EXPORT MeshNormalizeOp
  {
  public:
    MeshNormalizeOp(const vvGraph& S, const VtxVec& vs, float radius, VtxFloatAttr &result);
  
    void operator()(int i);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxFloatAttr& result;
  };
  
  class mgxBase_EXPORT MeshDilationOp
  {
  public:
    MeshDilationOp(const vvGraph& S, const VtxVec& vs, float radius, VtxFloatAttr &result);
    void operator()(int vi);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxFloatAttr& result;
  };
  
  class mgxBase_EXPORT MeshLabelDilationOp
  {
  public:
    MeshLabelDilationOp(const vvGraph& S, const VtxVec& vs, float radius, VtxIntAttr &result);
    void operator()(int vi);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxIntAttr& result;
  };

  class mgxBase_EXPORT MeshErosionOp
  {
  public:
    MeshErosionOp(const vvGraph& S, const VtxVec& vs, float radius, VtxFloatAttr &result);
    void operator()(int vi);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxFloatAttr& result;
  };

  class mgxBase_EXPORT MeshLabelErosionOp
  {
  public:
    MeshLabelErosionOp(const vvGraph& S, const VtxVec& vs, float radius, VtxIntAttr &result);
    void operator()(int vi);
  
  private:
    const vvGraph& S;
    const VtxVec& vs;
    float radius;
    VtxIntAttr& result;
  };


  class mgxBase_EXPORT MeshCircularHistogram : public Process
  {
  public:
    MeshCircularHistogram(const Process& process) : Process(process)
    {
    setName("Mesh/Signal/Export Histogram Circular");
    setDesc("Computes a circular histogram in counter-clockwise direction of the active main store signal values around the given cartesian axis.");
      setIcon(QIcon(":/images/StackHistoCirc.png"));

    addParm("Central Axis","Central Axis","Z", QStringList() << "X" << "Y" << "Z");
    addParm("Bin Number","Number of bins for the whole circle","360");
    addParm("Value Threshold (%)","Ignore voxels with values lower than this threshold (% from max value). Set to 0 to include all","1.0");
    addParm("Distance Min (um)","Only consider voxels with a minimum distance to the Bezier larger than this. Set to -1 to include all.","-1");
    addParm("Distance Max (um)","Only consider voxels with a maximum distance to the Bezier smaller than this. Set to -1 to include all.","-1");
    addParm("Align at Max","Align at Max","No", QStringList() << "Align at Max of Signal Sum" << "Align at Signal Max" << "No");
    addParm("Weight by Area","Weight signal values by the triangle area","Yes",booleanChoice());
    addParm("Filename","Filename","");
    addParm("Set Mesh Labels to Bin","Set Mesh Labels to Bin","No",booleanChoice());
  }

    bool initialize(QWidget* parent);

    bool run()
    {
      Stack* s1 = currentStack();
      Mesh* m1 = currentMesh();
      return run(s1, m1, parm("Central Axis"), parm("Bin Number").toInt(), parm("Value Threshold (%)").toDouble(), parm("Distance Min (um)").toDouble(),
        parm("Distance Max (um)").toDouble(), parm("Align at Max"), stringToBool(parm("Weight by Volume")), parm("Filename"), stringToBool(parm("Set Mesh Labels to Bin")));
    }
    bool run(Stack* s1, Mesh* m1, QString centralAxis, int binNumber, double thresholdValue, double minDis, double maxDis, QString align, bool weightVol, QString filename, bool writeLabels);

  };


} 

#endif
