//
// 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 STACK_PROCESS_HPP
#define STACK_PROCESS_HPP

/**
 * \file StackProcess.hpp
 * Contains various stack processes
 */

#include <Process.hpp>

namespace mgx 
{
  ///\addtogroup StackProcess
  ///@{
  /**
   * \class Annihilate StackProcess.hpp <StackProcess.hpp>
   *
   * Delete all but a layer of the stack just "below" the mesh.
   */
  class mgxBase_EXPORT Annihilate : public Process 
  {
  public:
    Annihilate(const Process& process) : Process(process) 
    {
      setName("Stack/Mesh Interaction/Annihilate");
      setDesc("Keep or fill a layer near the mesh");
      setIcon(QIcon(":/images/Annihilate.png"));
      
      addParm("Fill","Fill the layer with specified value, or keep the original data.","No", booleanChoice());		
      addParm("Fill Val","Value to fill the volume with.","30000");		
      addParm("Min Dist(µm)","Minimal distance from layer to mesh.","1.0");		
      addParm("Max Dist(µm)","Maximal distance from layre to mesh","5.0");								
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY).mesh(MESH_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
  
      bool fill = stringToBool(parm("Fill"));
      bool res
        = run(input, output, currentMesh(), parm("Min Dist(µm)").toFloat(), parm("Max Dist(µm)").toFloat(), 
             fill, parm("Fill Val").toUInt());
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    /**
     * Annihilate the stack
     * \param input Input stack
     * \param output Stack storing the result
     * \param mesh Mesh used
     * \param minDist Distance from the mesh, taken "below" (i.e. against the normal)
     * \param maxDist Distance from the mesh, taken "below" (i.e. against the normal)
     */
    bool run(const Store* input, Store* output, const Mesh* mesh, float minDist, 
           float maxDist, bool fill, uint fillval);
  };
  
  /**
   * \class AutoScaleStack StackProcess.hpp <StackProcess.hpp>
   *
   * Scale the stack intensity to fill exactly the whole range.
   */
  class mgxBase_EXPORT AutoScaleStack : public Process 
  {
  public:
    AutoScaleStack(const Process& process) : Process(process) {}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      bool res = run(input, output);
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    /**
     * Scale the stack values, so as to use the whole range.
     */
    bool run(const Store* store, Store* output);
  };
  
  /**
   * \class ApplyTransferFunction StackProcess.hpp <StackProcess.hpp>
   *
   * Transform the stack to reflect the transfer function in use.
   */
  class mgxBase_EXPORT ApplyTransferFunction : public Process 
  {
  public:
    ApplyTransferFunction(const Process& process) : Process(process) 
    {
      setName("Stack/Filters/Apply Transfer Function");
      setDesc("Apply the transfer function to the stack (modifies voxel values).");
      setIcon(QIcon(":/images/Palette.png"));
      
      addParm("Red","Red","0");
      addParm("Green","Green","0");		
      addParm("Blue","Blue","0");
      addParm("Alpha","Alpha","1");				
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      bool res
        = run(input, output, parm("Red").toFloat(), parm("Green").toFloat(), parm("Blue").toFloat(), 
              parm("Alpha").toFloat());
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    /**
     * Apply the transfer function. The final value is computed as a linear combination 
     * of the red, green, blue and alpha channels.
     * \param store Input store
     * \param output Output store
     * \param red Coefficient for the red channel
     * \param green Coefficient for the green channel
     * \param blue Coefficient for the blue channel
     * \param alpha Coefficient for the alpha channel
     * \note If the sum of the coefficient is not 1, values can globally increase/decrease
     */
    bool run(Store* store, Store* output, float red, float green, float blue, float alpha);
  };
  
  /**
   * \class BlobDetect StackProcess.hpp <StackProcess.hpp>
   *
   * Find and label blobs in an image.
   */
  class mgxBase_EXPORT BlobDetect : public Process 
  {
  public:
    BlobDetect(const Process& process) : Process(process) 
    {		
      setName("Stack/Segmentation/Blob Detect");
      setDesc("Find and label blobs in an image");
      setIcon(QIcon(":/images/BlobDetect.png"));
      
      addParm("Use watershed","Use watershed","No", booleanChoice());
      addParm("Start Label","Start Label","1");		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY | STORE_NON_LABEL))
        return false;
      bool wat = stringToBool(parm("Use watershed"));
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      bool res = run(input, output, wat, parm("Start Label").toUInt());
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
    bool run(const Store* input, Store* output, bool watershed, uint startlabel);    
  };
  
  /**
   * \class ClearWorkStack StackProcess.hpp <StackProcess.hpp>
   *
   * Erase the content of the work stack
   */
  class mgxBase_EXPORT ClearWorkStack : public Process 
  {
  public:
    ClearWorkStack(const Process& process) : Process(process) 
    {
      setName("Stack/System/Clear Work Stack");
      setDesc("Clear the work stack");
      setIcon(QIcon(":/images/ClearStack.png"));
      
      addParm("Fill value","Fill value","0");				
		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_WORK))
        return false;
      return run(currentStack(), parm("Fill value").toUInt());
    }
  
    bool run(Stack* stack, uint fillValue);
  };
  
  /**
   * \class ClearMainStack StackProcess.hpp <StackProcess.hpp>
   *
   * Erase the content of the main stack
   */
  class mgxBase_EXPORT ClearMainStack : public Process 
  {
  public:
    ClearMainStack(const Process& process) : Process(process) 
    {
      setName("Stack/System/Clear Main Stack");
      setDesc("Clear the main stack");
      setIcon(QIcon(":/images/ClearStack.png"));
      
      addParm("Fill value","Fill value","0");				
		
		}
  
    bool run()
    {
      if(!checkState().store(STORE_MAIN))
        return false;
      return run(currentStack(), parm("Fill value").toUInt());
    }
  
    bool run(Stack* stack, uint fillValue);
  };
  
  /**
   * \class ClipStack StackProcess.hpp <StackProcess.hpp>
   *
   * Apply the active clipping planes to the current stack
   */
  class mgxBase_EXPORT ClipStack : public Process 
  {
  public:
    ClipStack(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Clip Stack");
      setDesc("Trim stack to clipping planes");
      setIcon(QIcon(":/images/ClipStack.png"));
      addParm("Trim canvas?","Use AutoTrim to reduce canvas size after clipping the stack.","No", booleanChoice());				
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      bool res = run(input, output, stringToBool(parm("Trim canvas?")));
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
      
    bool run(const Store* input, Store* output, bool trimStack);  
  };
  
  /**
   * \class CopyMainToWork StackProcess.hpp <StackProcess.hpp>
   *
   * Copy the content of the main stack into the work stack
   */
  class mgxBase_EXPORT CopyMainToWork : public Process 
  {
  public:
    CopyMainToWork(const Process& process) : Process(process) 
    {
      setName("Stack/MultiStack/Copy Main to Work Stack");
      setDesc("Copy Main to Work Stack");				
      setIcon(QIcon(":/images/CopyMainToWork.png"));
	}
  
    bool run()
    {
      if(!checkState().store(STORE_MAIN | STORE_NON_EMPTY))
        return false;
      Stack* stack = currentStack();
      bool res = run(stack);
      if(res) {
        stack->main()->hide();
        stack->work()->show();
      }
      return res;
    }
      
    bool run(Stack* stack);
  };
  
  /**
   * \class CopyWorkToMain StackProcess.hpp <StackProcess.hpp>
   *
   * Copy the content of the work stack into the main stack
   */
  class mgxBase_EXPORT CopyWorkToMain : public Process 
  {
  public:
    CopyWorkToMain(const Process& process) : Process(process) 
    {
      setName("Stack/MultiStack/Copy Work to Main Stack");
      setDesc("Copy Work to Main Stack");	
      setIcon(QIcon(":/images/CopyWorkToMain.png"));	
	}
  
    bool run()
    {
      if(!checkState().store(STORE_WORK | STORE_NON_EMPTY))
        return false;
      Stack* stack = currentStack();
      bool res = run(stack);
      if(res) {
        stack->work()->hide();
        stack->main()->show();
      }
      return res;
    }
  
    bool run(Stack* stack);
  };
  
  /**
   * \class CopySwapStacks <StackProcess.hpp>
   *
   * Copy or swap stacks between stack 1 and 2.
   */
  class mgxBase_EXPORT CopySwapStacks : public Process 
  {
  public:
    CopySwapStacks(const Process& process) : Process(process) 
    {
      setName("Stack/MultiStack/Swap or Copy Stack 1 and 2");
      setDesc("Copy or Swap Stack 1 and 2");
      setIcon(QIcon(":/images/CopySwapStacks.png"));
      
      addParm("Store","Store","Main", QStringList() << "Main" << "Work");		
      addParm("Action","Action","1 -> 2", QStringList() << "1 -> 2" << "1 <- 2" << "1 <-> 2");								
	}
  
    bool run()
    {
      // Stack *stack = currentStack();
      bool res = run(parm("Store"), parm("Action"));
      // if(res)
      //{
      //  stack->main()->hide();
      //  stack->work()->show();
      //}
      return res;
    }
  
    bool run(const QString& storeStr, const QString& actionStr);
  };
  
  /**
   * \class StackMeshProcess StackProcess.hpp <StackProcess.hpp>
   *
   * Base class for a process that either fill or erase the inside part of a mesh in a stack.
   */
  class mgxBase_EXPORT StackMeshProcess : public Process 
   {
  public:
    StackMeshProcess(const Process& process) : Process(process) 
    {
	  setName("Stack/Mesh Interaction/Fill Stack from Mesh");
      setDesc("Fill volume contained by closed mesh");
      setIcon(QIcon(":/images/TrimStack.png"));
      
      addParm("Label", "If >= 0 only this label is used", "-1");		
      addParm("Fill Value","Fill Value", "32000");	
				
	}
  
    bool run(bool fill)
    {
      if(!checkState().store(STORE_NON_EMPTY).mesh(MESH_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      Mesh* mesh = currentMesh();
      uint fillValue = 0;
      if(fill)
        fillValue = parm("Fill Value").toUInt();
      bool res = run(input, output, mesh, parm("Label").toInt(), fill, fillValue);
      if(res) {
        input->hide();
        output->show();
      }
      return res;
    }
  
    bool run(const Store* input, Store* output, Mesh* mesh, 
                                           int label, bool fill, uint fillValue);
  };
  
  /**
   * \class FillStackToMesh StackProcess.hpp <StackProcess.hpp>
   *
   * Fill the volume contained by a closed mesh with a pre-defined intensity.
   */
  class mgxBase_EXPORT FillStackToMesh : public StackMeshProcess 
  {
  public:
    FillStackToMesh(const Process& process) : StackMeshProcess(process) 
    {
		
      setName("Stack/Mesh Interaction/Fill Stack from Mesh");
      setDesc("Fill volume contained by closed mesh");
      setIcon(QIcon(":/images/TrimStack.png"));
      
      addParm("Label","If >= 0 only this label is used","-1");
      addParm("Fill Value","Fill Value","32000");		
	}  
	using StackMeshProcess::run;
    bool run() {
	return run(true);}	

  };
  
  /**
   * \class TrimStackProcess StackProcess.hpp <StackProcess.hpp>
   *
   * Set to 0 any voxel not contained within the closed mesh.
   */
  class mgxBase_EXPORT TrimStackProcess : public StackMeshProcess 
  {
  public:
    TrimStackProcess(const Process& process) : StackMeshProcess(process) 
    {
      setName("Stack/Mesh Interaction/Trim Stack");
      setDesc("Trim parts of stack which are not contained within closed mesh.");
      setIcon(QIcon(":/images/TrimStack.png"));

      addParm("Label", "If >= 0 only this label is used", "-1");						
	}
    using StackMeshProcess::run;	
    bool run() {
	return run(false);}
	
  };
  
  /**
   * \class FillStack3D StackProcess.hpp <StackProcess.hpp>
   *
   * Fill the stack with labels from a labeled 3D mesh.
   */
  class mgxBase_EXPORT FillStack3D : public Process 
  {
  public:
    FillStack3D(const Process& process) : Process(process) 
    {
      setName("Stack/Mesh Interaction/Fill Stack from 3D Mesh");
      setDesc("Fill stack contained by labeled 3D mesh");
      setIcon(QIcon(":/images/FillStack3D.png"));
    }
    
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY).mesh(MESH_NON_EMPTY))
        return false;
      Store* input = currentStack()->currentStore();
      Store* output = currentStack()->work();
      Mesh* mesh = currentMesh();
      bool res = run(input, output, mesh);
      if(res) {
        input->hide();
        output->show();
        output->setLabels(true);
      }
      return res;
    }
  
    bool run(const Store* input, Store* output, Mesh* mesh);

  };
  
  /**
   * \class SwapStacks StackProcess.hpp <StackProcess.hpp>
   *
   * Swap the main and work stores of a stack
   */
  class mgxBase_EXPORT SwapStacks : public Process 
  {
  public:
    SwapStacks(const Process& process) : Process(process) 
    {
      setName("Stack/MultiStack/Swap Main and Work Stacks");
      setDesc("Swap the main and work data of the current stack.");	
      setIcon(QIcon(":/images/SwapStacks.png"));	
    }
  
    bool run()
    {
      if(!checkState().stack(STACK_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      if(!s) {
        setErrorMessage("You need to select a stack to launch this process.");
        return false;
      }
      return run(s);
    }
  
    bool run(Stack* stack);
  };
  
  /**
   * \class ReverseStack StackProcess.hpp <StackProcess.hpp>
   *
   * Reverse the direction of the selected axes.
   */
  class mgxBase_EXPORT ReverseStack : public Process 
  {
  public:
    ReverseStack(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Reverse Axes");
      setDesc("Reverse the direction of the selected axes. Press A-key to display the axis.");
      setIcon(QIcon(":/images/Resize.png"));
      
      addParm("X","X","No", QStringList() << "Yes" << "No");
      addParm("Y","Y","No", QStringList() << "Yes" << "No");
      addParm("Z","Z","Yes", QStringList() << "Yes" << "No");				
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      Store* input = s->currentStore();
      Store* output = s->work();
      bool reverse_x = stringToBool(parm("X"));
      bool reverse_y = stringToBool(parm("Y"));
      bool reverse_z = stringToBool(parm("Z"));
      if(run(output, input, reverse_x, reverse_y, reverse_z)) {
        input->hide();
        output->show();
        return true;
      }
      return false;
    }
  
    bool run(Store* output, const Store* input, bool x, bool y, bool z);
  };
  
  /**
   * \class ChangeVoxelSize StackProcess.hpp <StackProcess.hpp>
   *
   * Change the size of the stack's voxel, without changing the data itself.
   */
  class mgxBase_EXPORT ChangeVoxelSize : public Process 
  {
  public:
    ChangeVoxelSize(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Change Voxel Size");
      setDesc("Change the size of a voxel (i.e. doesn't change the data)");
      setIcon(QIcon(":/images/Resize.png"));
      
      addParm("X (µm)","X (µm)","1.0");	
      addParm("Y (µm)","Y (µm)","1.0");
      addParm("Z (µm)","Z (µm)","1.0");		
	}
  
    bool run()
    {
      if(!checkState().stack(STORE_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      Point3f nv(parm("X (µm)").toFloat(), parm("Y (µm)").toFloat(), parm("Z (µm)").toFloat());
      return run(s, nv);
    }
  
    bool run(Stack* stack, Point3f nv);
  };
  
  /**
   * \class ResizeCanvas StackProcess.hpp <StackProcess.hpp>
   *
   * Resize the stack to add or remove voxels.
   */
  class mgxBase_EXPORT ResizeCanvas : public Process 
  {
  public:
    ResizeCanvas(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Resize Canvas");
      setDesc("Resize the stack to add or remove voxels.Make sure BBox is checked on before running.");
      setIcon(QIcon(":/images/Resize.png"));
      
      addParm("Relative","If true, X, Y and Z are given in percentage, if false in voxels.","Yes", booleanChoice());		
      addParm("Center","New canvas centered as the old one, or else use the bottom left corner as reference.","Yes", booleanChoice());		
      addParm("X","Canvas size for X direction, in percentage or voxels.","0");		
      addParm("Y","Canvas size for Y direction, in percentage or voxels.","0");		
      addParm("Z","Canvas size for Z direction, in percentage or voxels.","0");				
	}
  
    bool run()
    {
      if(!checkState().stack(STORE_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      Point3i ds(parm("X").toInt(), parm("Y").toInt(), parm("Z").toInt());
      return run(s, stringToBool(parm("Relative")), stringToBool(parm("Center")), ds);
    }
  
    bool run(Stack* stack, bool isRelative, bool center, Point3i ds);
  };
  
  /**
   * \class ScaleStack StackProcess.hpp <StackProcess.hpp>
   *
   * Scale the stack.
   */
  class mgxBase_EXPORT ScaleStack : public Process 
  {
  public:
    ScaleStack(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Scale Stack");
      setDesc("Scale the stack.");
      setIcon(QIcon(":/images/Scale.png"));
      
      addParm("Percent","Percent","Yes", QStringList() << "Yes" << "No");
      addParm("X","X","0.0");
      addParm("Y","Y","0.0");
      addParm("Z","Z","0.0");				
	}
  
    bool run()
    {
      if(!checkState().stack(STORE_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      Point3f newsize(parm("X").toFloat(), parm("Y").toFloat(), parm("Z").toFloat());
      return run(s, newsize, stringToBool(parm("Percent")));
    }
  
    bool run(Stack* stack, Point3f newsize, bool percent);
  };
  
  /**
   * \class ShiftStack StackProcess.hpp <StackProcess.hpp>
   *
   * Shift main and work stores within the canvas (e.g. the voxels' values are
   * moved within the image)
   */
  class mgxBase_EXPORT ShiftStack : public Process 
  {
  public:
    ShiftStack(const Process& process) : Process(process) 
    {
      setName("Stack/Canvas/Shift Stack");
      setDesc("Shift both stores of the stack to within the canvas.");
      setIcon(QIcon(":/images/Shift.png"));
      
      addParm("Origin","Origin","No", booleanChoice());
      addParm("X","X","0");
      addParm("Y","Y","0");
      addParm("Z","Z","0");		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY))
        return false;
      Stack* s = currentStack();
      Point3i ds(parm("X").toInt(), parm("Y").toInt(), parm("Z").toInt());
      return run(s, stringToBool(parm("Origin")), ds);
    }
  
    bool run(Stack* stack, bool origin, Point3i ds);
  };
  
  /**
   * \class StackRelabel StackProcess.hpp <StackProcess.hpp>
   *
   * Relabel a 3D stack to use consecutive labels. The list of cells is
   * randomized before relabeling to ensure a different labeling at each run.
   */
  class mgxBase_EXPORT StackRelabel : public Process 
  {
  public:
    StackRelabel(const Process& process) : Process(process) 
    {
      setName("Stack/Segmentation/Relabel");
      setDesc("Relabel a 3D stack to use consecutive labels. The cells are shuffled so each relabling will be different.");
      setIcon(QIcon(":/images/Relabel.png"));
      
      addParm("StartLabel","StartLabel","1");
      addParm("Step","Step","1");		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY | STORE_LABEL))
        return false;
      Stack* s = currentStack();
      Store* store = s->currentStore();
      Store* output = s->work();
      int start = parm("StartLabel").toInt();
      int step = parm("Step").toInt();
      if(run(s, store, output, start, step)) {
        store->hide();
        output->show();
        return true;
      }
      return false;
    }
  
    bool run(Stack* stack, const Store* store, Store* output, int start, int step);
  };
  
  /**
   * \class StackRelabelFromMesh StackProcess.hpp <StackProcess.hpp>
   *
   * Change the labels of a stack to match the ones of a labeled 3D cell mesh.
   * Unknown cells (i.e. cells in the stack, not in the mesh), can be either
   * kept or deleted. If kept, they will be relabeled to not conflict with
   * existing cells.
   */
  class mgxBase_EXPORT StackRelabelFromMesh : public Process 
  {
  public:
    StackRelabelFromMesh(const Process& process) : Process(process) 
    {
      setName("Stack/Mesh Interaction/Relabel From Mesh");
      setDesc("Relabel a 3D stack reusing the same labels as in the stack.\n"
      "Unknown cells (i.e. cells in the stack, not in the mesh), \n"
      "can be either kept or deleted. If kept, they will be relabeled\n"
      "to not conflict with existing cells.");
      setIcon(QIcon(":/images/Relabel.png"));
      
      addParm("Delete unknown", "Delete unknown", "Yes", booleanChoice());		
	}
  
    bool run()
    {
      if(!checkState().store(STORE_NON_EMPTY | STORE_LABEL).mesh(MESH_NON_EMPTY | MESH_LABEL))
        return false;
      Stack* s = currentStack();
      Store* store = s->currentStore();
      Store* output = s->work();
      const Mesh* m = currentMesh();
      if(run(s, store, output, m, stringToBool(parm("Delete unknown")))) {
        store->hide();
        output->show();
        return true;
      }
      return false;
    }
  
    bool run(Stack* stack, const Store* store, Store* output, const Mesh* mesh,
                    bool delete_unknown);

  };

  /**
   * \class DeleteOutsideMesh StackProcess.hpp <StackProcess.hpp>
   *
   * Delete labels outside the mesh.
   */
  class mgxBase_EXPORT DeleteOutsideMesh : public Process 
  {
  public:
    DeleteOutsideMesh(const Process& process) : Process(process) 
    {
      setName("Stack/Mesh Interaction/Delete Outside Mesh");
      setDesc("Delete labels outside a mesh of 3D cells based on a threshold distance from the centers.");
      setIcon(QIcon(":/images/Relabel.png"));

      addParm("Threshold", "Threshold Distance (um)", "1");
	  }
  
    bool run()
    {
      Stack* stack = currentStack();
      if(!stack)
        throw QString("%1::run No current stack").arg(name());

      Store* input = stack->currentStore();
      if(!input)
        throw QString("%1::run No current store").arg(name());

      if(!input->labels())
        throw QString("%1::run Store must be labeled").arg(name());

      Store* output = stack->work();
      const Mesh* mesh = currentMesh();
      if(!mesh)
        throw QString("%1::run No current mesh").arg(name());
      if(run(stack, input, output, mesh, parm("Threshold").toDouble())) {
        input->hide();
        output->show();
        return true;
      }
      return false;
    }
  
    bool run(Stack* stack, const Store* input, Store* output, const Mesh* mesh, double threshold);
  }; 

  /**
   * \class SaveTransform StackProcess.hpp <StackProcess.hpp>
   *
   * Save the frame (or transform) matrix to a file, as a list of values in column-major.
   */
  class mgxBase_EXPORT SaveTransform : public Process 
  {
    Q_OBJECT
  
  public:
    SaveTransform(const Process& process) : Process(process) 
    {
      setName("Stack/Transform/Save Transform");
      setDesc("Save the frame matrix (or transform if trans checked) to a file");
      setIcon(QIcon(":/images/save.png"));
      
      addParm("Filename","Filename","");		// 0		
	}
  
    bool initialize(QWidget* parent);
  
    bool run() {
      return run(currentStack(), parm("Filename"));
    }
  
    bool run(Stack* stack, const QString& filename);

  };
  
  /**
   * \class LoadTransform StackProcess.hpp <StackProcess.hpp>
   *
   * Load the frame (or transform) matrix from a file containing a list of values in column-major.
   */
  class mgxBase_EXPORT LoadTransform : public Process 
  {
    Q_OBJECT
  
  public:
    LoadTransform(const Process& process) : Process(process) 
    {
      setName("Stack/Transform/Load Transform");
      setDesc("Save the frame matrix (or transform if trans checked) from a file");
      setIcon(QIcon(":/images/open.png"));
      
      addParm("Filename","Filename","");						
	}
  
    bool initialize(QWidget* parent);
  
    bool run() {
      return run(currentStack(), parm("Filename"));
    }
  
    bool run(Stack* stack, const QString& filename);
  };
  ///@}

  /**
   * \class DistanceStackToMesh StackProcess.hpp <StackProcess.hpp>
   *
   * Fills the work stack with voxel values according to their distance to the current mesh
   */
  class mgxBase_EXPORT DistanceStackToMesh : public Process
  {
  public:
    DistanceStackToMesh(const Process& process) : Process(process) 
    {
      setName("Stack/Mesh Interaction/Distance Stack to Mesh");
      setDesc("Fills the work stack with voxel values according to their distance to the current mesh");
      setIcon(QIcon(":/images/MeshDistance.png"));
      
      addParm("Distance Multiplier","The distance in um is multiplied by this value and rounded to the nearest integer. \n"
      "This is done to increase the range of the values and to provide a higher accuracy.","10.0");			
	}

    //bool initialize(QWidget* parent);

    bool run()
    {
      Stack* s1 = currentStack();
      Store* store1 = s1->main();
      Store* store2 = s1->work();
      Mesh* m = currentMesh();
      return run(s1, store1, store2, m, parm("Distance Multiplier").toDouble());
    }
    bool run(Stack* s1, Store* store1, Store* store2, Mesh* m, double mult);

  };

  std::set<int> mgxBase_EXPORT findNeighborsLabel(Store* store, Point3i imgSize, Point3i p, int radius = 1);
}

#endif
