https://github.com/PDLABCBGP/ROOTMODEL-PBD
Raw File
Tip revision: 3251ec9fb61c1d726b2960195e15f74fe2dd9249 authored by PDLAB on 27 October 2021, 16:12:49 UTC
Merge pull request #2 from PDLABCBGP/add-license-2
Tip revision: 3251ec9
Root.hpp
//
// This file is part of MorphoDynamX - http://www.MorphoDynamX.org
// Copyright (C) 2012-2016 Richard S. Smith and collaborators.
//
// If you use MorphoDynamX in your work, please cite:
//   http://dx.doi.org/10.7554/eLife.05864
//
// MorphoDynamX 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.
// beeeeeeeeeeeeeeeh

#ifndef ROOT_HPP
#define ROOT_HPP

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include "PBD.hpp"
#include <Attributes.hpp>
#include <Function.hpp>
#include <MDXProcessTissue.hpp>
#include <MeshProcessSystem.hpp>
#include <Contour.hpp>
#include <CCTopology.hpp>
#include <Mesh.hpp>
#include <MeshBuilder.hpp>
#include <Process.hpp>
#include <MDXProcessCellDivide.hpp>
#include <MeshProcessStructure.hpp>
#include <ToolsProcessSystem.hpp>
#include <CellMakerUtils.hpp>

#include <CCVerify.hpp>
#include <CCIndex.hpp>
#include <CellMakerMesh2D.hpp>
#include <GeomMathUtils.hpp>
#include <Geometry.hpp>
#include <Matrix.hpp>
#include <Triangulate.hpp>
#include <limits>
#include <math.h>
#include <CCDivideCell.hpp>
#include <cstdlib>
#include "tbb/concurrent_unordered_map.h"
#include <fstream>
#include <chrono>
#include <complex>
#include <ctime>

using namespace mdx;

namespace ROOT
{

// pre-definition
class Root;
class Remesh;
class MechanicalGrowth;
class SetGlobalAttr;
class ClearCells;
class TriangulateFacesX;
class DeleteCell;
class ExecuteTest;
class AddFace;




class Mechanics : public Process{
public:
    Mechanics(const Process& process)
        : Process(process) {
        setName("Model/Root/05 Mechanics");
        addParm("Dt", "Time step in hours", "0.03");
        addParm("Converge Threshold", "Threshold for convergence", ".1");
        addParm("Convergence Lag", "Convergence Lag", "1");
        addParm("Verbose", "Verbose", "True",
                QStringList() << "True"
                              << "False");
        addParm("Debug Steps", "Debug Steps", "10");

        // Mass Springs
        addParm("Mass Spring Parameters", "", "");
        addParm("Wall EK", "Stiffness of the cross springs", "1");
        addParm("Wall CK", "Stiffness of the cross springs", "1");
        addParm("Shear EK", "Extensional Stiffness of the shear edge springs", "0");
        addParm("Shear CK", "Compression Stiffness of the shear edge springs", "1");
        addParm("Auxin-induced wall relaxation K1", "Auxin-induced wall relaxation K1", "0.05");
        addParm("Auxin-induced wall relaxation K2", "Auxin-induced wall relaxation K2", "3");
        addParm("Wall stress", "Wall stress", "1");
        addParm("Wall stress K1", "Wall stress K1", "0.01");
        addParm("Wall stress K2", "Wall stress K2", "2");
        // Hydrostatics
        addParm("Hydrostatic Parameters", "", "");
        addParm("Turgor Pressure", "Value of the turgor pressure in the cells", "2");
        addParm("Turgor Pressure Rate", "Value of the rate of turgor pressure in the cells", "0.5");
        // External Forces
        addParm("External Forces", "", "");
        addParm("Gravity Force", "Gravity Force", "0");
        addParm("Gravity Direction", "Gravity Direction", "0,-1,0");
        addParm("Friction", "Friction", "0");
        // Misc
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");
        addParm("PBD Engine",
                "Name of PBD Engine",
                "Model/Root/07 PBD Engine");
    }


    double calcNorm();
    bool initialize(QWidget* parent);
    bool rewind(QWidget* parent);
    bool step();
    PBD* PBDProcess = 0;
    double Dt = 0;
    double userTime = 0;
    double realTime = 0;
    std::vector<double> growthRatesVector;

private:
    void calcForces(CCIndex v, const CCStructure& cs, const CCIndexDataAttr& indexAttr,
                    Tissue::CellDataAttr &cellAttr, Tissue::FaceDataAttr &faceAttr,
                    Tissue::EdgeDataAttr &edgeAttr, Tissue::VertexData& vD);

    Point3d calcForcesFace(CCIndex f,
        Tissue::CellData& cD, Tissue::FaceData& fD, Tissue::EdgeData& eD, Tissue::VertexData& vD);
    Point3d calcForcesEdge(
        const CCStructure& cs, const CCIndexDataAttr& indexAttr, CCIndex e, CCIndex v, int label);

    CCIndexDataAttr* indexAttr = 0;
    Tissue* tissueProcess = 0;
    double wallStress = 0, wallStressK1 = 0, wallStressK2 = 0;
    Point3d gravity;
    double convergeThresh = 1e-6;
    double convergenceLag = 0;
    double normal = 0, prev_normal;
    Point3d prevQCcentroid , QCcentroid;
    int debug_step = 0;
    bool Verbose = false;
};

class SetDirichlet : public Process {
public:
    SetDirichlet(const Process& process)
        : Process(process) {

        setName("Model/Root/24 Set Dirichlet");
        setDesc("Assign Dirichlet to vertexes.");
        setIcon(QIcon(":/images/CellType.png"));

        addParm("Dirichlet", "Set Dirichlet condition on selected vtx in x,y,z", "0 0 0");
    }
    bool initialize(QWidget* parent);
    bool run();

private:
    Point3u dirichletFixed;
};


class MechanicalGrowth : public Process {
public:
    MechanicalGrowth(const Process& process)
        : Process(process) {
        setName("Model/Root/052 Mechanical Growth");
        addParm("Verbose", "Verbose", "True",
                QStringList() << "True"
                              << "False");
        addParm("Polarity Method",
                "Polarity Method",
                "Cell Axis",
                QStringList() << "Cell Axis"
                              << "Principal Strains"
                              << "Membrane Stretch");
        addParm("Strain Tensor",
                "Strain Tensor",
                "Green Strain Tensor",
                QStringList() << "Green Strain Tensor"
                              << "Shape Strain Tensor");
        addParm("Walls Growth", "Walls Growth", "");
        addParm("Strain Threshold for Growth", "Strain Threshold for Growth", "0.01");
        addParm("Walls Growth Rate", "Walls Growth Rate", "10");
        addParm("Area Growth Rate", "Area Growth Rate", "0");
        addParm("MicroFibrils", "MicroFibrils", "");
        addParm("MF reorientation rate", "MF Reorientation Rate", "0.02");
        addParm("MF reorientation strainrate",
                "MF Reorientation strainrate",
                "0.02");
        addParm("MF Degradation", "MF Degradation", "0.01");
        addParm("MF Delete After Division", "MF Delete After Division","True",
                QStringList() << "True"
                              << "False");
        addParm("Zonation", "Zonation", "");
        addParm("Elongation Zone", "Elongation Zone", "50");
        addParm("Differentiation Zone", "Differentiation Zone", "100");
        addParm("Mechanics Process",
                "Name of Mechanics derivatives process",
                "Model/Root/05 Mechanics");
        addParm("Mechanical Solver Process",
                "Name of Mechanical Solver Process",
                "Model/Root/051 Mechanical Solver");

    }

    bool initialize(QWidget* parent);
    bool step(double Dt);

private:
    Mechanics* mechanicsProcess = 0;
    PBD* PBDProcess = 0;
    bool Verbose = false;
};


/* Suggestions:
 * - High auxin -> higher auxin degradation
 * - High auxin -> lower PIN degradation on membranes  (Paciorek et al. 2005.
 * - High auxin -> Lower PIN expression  (Vieten 2005) or higher PIN degradation in cytoplasm (Baster et al., 2013).
 * - Growth dynamically affect by auxin? (tested: does not seems to work well)
*/
class Chemicals : public Process {
public:
    Chemicals(const Process& process)
        : Process(process) {
        setName("Model/Root/06 Chemicals");
        addParm("Dt", "Dt", "0.01");
        addParm("Converge Threshold", "Converge Threshold", "0.1");
        addParm("Verbose", "Verbose", "True",
                QStringList() << "True"
                              << "False");
        addParm("Debug Steps", "Debug Steps", "10");

        addParm("Auxin", "Auxin", "");
        addParm("Auxin Source", "Auxin Source", "0");
        addParm("Auxin Intercellular Diffusion", "Auxin Intercellular Diffusion", "1");
        addParm("Auxin Cell Permeability", "Auxin Cell Permeability", "0.2");
        addParm("Auxin Basal Production Rate", "Auxin Basal Production Rate", "0");
        addParm("Auxin QC Basal Production Rate", "Auxin QC Basal Production Rate", "0");
        addParm("Auxin SCN Basal Production Rate", "Auxin SCN Basal Production Rate", "0");
        addParm("Auxin Decay Rate", "Auxin Decay Rate", "0.0125");
        addParm("Auxin Max Decay Rate", "Auxin Decay Rate", "0.125");
        addParm("Auxin Max Amount Cell", "Auxin Max Amount (auxin per nm squared)", "3");
        addParm("Auxin Max Amount Edge", "Auxin Max Amount (auxin per nm)", "10");
        addParm("Pin1", "Pin1", "");
        addParm("Pin1 Basal Production Rate", "Pin1 Basal Production Rate", "0.2");
        addParm("Pin1 Max Auxin-induced Expression", "Pin1 Max Auxin-induced Expression", "30");
        addParm("Pin1 Half-max Auxin-induced K", "Pin1 Half-max Auxin-induced K", "0.05");
        addParm("Pin1 Max Concentration", "Pin1 Concentration", "2");
        //addParm("Pin1 Half-max Auxin-induced Decay", "Pin1 Half-max Auxin-induced Decay", "2000");
        //addParm("Pin1 Max Auxin-induced Decay", "Pin1 Max Auxin-induced Decay", "1");
        addParm("Pin1 Cytoplasmic Decay Rate", "Pin1 Cytoplasmic Decay Rate", "0.08");
        addParm("Pin1 Membrane Max Decay Rate", "Pin1 Membrane Max Decay Rate", "0.8");
        addParm("Pin1 Max Trafficking Rate", "Pin1 Max Trafficking Rate", "1");
        addParm("Pin1 Max Amount Edge", "Pin1 Max Amount Edge( auxin per nm)", "15");
        addParm("Pin1-auxin export rate", "Pin1-auxin export rate", "1.4");
        addParm("Pin1 Sensitivity Suppression by Auxin Amount",
                "Pin1 Sensitivity Suppression by Auxin Amount (auxin per nm squared)", "400"); //// be careful
        addParm("Pin1 Sensitivity Suppression by Auxin Max Cell",
                "Pin1 Sensitivity Suppression by Auxin Max Cell (auxin per nm squared)", "True", QStringList() << "True" << "False" ); //// be careful
        addParm("Simulate PIN4", "Simulate PIN4", "False", QStringList() << "True" << "False" );
        addParm("Columella Auto-Efflux", "Columella Auto-Efflux", "True", QStringList() << "True" << "False" );
        addParm("Pin1 Sensitivity MF K", "Pin1 Sensitivity MF K", "0");
        addParm("Pin1 Sensitivity Auxin-flux K", "Pin1 Sensitivity Auxin-flux K", "3");
        addParm("Pin1 Sensitivity Geometry K", "Pin1 Sensitivity Geometry K", "2");
        addParm("Pin1 Sensitivity MF+Auxin-flux K", "Pin1 Sensitivity MF+Auxin-flux K", "3");
        addParm("Pin1 Sensitivity MF+Geometry K", "Pin1 Sensitivity MF+Geometry K", "0");
        addParm("Pin1 Sensitivity Auxin-flux+Geometry K", "Pin1 Sensitivity Auxin-flux+Geometry K", "0");
        addParm("Pin1 Sensitivity Average Method", "Pin1 Sensitivity Average Method", "Soft-max",
                                                        QStringList() << "Soft-max"
                                                                      << "Arithmetic Average");

        addParm("Auxin Polarity Method", "Auxin Polarity Method", "Flow",
                QStringList() << "PINOID"
                              << "Flow");
        addParm("Auxin-Flux Impact Half-max", "Auxin-Flux Impact Half-max", "0.1");
        addParm("PINOID Impact Half-max", "PINOID Impact Half-max", "0.1");
        addParm("MF Impact Half-max",
                "MF Impact Half-max", "0.5");
        addParm("Geometry Impact Half-max",
                "Geometry Impact Half-max", "0.5");
        addParm("Aux1", "Aux1", "");
        addParm("Aux1 Basal Production Rate", "Aux1 Basal Production Rate", "1");
        addParm("Aux1 Half-max Auxin-induced K", "Aux1 Half-max Auxin-induced K", "0.01");
        addParm("Aux1 Max Auxin-induced Expression", "Aux1 Max Auxin-induced Expression", "30");
        addParm("Aux1 Max Concentration", "Aux1 Max Concentration", "2");
        addParm("Aux1 Cytoplasmic Decay Rate", "Aux1 Cytoplasmic Decay Rate", "0.08");
        addParm("Aux1 Max Trafficking Rate", "Aux1 Max Trafficking Rate", "1");
        addParm("Aux1 Max Amount Edge", "Aux1 Max Amount Edge", "15");
        addParm("Aux1-auxin import rate", "Aux1-auxin import rate", "1");
        addParm("Division Inhibitor", "Division Inhibitor", "");
        addParm("Division Inhibitor Basal Production Rate", "Division Inhibitor Basal Production Rate", "0");
        addParm("Division Inhibitor Max Promoter-induced Expression", "Division Inhibitor Max Promoter-induced Expression", "20");
        addParm("Division Inhibitor Half-max Promoter-induced K", "Division Inhibitor Half-max Promoter-induced K", "5"); // 20 or more for the mutant
        addParm("Division Inhibitor Half-max Promoter-induced n", "Division Inhibitor Half-max Promoter-induced n", "2");
        addParm("Division Inhibitor Decay Rate", "Division Inhibitor Decay Rate", "0.01");
        addParm("Division Inhibitor Permeability", "Division Inhibitor Permeability", "0");
        addParm("Division Promoter", "Division Promoter", "");
        addParm("Division Promoter Basal Production Rate", "Division Promoter Basal Production Rate", "0");
        addParm("Division Promoter Max Auxin-induced Expression", "Division Promoter Max Auxin-induced Expression", "20");
        addParm("Division Promoter Half-max Auxin-induced K", "Division Promoter Half-max Auxin-induced K", "2");
        addParm("Division Promoter Half-max Auxin-induced n", "Division Promoter Half-max Auxin-induced n", "4");
        addParm("Division Promoter Decay Rate", "Division Promoter Decay Rate", "0.01");
        addParm("Division Promoter Permeability", "Division Promoter Permeability", "1"); // 1 for the data, 5 for the figure
        addParm("Phosphorilation", "Phosphorilation", "");
        addParm("PINOID Basal Production Rate", "PINOID Basal Production Rate", "10");
        addParm("PP2A Basal Production Rate", "PP2A Basal Production Rate", "10");
        addParm("PINOID Dilution Rate", "PINOID Dilution Rate", "0.1");
        addParm("PP2A Dilution Rate", "PP2A Dilution Rate", "1");
        addParm("PINOID Decay Rate", "PINOID Decay Rate", "0.08");
        addParm("PP2A Decay Rate", "PP2A Decay Rate", "0.08");
        addParm("PINOID Trafficking Rate", "PINOID Trafficking Rate", "1");
        addParm("PP2A Trafficking Rate", "PP2A Trafficking Rate", "0.01");
        addParm("PINOID Max Amount Edge", "PINOID Max Amount Edge", "20");
        addParm("PP2A Max Amount Edge", "PP2A Max Amount Edge", "20");
        addParm("PINOID Displacing K", "PINOID Displacing K", "10");
        addParm("PINOID Fluidity K", "PINOID Fluidity K", "0.1");
        addParm("Auxin-PP2A Relief T", "Auxin-PP2A Relief T", "1");
        addParm("Auxin-PP2A Relief K", "Auxin-PP2A Relief K", "1");
        addParm("PINOID-PP2A Trafficking Toggle K", "PINOID-PP2A Trafficking Toggle K", "100");
        addParm("PINOID-PP2A Disassociation Toggle K", "PINOID-PP2A Disassociation Toggle K", "100");
        addParm("Geom-PP2A Relief T", "Geom-PP2A Relief T", "0.1");
        addParm("Geom-PP2A Relief K", "Geom-PP2A Relief K", "0.5");
        addParm("MF-PP2A Relief T", "Geom-PP2A Relief T", "0.1");
        addParm("MF-PP2A Relief K", "Geom-PP2A Relief K", "0.5");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");
        addParm("Chemicals Solver Process",
                "Name of Chemicals solver process",
                "Model/Root/061 Chemicals Solver");
    }
    bool initialize(QWidget* parent);
    bool rewind(QWidget* parent);
    bool step();
    bool update();
    double calcNorm();
    Point8d calcDerivatives(CCIndex v, const CCStructure& csDual, const CCIndexDataAttr& indexAttr, Tissue::CellDataAttr &cellAttr,
                            Tissue::FaceDataAttr &faceAttr,
                            Tissue::EdgeDataAttr& edgeAttr, Tissue::VertexDataAttr& vMAttr);
    void calcDerivsEdge(const CCStructure &cs, const CCIndexDataAttr &indexAttr,
                        Tissue::EdgeDataAttr& edgeAttr,
                        CCIndex e, double Dt);
    void calcDerivsCell(const CCStructure& cs,
                           const CCStructure& csDual,
                           const CCIndexDataAttr& indexAttr,
                           Tissue::CellDataAttr& cellAttr,
                           Tissue::EdgeDataAttr& edgeAttr,
                           int label, double Dt);
    tbb::interface5::concurrent_unordered_map<string, double> debugs;
private:
    double Dt = 0;
    Tissue* tissueProcess = 0;
    Mesh* mesh = 0;
    CCIndexDataAttr* indexAttr = 0;
    Tissue::CellDataAttr* cellAttr = 0;
    Tissue::FaceDataAttr* faceAttr = 0;
    Tissue::VertexDataAttr* vMAttr = 0;
    double  userTime = 0;
    double convergeThresh = 0.01;
    int debug_step = 0;
};


struct MDXSubdivideX : public Subdivide
{
  MDXSubdivideX() {}
  MDXSubdivideX(Mesh &mesh);

  void splitCellUpdate(Dimension dim, const CCStructure &cs, const CCStructure::SplitStruct &ss,
      CCIndex otherP = CCIndex(), CCIndex otherN = CCIndex(), double interpPos = 0.5);

  Mesh *mesh = 0;
  CCIndexDataAttr *indexAttr = 0;
  Subdivide *nextSubdivide = 0;

  std::vector<CCIndexDoubleAttr*> attrVec;
};


class CellDivision : public Process, virtual public Cell2dDivideParms {
public:
    CellDivision(const Process& process)
        : Process(process) {
        //setName("03 Divide Cells");
        setDesc("Divide cells Parameters for cell division on tissue mesh.");
        setIcon(QIcon(":/images/CellDivide.png"));
        addParm("Verbose", "Verbose", "True",
                QStringList() << "True"
                              << "False");
        addParm("Division Algorithm",
                "Name of the Division Algorithm",
                "1",
                QStringList() << "0"
                              << "1"
                              << "2"  );
        addParm("Max Joining Distance",
                "When using division algorithm 2, the closest existing division point will be used as \
                joining point, up to this maximum distance",
                "1" );
        addParm("Minimum Polarity Vector Norm",
                "Minimum Polarity Vector Norm",
                "0.05" );
        addParm("Max Division Time",
                "Max Division Time",
                "50");
        addParm("Min Division Time",
                "Min Division Time",
                "2");
        addParm("Division Meristem Size",
                "Division Meristem Size",
                "200");
        addParm("Division Promoter Level",
                "Division Promoter Level",
                "0.05" );
        addParm("Division half-probability by Cell Size Ratio",
                "Division half-probability by Cell Size Ratio",
                "1.0" );
        addParm("Division half-probability by Inhibitor",
                "Division half-probability by Inhibitor",
                "0.03" );
        addParm("Division Control",
                "Division Control",
                "False",
                                QStringList() << "True"
                                              << "False");
        addParm("Ignore Cell Type",
                "Ignore Cell Type",
                "False",
                                QStringList() << "True"
                                              << "False");
        addParm("Root Process", "Name of the process for the Root", "Model/Root/01 Root");
        addParm("Remesh", "Remesh", "Model/Root/02 Remesh");
        addParm("Triangulate Faces Process", "Triangulate Faces", "Model/Root/Triangulate Faces");
        addParm("ClearCells Process", "ClearCells", "Model/Root/ClearCells");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");

    }

    ~CellDivision() {}

    // Initialize simulation, called from GUI thread
    bool initialize(QWidget* parent);

    // Process the parameters
    bool processParms();

    // Run a step of cell division
    bool step() {
        Mesh* mesh = currentMesh();
        return step(mesh, &subdiv);
    }

    // Run a step of cell division
    virtual bool step(Mesh* mesh, Subdivide* subdiv);

    // Subdivide object
    MDXSubdivideX* subdivider() {
        return &subdiv;
    }
    Tissue::FaceDataAttr* FaceDataAttr = 0;

private:
    MDXSubdivideX subdiv;
    bool Verbose = false;
    Root* rootProcess = 0;
    Remesh* remeshProcess = 0;
    ClearCells* clearCellsProcess = 0;
    TriangulateFacesX* triangulateProcess = 0;
    Tissue* tissueProcess = 0;
    Point3d divVector;
};




class Splitter : virtual public mdx::Subdivide {
public:
    Splitter(bool cellDivision = false) {
        this->cellDivision = cellDivision;
    }

    void splitCellUpdate(Dimension dim,
                         const CCStructure& cs,
                         const CCStructure::SplitStruct& ss,
                         CCIndex otherP = CCIndex(),
                         CCIndex otherN = CCIndex(),
                         double interpPos = 0.5);

    MDXSubdivideX mdx;
    Tissue::Subdivide mechanics;
    bool cellDivision = false;
};

class RootDivide : public CellDivision {
public:
    RootDivide(const Process& process)
        : CellDivision(process) {
        setName("Model/Root/04 Divide Cells");

        addParm("Cell Division Enabled", "Cell Division Enabled", "True",
                                                        QStringList() << "False"
                                                                      << "True");
        addParm("Manual Cell Division Enabled", "Manual Cell Division Enabled", "False",
                                                        QStringList() << "False"
                                                                      << "True");
        setParmDefault("Cell Max Area", "100");
        setParmDefault("Cell Wall Min", "0.1");
    }

    // Initialize to grab subdivider
    bool initialize(QWidget* parent);

    // Run a step of cell division
    bool step(double Dt);

    Splitter& subdivider() {
        return subdiv;
    }

private:
    Splitter subdiv;

};


/**
 * \class TriangulateFaces CellMakerProcesses2D.hpp <CellMakerProcesses2D.hpp>
 * takes all faces of the CC and triangulates them using triangle
 *
 * TriangulateFaces
 */
class TriangulateFacesX : public Process {
public:
    TriangulateFacesX(const Process& process)
        : Process(process) {
        setName("Model/Root/Triangulate Faces");
        setDesc("Triangulate all polygonal faces.");
        addParm("Max Area", "Max area for triangles", "100");
        addParm("Destructive", "Destructive", "False",
                                                        QStringList() << "False"
                                                                      << "True");

    }

    bool triangulateFace(CCIndex f, CCStructure& cs,
                         CCIndexDataAttr& indexAttr,
                         double maxArea);

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::TriangulateFacesX No current mesh"));

        CCIndexDataAttr& indexAttr = mesh->indexAttr();
        CCStructure& cs = mesh->ccStructure("Tissue");
        for(CCIndex f : cs.faces())
            if(indexAttr[f].selected) {
                triangulateFace(f, cs, indexAttr, parm("Max Area").toDouble());
                step(false);
                return false;
            }
        return step(parm("Destructive") == "True");
    }


    bool step(bool destructive) {
        Mesh* mesh = currentMesh();
        if(!mesh)
            throw QString("%1 No current mesh").arg(name());

        QString ccName = mesh->ccName();
        CCStructure& cs = mesh->ccStructure(ccName);
        CCIndexDataAttr& indexAttr = mesh->indexAttr();

        QString ccNameOut = ccName;
        CCStructure& csOut = mesh->ccStructure(ccNameOut);
        bool vVisible = mesh->drawParms(ccName).isGroupVisible("Vertices");
        bool eVisible = mesh->drawParms(ccName).isGroupVisible("Edges");
        bool fVisible = mesh->drawParms(ccName).isGroupVisible("Faces");
        bool result;
        if(destructive)
            result = step_destructive(cs, csOut, indexAttr, parm("Max Area").toDouble());
        else
            result = step_nondestructive(cs, indexAttr, parm("Max Area").toDouble());
        if(result)
            ccUpdateDrawParms(*mesh, ccName, ccNameOut);
        else {
            mesh->erase(ccNameOut);
            throw QString("%1 Triangulation of mesh failed").arg(name());
        }
        mesh->drawParms(ccNameOut).setGroupVisible("Vertices", vVisible);
        mesh->drawParms(ccNameOut).setGroupVisible("Edges", eVisible);
        mesh->drawParms(ccNameOut).setGroupVisible("Faces", fVisible);
        mesh->updateAll(ccNameOut);
        return false;
    }
    bool step_destructive(CCStructure& cs, CCStructure& csOut, CCIndexDataAttr& indexAttr, double maxArea);
    bool step_nondestructive(CCStructure& cs, CCIndexDataAttr& indexAttr, double maxArea);
};


class ClearCells : public Process {
public:
    ClearCells(const Process& process)
        : Process(process) {
        setName("Model/Root/ClearCells");
        setDesc("ClearCells.");
        addParm("AddFace", "AddFace", "Model/Root/Add Face");
    }


    CCIndex clearCell(int label);
    bool clearCell_old(int label);



    bool clearAllCells();

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::ClearCells No current mesh"));
        CCIndexDataAttr& indexAttr = mesh->indexAttr();
        CCStructure& cs = mesh->ccStructure("Tissue");
        for(CCIndex f : cs.faces())
            if(indexAttr[f].selected) {
                clearCell(indexAttr[f].label);
                return false;
            }
        return clearAllCells();
    }

    AddFace *addFaceProcess = 0;

};

// This process is a big mess, it should be rewritten completely
// remesh the CS, a very messy job, remember to reinitialize
// all the processes as the CS is destroyed
class Remesh : public Process {
public:
    Remesh(const Process& process)
        : Process(process) {
        setName("Model/Root/02 Remesh");
        setDesc("Remesh");
        setIcon(QIcon(":/images/Recycle.png"));
        addParm("Destructive", "Destructive", "False",
                                                        QStringList() << "False"
                << "True");
        addParm("Type", "Type", "Hard",
                                                        QStringList() << "Hard"
                << "Soft");
        addParm("Split Edges Max Length", "Split Edges Max Length", "3");

        addParm("Remeshing Min Area", "Minimum triangle area that triggers remeshing", "0");
        addParm("Remeshing Max Area", "Maximum triangle area that triggers remeshing", "100");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");
        addParm("Triangulate Faces", "Triangulate Faces", "Model/Root/Triangulate Faces");
        addParm("ClearCells", "ClearCells", "Model/Root/ClearCells");
        addParm("SplitEdges", "SplitEdges", "Mesh/Structure/Split Edges");
        addParm("Polygonize", "Polygonize", "Tools/Cell Maker/Mesh 2D/Tools/Polygonize Triangles");
    }

    bool initialize(QWidget* parent) {
        if(!getProcess(parm("Tissue Process"), tissueProcess))
            throw(QString("Root::initialize Cannot make Tissue Process:") + parm("TissueProcess"));
        if(!getProcess(parm("Triangulate Faces"), triangulateProcess))
            throw(QString("Root::initialize Cannot make Triangulate Faces") +
                  parm("Triangulate Faces"));
        if(!getProcess(parm("ClearCells"), clearCellsProcess))
            throw(QString("Root::initialize Cannot make ClearCells") + parm("ClearCells"));
        if(!getProcess(parm("SplitEdges"), splitEdgesProcess))
            throw(QString("Root::initialize Cannot make SplitEdges") + parm("SplitEdges"));
        if(!getProcess(parm("Polygonize"), polygonizeProcess))
            throw(QString("Root::initialize Cannot make Polygonize") + parm("Polygonize"));

        Mesh* mesh = getMesh("Mesh 1");
        indexAttr = &mesh->indexAttr();
        widget_parent = parent;
        return true;
    }

    void polygonize(bool destructive = false) {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");
        if(destructive)
            polygonizeTriangles(cs, cs, *indexAttr, cs.faces(), 0);
        else
            clearCellsProcess->step();
    }


    void check() {
        Mesh* mesh = getMesh("Mesh 1");
        Tissue::CellDataAttr& cellAttr = mesh->attributes().attrMap<int, Tissue::CellData>("CellData");
        double maxArea = parm("Remeshing Max Area").toDouble();
        double minArea = parm("Remeshing Min Area").toDouble();
        std::set<int> cellToRemesh;

        // check if we should remesh
        for(auto c : cellAttr) {
            Tissue::CellData& cD = cellAttr[c.first];
            if(c.second.label == -1) // ?
                cellToRemesh.insert(cD.label);
            for(CCIndex f : *cD.cellFaces)
                if((*indexAttr)[f].measure > maxArea ||
                   (*indexAttr)[f].measure < minArea) {
                    mdxInfo << "Face " << f << " : "
                            << " label " << (*indexAttr)[f].label << " of size " << (*indexAttr)[f].measure
                            << " triggered remeshing" << endl;
                    cellToRemesh.insert(cD.label);
                }
        }

        if(cellToRemesh.size() == 0)
            return;
/*
        for(int label : cellToRemesh) {
            CCIndex ft = clearCellsProcess->clearCell(label);
            updateGeometry(cs, (*indexAttr));
            (*indexAttr)[ft].selected = true;
        }

        triangulateProcess->step();
        tissueProcess->initialize();*/
        step(true, false, false);

        return ;
    }

    bool step(bool forceRemesh=false, bool destructive = false, bool soft = false) {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");

        mdxInfo << "Remeshing!" << (soft ? "(Soft)" : "(Hard)") << endl;
        Splitter subdiv;
        // Setup subdivision objects
        subdiv.mdx = MDXSubdivideX(*mesh);
        subdiv.mechanics =
            Tissue::Subdivide(*indexAttr,
                              mesh->attributes().attrMap<CCIndex, Tissue::VertexData>("VertexData"),
                              mesh->attributes().attrMap<CCIndex, Tissue::EdgeData>("EdgeData"),
                              mesh->attributes().attrMap<CCIndex, Tissue::FaceData>("FaceData"),
                              mesh->attributes().attrMap<int, Tissue::CellData>("CellData"));

        // take a copy of current cs
        CCStructure csCurr = cs;
        // Set Z zero
        for(CCIndex v : cs.vertices())
            (*indexAttr)[v].pos[2] = 0;
        // create the tissue, so that the dual one is based on one-faced cells
        tissueProcess->initialize(widget_parent);
        // let's wipe delete cells' internal faces
        if(!soft) {
            mdxInfo << "Clearing Cells..." << endl;
            polygonize(destructive);
            splitEdgesProcess->run(*mesh, cs, parm("Split Edges Max Length").toDouble(), &subdiv);
            // create the tissue, so that the dual one is based on one-faced cells
            tissueProcess->initialize(widget_parent);
            // triangulate, this will erase the current cs and create a new one!
            mdxInfo << "Triangulating..." << endl;
            triangulateProcess->step(destructive);
        }
        // restore all tissue's properties, based on the old cs
        mdxInfo << "Restoring Cell Tissue..." << endl;
        tissueProcess->restore(csCurr);
        // update geometries as usual
        tissueProcess->step(EPS);
        // initialize the provided processes
        for(Process* p : processes)
            p->initialize(widget_parent);
        // check the CS for consistency
        if(!verifyCCStructure(cs, *indexAttr))
            throw(QString("Remesh::step The Cell Complex is broken"));

        return false;
    }

    bool run(){
        bool soft = parm("Type") == "Soft";
        bool destructive = parm("Destructive") == "True";
        step(true, destructive, soft);
        return false;
    }

    void setProcesses(std::vector<Process*> processes) {
        this->processes = processes;
    }

private:
    QWidget* widget_parent = 0;
    CCIndexDataAttr* indexAttr = 0;
    Tissue* tissueProcess = 0;
    ClearCells* clearCellsProcess = 0;
    mdx::SplitEdges* splitEdgesProcess = 0;
    Process* polygonizeProcess = 0;
    TriangulateFacesX* triangulateProcess = 0;
    std::vector<Process*> processes;
};


// Main model class
class Root : public Process {
public:
    Root(const Process& process)
        : Process(process) {
        setName("Model/Root/01 Root");
        addParm("Max Mechanical Iterations", "Max Mechanical Iterations", "1");
        addParm("Max Chemical Iterations", "Max Chemical Iterations", "1");
        addParm("Mechanics Enabled",
                "Mechanics Enabled",
                "True",
                QStringList() << "True"
                              << "False");
        addParm("Chemicals Enabled",
                "Chemicals Enabled",
                "True",
                QStringList() << "True"
                              << "False");
        addParm("Growth Enabled",
                "Growth Enabled",
                "True",
                QStringList() << "True"
                              << "False");
        addParm("Cell Division Enabled",
                "Cell Division Enabled",
                "True",
                QStringList() << "True"
                              << "False");
        addParm("Remesh at start",
                "Remesh at start",
                "Hard",
                QStringList() << "Hard"
                              << "Soft"
                              << "None");
        addParm("Remesh during execution",
                "Remesh during execution",
                "True",
                QStringList() << "True"
                              << "False");
        addParm("Mesh Update Timer", "Mesh Update Timer", "1");
        addParm("Snapshots Timer",
                "Time frames between snapshots",
                "0");
        addParm("Snapshots Directory",
                "Snapshots Directory",
                "");
        addParm("Debug",
                "Debug",
                "True",
                QStringList() << "False"
                              << "True");
        addParm("Debug File", "Debug File", "debug.csv");
        addParm("Frame fixed on QC", "Frame fixed on QC", "0");
        addParm("Frame fixed on Substrate", "Frame fixed on Substrate", "0");
        addParm("Execution Time", "Execution Time", "0");
        addParm("Output Mesh", "Output Mesh", "output.mdxm");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");
        addParm("Mechanical Solver Process",
                "Name of Mechanical Solver Process",
                "Model/Root/051 Mechanical Solver");
        addParm("Mechanics Process", "Name of Mechanics Process", "Model/Root/05 Mechanics");
        addParm("Mechanical Growth Process",
                "Name of the process for Mechanical Growth",
                "Model/Root/052 Mechanical Growth");
        addParm(
            "Chemicals Process", "Name of the process for Chemicals", "Model/Root/06 Chemicals");
        addParm("Chemicals Solver Process",
                "Name of the process for Chemicals Solver",
                "Model/Root/061 Chemicals Solver");
        addParm("Divide Process",
                "Name of the process for Cell Division",
                "Model/Root/04 Divide Cells");
        addParm("Delete Cell Process",
                "Name of the process for Delete Cell",
                "Model/Root/31 Delete Cell");
        addParm("Execute Test Process",
                "Name of the process for Execute Test",
                "Model/Root/4 Execute Test");
        addParm("Set Global Attr Process",
                "Name of the process for Set Global Attr",
                "Model/Root/23 Set Global Attr");
        addParm("Triangulate Faces", "Triangulate Faces", "Model/Root/Triangulate Faces");
        addParm("Remesh", "Remesh", "Model/Root/02 Remesh");
        addParm("SplitEdges", "SplitEdges", "Mesh/Structure/Split Edges");
        addParm("SaveView", "SaveView", "Tools/System/Save View");
        addParm("SaveMesh", "SaveMesh", "Mesh/System/Save");
    }

    // Initialize the model
    bool initialize(QWidget* parent);

    // Run the model
    // bool run();
    bool step();

    bool finalize();

    // Rewind the model (reloads the mesh)
    bool rewind(QWidget* parent);

    Mesh* mesh = 0;
    QWidget* widget_parent = 0;
    MechanicalGrowth* mechanicalGrowthProcess = 0;
    Chemicals* chemicalsProcess = 0;
    Mechanics* mechanicsProcess = 0;
    Tissue* tissueProcess = 0;
    RootDivide* divideProcess = 0;
    DeleteCell* deleteProcess = 0;
    ExecuteTest* executeTestProcess = 0;
    SetGlobalAttr* setGlobalAttrProcess = 0;
    TriangulateFacesX* triangulateProcess = 0;
    Remesh* remeshProcess = 0;
    Process* splitEdgesProcess = 0;
    SaveViewFile* saveViewProcess = 0;
    MeshSave* saveMeshProcess = 0;
    std::vector<Process*> processes;
    double userTime = 0;

private:
    bool debugging = false;
    bool process_started = false;
    bool snapshot = false;
    string snapshotDir;
    int screenShotCount = 0;
    bool mechanicsEnabled = true;
    bool chemicalsEnabled = true;
    bool growthEnabled = true;
    bool divisionEnabled = true;
    int stepCount = 0, prevStepCount = 0;
    clock_t begin_clock, prev_clock;
    std::ofstream output_file;
    int maxMechanicsIter = 0, maxChemicalIter = 0;
    bool mechanicsProcessConverged = false;
};


class DeleteCell : public Process {
public:
    DeleteCell(const Process& process)
        : Process(process) {
        setName("Model/Root/31 Delete Cell");
        setDesc("DeleteCell.");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");

    }

    bool step(std::set<int> labels) {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");
        Tissue::CellDataAttr &cellAttr =
            mesh->attributes().attrMap<int, Tissue::CellData>(
                "CellData");

        if(!getProcess(parm("Tissue Process"), tissueProcess))
            throw(QString("Root::initialize Cannot make Tissue Process") + parm("Tissue Process"));

        std::set<int> to_delete;
        for(auto c : cellAttr) {
            Tissue::CellData& cD = cellAttr[c.first];
            if(labels.find(cD.label) != labels.end())
                 to_delete.insert(cD.label);
        }
        for(int label : to_delete)
                deleteCell(label);

        tissueProcess->initialize();
        tissueProcess->restore(cs);
        tissueProcess->step(EPS);
        mesh->updateAll();

        return false;
    }

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");
        CCIndexDataAttr& indexAttr = mesh->indexAttr();
        if(!getProcess(parm("Tissue Process"), tissueProcess))
            throw(QString("Root::initialize Cannot make Tissue Process") + parm("Tissue Process"));

        std::set<int> to_delete;
        for(CCIndex f : cs.faces())
            if(indexAttr[f].selected)
                to_delete.insert(indexAttr[f].label);
        for(int label : to_delete)
                deleteCell(label);

        tissueProcess->initialize();
        tissueProcess->restore(cs);
        tissueProcess->step(EPS);
        mesh->updateAll();

        return false;
    }


    void deleteCell(int label) {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");
        CCStructure& csVisual = mesh->ccStructure("TissueVisual");

        CCIndexDataAttr& indexAttr = mesh->indexAttr();
        Tissue::CellDataAttr &cellAttr =
            mesh->attributes().attrMap<int, Tissue::CellData>(
                "CellData");
        Tissue::FaceDataAttr &faceAttr =
            mesh->attributes().attrMap<CCIndex, Tissue::FaceData>(
                "FaceData");
        Tissue::VertexDataAttr &vMAttr =
            mesh->attributes().attrMap<CCIndex, Tissue::VertexData>(
                "VertexData");
        if(cellAttr.find(label) == cellAttr.end())
            throw(QString("DeleteCell: cell labelled" + QString::number(label) + " not found"));
        Tissue::CellData& cD = cellAttr[label];
        std::set<CCIndex> to_delete_faces;
        std::set<CCIndex> to_delete_edges;
        std::set<CCIndex> to_delete_vertices;
        for(CCIndex f : *cD.cellFaces) {
            std::set<int> labels;
            Tissue::FaceData fD = faceAttr[f];
            for(CCIndex e : cs.incidentCells(f, 1)) {
                labels.clear();
                for(CCIndex fn : cs.incidentCells(e, 2))
                    labels.insert(indexAttr[fn].label);
                if(labels.size() == 1)
                    to_delete_edges.insert(e);
            }
            for(CCIndex v : cs.incidentCells(f, 0)) {
                labels.clear();
                for(CCIndex fn : cs.incidentCells(v, 2))
                    labels.insert(indexAttr[fn].label);
                if (labels.size() == 1)
                    to_delete_vertices.insert(v);
            }
            if(csVisual.hasCell(fD.G_v0)) {
                csVisual.deleteCell(fD.G_e1);
                csVisual.deleteCell(fD.G_e2);
                csVisual.deleteCell(fD.G_v0);
                csVisual.deleteCell(fD.G_v1);
                csVisual.deleteCell(fD.G_v2);
            }
            if(csVisual.hasCell(fD.a1_v1)) {
                csVisual.deleteCell(fD.a1_e);
                csVisual.deleteCell(fD.a2_e);
                csVisual.deleteCell(fD.a1_v1);
                csVisual.deleteCell(fD.a1_v2);
                csVisual.deleteCell(fD.a2_v1);
                csVisual.deleteCell(fD.a2_v2);
            }
            to_delete_faces.insert(f);
        }
        for(auto i : to_delete_faces)
            cs.deleteCell(i);
        for(auto i : to_delete_edges)
            cs.deleteCell(i);
        for(auto v : to_delete_vertices) {
            Tissue::VertexData vD = vMAttr[v];
            if(csVisual.hasCell(vD.V_e)) {
                csVisual.deleteCell(vD.V_e);
                csVisual.deleteCell(vD.V_v0);
                csVisual.deleteCell(vD.V_v1);
            }
            cs.deleteCell(v);
        }
        if(csVisual.hasCell(cD.a1_e)) {
            csVisual.deleteCell(cD.a1_e);
            csVisual.deleteCell(cD.a1_v1);
            csVisual.deleteCell(cD.a1_v2);
            csVisual.deleteCell(cD.a2_e);
            csVisual.deleteCell(cD.a2_v1);
            csVisual.deleteCell(cD.a2_v2);
        }
        if(csVisual.hasCell(cD.auxinFlux_f)) {
            std::set<CCIndex> to_delete;
            for(CCIndex e : csVisual.incidentCells(cD.auxinFlux_f, 1))
                to_delete.insert(e);
            csVisual.deleteCell(cD.auxinFlux_f);
            for(CCIndex e : to_delete)
                csVisual.deleteCell(e);
            csVisual.deleteCell(cD.auxinFlux_e);
            csVisual.deleteCell(cD.auxinFlux_v1);
            csVisual.deleteCell(cD.auxinFlux_v2);
            csVisual.deleteCell(cD.auxinFlux_v3);
            csVisual.deleteCell(cD.auxinFlux_v4);
        }
        mdxInfo << "Cell " << cD.label << " in position " << label <<  " removed" << endl;
        cellAttr.erase(label);
    }

private:
    Tissue* tissueProcess = 0;

};



class ExecuteTest : public Process {
private:
    Tissue* tissueProcess = 0;
    DeleteCell* deleteProcess = 0;
    MechanicalGrowth * mechanicalGrowthProcess = 0;
    DeleteSelection* deleteSelectionProcess = 0;
    Remesh* remeshProcess = 0;
    std::map<int, double> cellsProdRates;
    double auxinConc = 0;
    int source_removal_count = 0, alternate_source_count = 0, auxin_overflow_count = 0, QC_ablation_count = 0, LRC_removal_count = 0,
        tip_removal_count = 0, pin2_removal_count = 0, pin1_removal_count = 0, aux1_removal_count = 0,aux1_overexpr_count = 0, strain_removal_count = 0, endodermal_cells_count = 0;
    bool PIN1_knockdown = false;
    bool AUX1_knockdown = false;
    bool AUX1_overexpr = false;
    bool LRC_removed = false;
    bool tip_removed = false;
    bool overflow = false;

public:
    ExecuteTest(const Process& process)
        : Process(process) {
        setName("Model/Root/4 Execute Test");
        setDesc("ExecuteTest.");
        addParm("QC Ablation", "QC Ablation", "0");
        addParm("Source Removal", "Source Removal", "0");
        addParm("Alternate Source Removal", "Alternate Source Removal", "0");
        addParm("Auxin Overflow", "Auxin Overflow", "0");
        addParm("TIP Removal Time", "TIP Removal Time", "0");
        addParm("LRC Removal Time", "LRC Removal Time", "0");
        addParm("PIN2 Knockdown Time", "PIN2 Knockdown Time", "0");
        addParm("PIN1 Knockdown Time", "PIN1 Knockdown Time", "0");
        addParm("AUX1 Knockdown Time", "AUX1 Knockdown Time", "0");
        addParm("AUX1 Overexpression Time", "AUX1 Overexpression Time", "0");
        addParm("Strain Removal Time", "Strain Removal Time", "0");
        addParm("Endodermal Cells Delete", "Endodermal Cells Delete", "0");
        addParm("Tissue Process", "Name of Tissue Process", "Model/Root/03 Cell Tissue");
        addParm("Delete Cell Process", "Delete Cell Process", "Model/Root/31 Delete Cell");
        addParm("Mechanical Growth Process",
                "Name of the process for Mechanical Growth",
                "Model/Root/052 Mechanical Growth");
        addParm("Remesh Process", "Remesh Process", "Model/Root/02 Remesh");
        addParm("Delete Selection Process", "Delete Selection Process", "Mesh/System/Delete Selection");

        source_removal_count = 0, alternate_source_count = 0, auxin_overflow_count = 0, QC_ablation_count = 0, LRC_removal_count = 0,
        tip_removal_count = 0, pin1_removal_count = 0, pin2_removal_count = 0, aux1_removal_count = 0,aux1_overexpr_count = 0, strain_removal_count = 0, endodermal_cells_count = 0;;
        PIN1_knockdown = false;
        AUX1_knockdown = false;
        AUX1_overexpr = false;
        LRC_removed = false;
        tip_removed = false;
        overflow = false;
        cellsProdRates.clear();
        auxinConc = 0;
    }


    bool rewind(QWidget* parent) {
        source_removal_count = 0, alternate_source_count = 0, auxin_overflow_count = 0, QC_ablation_count = 0, LRC_removal_count = 0,
        tip_removal_count = 0, pin2_removal_count = 0, pin1_removal_count = 0, aux1_removal_count = 0,aux1_overexpr_count = 0, strain_removal_count = 0, endodermal_cells_count = 0;
        PIN1_knockdown = false;
        AUX1_knockdown = false;
        AUX1_overexpr = false;
        LRC_removed = false;
        tip_removed = false;
        overflow = false;
        cellsProdRates.clear();
        auxinConc = 0;
        return false;
    }

    bool step() {return false;}

    bool step(int stepCount=0) {
        Mesh* mesh = getMesh("Mesh 1");
        CCStructure& cs = mesh->ccStructure("Tissue");
        CCIndexDataAttr& indexAttr = mesh->indexAttr();
        Tissue::EdgeDataAttr& edgeAttr =
            mesh->attributes().attrMap<CCIndex, Tissue::EdgeData>("EdgeData");
        Tissue::CellDataAttr &cellAttr =
            mesh->attributes().attrMap<int, Tissue::CellData>(
                "CellData");
        Tissue::VertexDataAttr& vMAttr =
            mesh->attributes().attrMap<CCIndex, Tissue::VertexData>("VertexData");

        if(!getProcess(parm("Tissue Process"), tissueProcess))
            throw(QString("Root::initialize Cannot make Tissue Process") + parm("Tissue Process"));
        if(!getProcess(parm("Delete Cell Process"), deleteProcess))
            throw(QString("Root::initialize Cannot make Delete Cell Process:") + parm("Delete Cell Process"));
        if(!getProcess(parm("Mechanical Growth Process"), mechanicalGrowthProcess))
            throw(QString("Root::initialize Cannot make Mechanical Growth Process:") +
                  parm("Mechanical Growth Process"));
        if(!getProcess(parm("Remesh Process"), remeshProcess))
            throw(QString("Root::initialize Cannot make Remesh Process:") +
                  parm("Remesh Process"));
        if(!getProcess(parm("Delete Selection Process"), deleteSelectionProcess))
            throw(QString("Root::initialize Cannot make Delete Selection Process:") + parm("Delete Selection Process"));


        int QC_ablation_time = parm("QC Ablation").toInt();
        QC_ablation_count ++;
        if(QC_ablation_time > 0 && QC_ablation_count > QC_ablation_time) {
            QC_ablation_count = 0;
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::QC)
                     cD.type = Tissue::Undefined;
            }
        }

        int source_removal_time = parm("Source Removal").toDouble();
        source_removal_count ++;
        if(source_removal_time > 0 && source_removal_count > source_removal_time) {
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::Source)
                    cD.setType(Tissue::Substrate, vMAttr);
            }
        }

        int alternate_source_time = parm("Alternate Source Removal").toDouble();
        alternate_source_count ++;
        if(alternate_source_time > 0 && alternate_source_count > alternate_source_time) {
            alternate_source_count = 0;
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.auxinProdRate > 0) {
                    cellsProdRates[cD.label] = cD.auxinProdRate;
                    cD.auxinProdRate = 0;
                }
                else if (cD.auxinProdRate == 0) {
                    cD.auxinProdRate = cellsProdRates[cD.label];
                }
            }
        }

        QStringList list_overflow = parm("Auxin Overflow").split(QRegExp(","));
        if(list_overflow.size() != 4)
             throw(QString("Specify the Auxin Overflow test as start,inverval,amout"));
        int start = list_overflow[0].toInt();
        int interval1 = list_overflow[1].toInt();
        int interval2 = list_overflow[2].toInt();
        int amount = list_overflow[3].toInt();
        if(stepCount > start) {
            auxin_overflow_count ++;
            if(!overflow && interval1 > 0 && auxin_overflow_count > interval1) {
                auxinConc = 0;
                for(auto c : cellAttr) {
                    Tissue::CellData& cD = cellAttr[c.first];
                    if(cD.type != Tissue::Source) {
                        cellsProdRates[cD.label] = cD.auxinProdRate;
                        cD.auxinProdRate = amount;
                        auxinConc += cD.auxin;
                    }
                }
                overflow = true;
                auxin_overflow_count = 0;
                auxinConc /= cellAttr.size();

            }
            if(overflow && interval2 > 0 && auxin_overflow_count > interval2) {
                for(auto c : cellAttr) {
                    Tissue::CellData& cD = cellAttr[c.first];
                    if (cD.type != Tissue::Source) {
                        cD.auxinProdRate = cellsProdRates[cD.label];
                        cD.auxin = auxinConc;
                    }
                }
                overflow = false;
                auxin_overflow_count = 0;

            }

        }

        int tip_removal_time = parm("TIP Removal Time").toDouble();
        tip_removal_count ++;
        if(!tip_removed && tip_removal_time > 0 && tip_removal_count > tip_removal_time) {
            std::set<int> labels;
            Point3d VIcm;
            int VIcount = 0;
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::VascularInitial) {
                    VIcm += cD.centroid;
                    VIcount++;
                }
            }
            VIcm /= VIcount;
            /*
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::QC || cD.centroid.y() < QCcm.y())
                    labels.insert(cD.label);
            }
            deleteProcess->step(labels);
            */
            for(CCIndex v : cs.vertices())
                if(indexAttr[v].pos[1] < VIcm.y())
                    indexAttr[v].selected = true;
            deleteSelectionProcess->run(cs, indexAttr);
            remeshProcess->step(true, false, false);
            tip_removed = true;
        }

        int LRC_removal_time = parm("LRC Removal Time").toDouble();
        LRC_removal_count ++;
        if(LRC_removal_time > 0 && LRC_removal_count > LRC_removal_time && !LRC_removed) {
            std::set<int> labels;
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::LRC)
                    labels.insert(cD.label);
            }
            deleteProcess->step(labels);
            LRC_removed = true;
        }

        int pin2_removal_time = parm("PIN2 Knockdown Time").toDouble();
        pin2_removal_count ++;
        if(pin2_removal_time > 0 && pin2_removal_count > pin2_removal_time) {
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::LRC || cD.type == Tissue::Cortex || cD.type == Tissue::Epidermis || cD.type == Tissue::EpLrcInitial)
                    cD.Pin1 = 1;

            }
        }

        int pin1_removal_time = parm("PIN1 Knockdown Time").toDouble();
        pin1_removal_count ++;
        if(!PIN1_knockdown && pin1_removal_time > 0 && pin1_removal_count > pin1_removal_time) {
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::Vascular || cD.type == Tissue::VascularInitial || cD.type == Tissue::Pericycle || cD.type == Tissue::Endodermis) {
                    cD.pinProdRate /= 10;
                    cD.pinInducedRate /= 10;
                }
                PIN1_knockdown = true;
            }
        }

        int aux1_removal_time = parm("AUX1 Knockdown Time").toDouble();
        aux1_removal_count ++;
        if(!AUX1_knockdown && aux1_removal_time > 0 && aux1_removal_count > aux1_removal_time) {
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type != Tissue::Source && cD.type != Tissue::Substrate ) {
                    cD.Aux1 = 0;
                    cD.aux1InducedRate = 0;
                    //cD.aux1ProdRate /= 10;
                    for(CCIndex e : cD.perimeterEdges) {
                        Tissue::EdgeData& eD = edgeAttr[e];
                        eD.Aux1[cD.label] /= 10;
                    }
                }
                AUX1_knockdown = true;
           }

        }

        int aux1_overexpr_time = parm("AUX1 Overexpression Time").toDouble();
        aux1_overexpr_count ++;
        if(!AUX1_overexpr && aux1_overexpr_time > 0 && aux1_overexpr_count > aux1_overexpr_time) {
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type != Tissue::Source && cD.type != Tissue::Substrate ) {
                    cD.aux1MaxEdge *= 2;
                    cD.aux1ProdRate = 100;
                }
            }
            AUX1_overexpr = true;
        }


        int strain_removal_time = parm("Strain Removal Time").toDouble();
        strain_removal_count ++;
        if(strain_removal_time > 0 && strain_removal_count > strain_removal_time) {
               mechanicalGrowthProcess->setParm("MF Degradation", "0.5");
        }

        int endodermal_cells_time = parm("Endodermal Cells Delete").toDouble();
        endodermal_cells_count ++;
        if(stepCount > 1200 && endodermal_cells_time > 0 && endodermal_cells_count > endodermal_cells_time) {
            endodermal_cells_count = 0;
            std::vector<int> endos ;
            for(auto c : cellAttr) {
                Tissue::CellData& cD = cellAttr[c.first];
                if(cD.type == Tissue::Endodermis)
                    endos.push_back(cD.label);
            }
            // choose a random endodermal cell to delete
            std::set<int> to_delete;
            std::random_shuffle(endos.begin(), endos.end());
            to_delete.insert(*endos.begin());
            deleteProcess->step(to_delete);
        }

        return false;
    }


};



class PrintCellAttr : public Process {
public:
    PrintCellAttr(const Process& process)
        : Process(process) {
        setName("Model/Root/21 Print Cell Attr");
        setDesc("Print the cell type.");
    }
    bool step();
};


class DumpSignalInfo : public Process {
public:
    DumpSignalInfo(const Process& process)
        : Process(process) {
        setName("Model/Root/26 Dump Signal Info");
        setDesc("Dump Signal Info.");
        addParm("Signal",
                "Signal",
                "Chems: Auxin By Area");
    }

    bool step();
};





class SetCellAttr : public Process {
public:
    SetCellAttr(const Process& process)
        : Process(process) {
        setName("Model/Root/22 Set Cell Attr");
        addParm("Cell Type",
                "Cell Type",
                "QC",
                QStringList()
                              << "Undefined"
                              << "QC"
                              << "Columella"
                              << "ColumellaInitial"
                              << "CEI"
                              << "CEID"
                              << "Cortex"
                              << "Endodermis"
                            << "VascularInitial"
                            << "Vascular"
                              << "Pericycle"
                              << "EpLrcInitial"
                              << "Epidermis"
                              << "LRC"
                              << "Columella"
                              << "Substrate"
                              << "Source");
        addParm("Vertices Masses", "Vertices Masses", "1");
        addParm("Auxin production rate", "Auxin production rate", "0");
        addParm("Division Algorithm", "Division Algorithm", "-1");
        addParm("Periclinal Division", "Periclinal Division","",
                QStringList() << ""
                              << "False"
                              << "True");
        addParm("MF reorientation rate", "MF reorientation rate", "-1");
        addParm("Microfibril 1", "Microfibril 1", "0,1,0");
        addParm("Microfibril 2", "Microfibril 2", "0,-1,0");

    }
    bool step();
};


class SetGlobalAttr : public Process {
public:
    SetGlobalAttr(const Process& process)
        : Process(process) {
        setName("Model/Root/23 Set Global Attr");
        addParm("Mechanics Process", "Name of Mechanics Process", "Model/Root/05 Mechanics");
        addParm("Mechanical Growth Process",
                "Name of the process for Mechanical Growth",
                "Model/Root/052 Mechanical Growth");
        addParm("Divide Process", "Name of Divide Process", "Model/Root/04 Divide Cells");
        addParm("Undefined Wall EK", "", "-1");
        addParm("Undefined Wall CK", "", "-1");
        addParm("Undefined Shear EK", "", "-1");
        addParm("Undefined Shear CK", "", "-1");
        addParm("Undefined Turgor Pressure", "", "-1");
        addParm("Undefined Growth Factor", "", "0");
        addParm("Undefined Max area", "", "10000");
        addParm("Undefined MF reorientation rate", "", "0");
        addParm("QC Wall EK", "", "1");
        addParm("QC Wall CK", "", "1");
        addParm("QC Shear EK", "", "1");
        addParm("QC Shear CK", "", "1");
        addParm("QC Turgor Pressure", "", "0");
        addParm("QC Growth Factor", "", "0");
        addParm("QC Max area", "", "1000");
        addParm("QC MF reorientation rate", "", "0");
        addParm("ColumellaInitial Wall EK", "", "-1");
        addParm("ColumellaInitial Wall CK", "", "-1");
        addParm("ColumellaInitial Shear EK", "", "-1");
        addParm("ColumellaInitial Shear CK", "", "-1");
        addParm("ColumellaInitial Growth Factor", "", "1");
        addParm("ColumellaInitial Turgor Pressure", "", "1");
        addParm("ColumellaInitial Max area", "", "100");
        addParm("ColumellaInitial MF reorientation rate", "", "0");
        addParm("Columella Wall EK", "", "-1");
        addParm("Columella Wall CK", "", "-1");
        addParm("Columella Shear EK", "", "-1");
        addParm("Columella Shear CK", "", "-1");
        addParm("Columella Growth Factor", "", "1");
        addParm("Columella Turgor Pressure", "", "1");
        addParm("Columella MF reorientation rate", "", "0");
        addParm("Columella Max area", "", "150");
        addParm("VascularInitial Wall EK", "", "-1");
        addParm("VascularInitial Wall CK", "", "-1");
        addParm("VascularInitial Shear EK", "", "-1");
        addParm("VascularInitial Shear CK", "", "-1");
        addParm("VascularInitial Turgor Pressure", "", "-1");
        addParm("VascularInitial Growth Factor", "", "5");
        addParm("VascularInitial MF reorientation rate", "", "-1");
        addParm("VascularInitial Max area", "", "70");
        addParm("Vascular Wall EK", "", "-1");
        addParm("Vascular Wall CK", "", "-1");
        addParm("Vascular Shear EK", "", "-1");
        addParm("Vascular Shear CK", "", "-1");
        addParm("Vascular Turgor Pressure", "", "-1");
        addParm("Vascular Growth Factor", "", "5");
        addParm("Vascular MF reorientation rate", "", "0");
        addParm("Vascular Max area", "", "100");
        addParm("Pericycle Wall EK", "", "-1");
        addParm("Pericycle Wall CK", "", "-1");
        addParm("Pericycle Shear EK", "", "-1");
        addParm("Pericycle Shear CK", "", "-1");
        addParm("Pericycle Turgor Pressure", "", "-1");
        addParm("Pericycle Growth Factor", "", "1");
        addParm("Pericycle MF reorientation rate", "", "-1");
        addParm("Pericycle Max area", "", "75");
        addParm("Cortex Wall EK", "", "-1");
        addParm("Cortex Wall CK", "", "-1");
        addParm("Cortex Shear EK", "", "-1");
        addParm("Cortex Shear CK", "", "-1");
        addParm("Cortex Turgor Pressure", "", "-1");
        addParm("Cortex Growth Factor", "", "1");
        addParm("Cortex Max area", "", "75");
        addParm("Cortex MF reorientation rate", "", "-1");
        addParm("Endodermis Wall EK", "", "-1");
        addParm("Endodermis Wall CK", "", "-1");
        addParm("Endodermis Shear EK", "", "-1");
        addParm("Endodermis Shear CK", "", "-1");
        addParm("Endodermis Turgor Pressure", "", "-1");
        addParm("Endodermis Growth Factor", "", "1");
        addParm("Endodermis Max area", "", "50");
        addParm("Endodermis MF reorientation rate", "", "-1");
        addParm("Epidermis Wall EK", "", "-1");
        addParm("Epidermis Wall CK", "", "-1");
        addParm("Epidermis Shear EK", "", "-1");
        addParm("Epidermis Shear CK", "", "-1");
        addParm("Epidermis Turgor Pressure", "", "-1");
        addParm("Epidermis Growth Factor", "", "1");
        addParm("Epidermis Max area", "", "75");
        addParm("Epidermis MF reorientation rate", "", "-1");
        addParm("CEI Wall EK", "", "-1");
        addParm("CEI Wall CK", "", "-1");
        addParm("CEI Shear EK", "", "-1");
        addParm("CEI Shear CK", "", "-1");
        addParm("CEI Turgor Pressure", "", "-1");
        addParm("CEI Growth Factor", "", "1");
        addParm("CEI Max area", "", "150");
        addParm("CEI MF reorientation rate", "", "-1");
        addParm("CEID Wall EK", "", "-1");
        addParm("CEID Wall CK", "", "-1");
        addParm("CEID Shear EK", "", "-1");
        addParm("CEID Shear CK", "", "-1");
        addParm("CEID Turgor Pressure", "", "-1");
        addParm("CEID Growth Factor", "", "1");
        addParm("CEID Max area", "", "100");
        addParm("CEID MF reorientation rate", "", "-1");
        addParm("EpLrcInitial Wall EK", "", "-1");
        addParm("EpLrcInitial Wall CK", "", "-1");
        addParm("EpLrcInitial Shear EK", "", "-1");
        addParm("EpLrcInitial Shear CK", "", "-1");
        addParm("EpLrcInitial Turgor Pressure", "", "-1");
        addParm("EpLrcInitial Growth Factor", "", "1");
        addParm("EpLrcInitial Max area", "", "50");
        addParm("EpLrcInitial MF reorientation rate", "", "-1");
        addParm("LRC Wall EK", "", "-1");
        addParm("LRC Wall CK", "", "-1");
        addParm("LRC Shear EK", "", "-1");
        addParm("LRC Shear CK", "", "-1");
        addParm("LRC Turgor Pressure", "", "-1");
        addParm("LRC Growth Factor", "", "1");
        addParm("LRC Max area", "", "75");
        addParm("LRC MF reorientation rate", "", "-1");
        addParm("Substrate Wall EK", "", "-1");
        addParm("Substrate Wall CK", "", "-1");
        addParm("Substrate Shear EK", "", "-1");
        addParm("Substrate Shear CK", "", "-1");
        addParm("Substrate Turgor Pressure", "", "-1");
        addParm("Substrate Growth Factor", "", "0");
        addParm("Substrate MF reorientation rate", "", "0");
        addParm("Substrate Max area", "", "1000");
        addParm("Source Wall EK", "", "1");
        addParm("Source Wall CK", "", "1");
        addParm("Source Shear EK", "", "1");
        addParm("Source Shear CK", "", "1");
        addParm("Source Turgor Pressure", "", "-1");
        addParm("Source Growth Factor", "", "0");
        addParm("Source MF reorientation rate", "", "0");
        addParm("Source Max area", "", "1000");

    }
    bool initialize(QWidget* parent);
    bool step();
    Mechanics* mechanicsProcess = 0;
    MechanicalGrowth * mechanicalGrowthProcess = 0;
    RootDivide*  divideProcess = 0;
};

class PrintVertexAttr : public Process {
public:
    PrintVertexAttr(const Process& process)
        : Process(process) {
        setName("Model/Root/24 PrintVertexAttr");
        setDesc("PrintVertexAttr.");

    }
    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        CCIndexDataAttr& indexAttr = mesh->indexAttr();

        Tissue::VertexDataAttr& vMAttr =
            mesh->attributes().attrMap<CCIndex, Tissue::VertexData>("VertexData");
        CCStructure& cs = mesh->ccStructure(mesh->ccName());
        for(CCIndex v : cs.vertices())
            if(indexAttr[v].selected) {
                mdxInfo << "Vertex: " << v << ", " << indexAttr[v].pos << " - ";
                if(((Tissue::CellData*)vMAttr[v].dualCell != 0))
                    mdxInfo << ((Tissue::CellData*)vMAttr[v].dualCell)->label << endl;
                else
                    mdxInfo << "No dual cell" << endl;
            }
        return false;
    }
};


class HighlightCell : public Process {
public:
    HighlightCell(const Process& process)
        : Process(process) {
        setName("Model/Root/25 Highlight Cell");
        setDesc("HighlightCell.");
        addParm("Face Label", "", "0");

    }
    bool step();
};


class AddFace : public Process {
public:
    AddFace(const Process& process)
        : Process(process) {
        setName("Model/Root/Add Face");
        addParm("Orientation",
                "Orientation",
                "Counter clock-wise",
                QStringList() << "Counter clock-wise"
                              << "Clock-wise");
        addParm("Label", "Label", "0");
    }



    void addFace(CCStructure &cs, CCIndexDataAttr& indexAttr, std::set<CCIndex> vs, int label, int orientation = 0) {

        // the selected orientation
        if(orientation == 0){
            if(parm("Orientation") == "Counter clock-wise")
                orientation = -1;
            else if(parm("Orientation") == "Clock-wise")
                orientation = 1;
            else
                throw(QString("Root::AddFace bad orientation"));
        }
        if(orientation != -1 && orientation != 1)
            throw(QString("Root::AddFace bad orientation"));


        if(vs.size() < 3)
            throw(QString("Root::AddFace needs at least 3 vertices to make a face"));

        // obtain the edges connecting the selected vertices
        std::vector<std::pair<Point3d, Point3d>> polygonSegs;
        for(CCIndex v1 : vs)
            for(CCIndex v2 : vs)
                if(!edgeBetween(cs, v1, v2).isPseudocell())
                    if(std::find(polygonSegs.begin(), polygonSegs.end(), std::make_pair(indexAttr[v1].pos, indexAttr[v2].pos)) == polygonSegs.end() &&
                       std::find(polygonSegs.begin(), polygonSegs.end(), std::make_pair(indexAttr[v2].pos, indexAttr[v1].pos)) == polygonSegs.end())
                            polygonSegs.push_back(std::make_pair(indexAttr[v1].pos, indexAttr[v2].pos));

        // get the centroid
        Point3d centroid;
        int num_v = 0;
        for(CCIndex v : vs) {
            centroid += indexAttr[v].pos;
            num_v++;
        }
        centroid /= num_v;

        // sort the edges and get the sum of the normals, reverse the vertices according to user selection
        // (the polygon can be concave, so we get a majority vote or normals among the edges)
        std::vector<Point3d> ps_ordered = orderPolygonSegs(polygonSegs);
        int nrml_count = 0;
        for(uint i = 1; i < ps_ordered.size(); i++) {
            Point3d p1 = ps_ordered[i-1];
            Point3d p2 = ps_ordered[i];
            Point3d nrml = (p2 - centroid).cross(p1 - centroid);
            nrml_count += (nrml.z() > 0) ? 1 : -1;
        }
        if((nrml_count > 0 && orientation == -1) ||
           (nrml_count < 0 && orientation == 1))
            std::reverse(std::begin(ps_ordered), std::end(ps_ordered));

        // retrieve the original vertices now sorted
        std::vector<CCIndex> vs_final;
        for(Point3d p : ps_ordered)
            for(CCIndex v : vs)
                if(norm(indexAttr[v].pos - p) < EPS)
                    vs_final.push_back(v);

        // add final face
        CCIndex ft = CCIndexFactory.getIndex();
        mdx::addFace(cs, ft, vs_final);
        indexAttr[ft].label = label;

    }


    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::AddFace No current mesh"));

        QString ccName = mesh->ccName();
        if(ccName.isEmpty())
            throw(QString("Root::AddFace Error, no cell complex selected"));

        CCStructure& cs = mesh->ccStructure(ccName);
        CCIndexDataAttr& indexAttr = mesh->indexAttr();


        // find the selected vertices
        std::set<CCIndex> vs;
        for(CCIndex v : cs.vertices())
            if(indexAttr[v].selected)
                vs.insert(v);

        // the selected orientation
        int orientation = 0;
        if(parm("Orientation") == "Counter clock-wise")
            orientation = -1;
        else if(parm("Orientation") == "Clock-wise")
            orientation = 1;
        else
            throw(QString("Root::AddFace bad orientation"));

        this->addFace(cs, indexAttr, vs, parm("Label").toInt(), orientation);
        mesh->updateAll();

        return false;
    }
};


class DeleteEdges : public Process {
public:
    DeleteEdges(const Process& process)
        : Process(process) {
        setName("Model/Root/Delete Edges");

    }

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::AddFace No current mesh"));

        QString ccName = mesh->ccName();
        if(ccName.isEmpty())
            throw(QString("Root::AddFace Error, no cell complex selected"));

        CCStructure& cs = mesh->ccStructure(ccName);
        CCIndexDataAttr& indexAttr = mesh->indexAttr();


        // find the selected vertices
        std::set<CCIndex> vs;
        for(CCIndex v : cs.vertices())
            if(indexAttr[v].selected)
                vs.insert(v);

        for(CCIndex v : vs)
            for(CCIndex vn : cs.neighbors(v))
                if(vs.find(vn) != vs.end()) {
                    CCSignedIndex oe = cs.orientedEdge(v, vn);
                    if(!(~oe).isPseudocell()) {
                        cs.deleteCell(~oe);
                        mdxInfo << "Deleted: " << oe << endl;
                    }
                }
        mesh->updateAll();

        return false;
    }
};



class CreateEdge : public Process {
public:
    CreateEdge(const Process& process)
        : Process(process) {
        setName("Model/Root/Create Edge");

    }

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::AddFace No current mesh"));

        QString ccName = mesh->ccName();
        if(ccName.isEmpty())
            throw(QString("Root::AddFace Error, no cell complex selected"));

        CCStructure& cs = mesh->ccStructure(ccName);
        CCIndexDataAttr& indexAttr = mesh->indexAttr();


        // find the selected vertices
        std::vector<CCIndex> vs;
        for(CCIndex v : cs.vertices())
            if(indexAttr[v].selected)
                vs.push_back(v);

        if(vs.size() != 2)
            throw(QString("Too many edges selected"));

        CCIndex e = CCIndexFactory.getIndex();

        cs.addCell(e, +vs[0] -vs[1]);

        mesh->updateAll();

        return false;
    }
};




class ReverseCell : public Process {
public:
    ReverseCell(const Process& process)
        : Process(process) {
        setName("Model/Root/Reverse Cell");

    }

    bool step() {
        Mesh* mesh = getMesh("Mesh 1");
        if(!mesh or mesh->file().isEmpty())
            throw(QString("Root::AddFace No current mesh"));

        QString ccName = mesh->ccName();
        if(ccName.isEmpty())
            throw(QString("Root::AddFace Error, no cell complex selected"));

        CCStructure& cs = mesh->ccStructure(ccName);
        CCIndexDataAttr& indexAttr = mesh->indexAttr();

        for(CCIndex f : cs.faces())
            if(indexAttr[f].selected)
                cs.reverseOrientation(f);

        mesh->updateAll();

        return false;
    }
};




/*
class ReadRootVerticesFromFile : public Process {
public:
    ReadRootVerticesFromFile(const Process &process) : Process(process) {
        setName("Model/Root/Read Root Vertices From File");
        addParm("File", "File", "");
        addParm("Scale", "Scale", "1");
    }
    bool step() {
        Mesh *mesh = getMesh("Mesh 1");
        if (!mesh or mesh->file().isEmpty())
            throw(QString("Root::ReadRootVerticesFromFile No current mesh"));

        CCStructure &cs = mesh->ccStructure("Root");
        CCIndexDataAttr &indexAttr = mesh->indexAttr();

        std::ifstream file(parm("File").toStdString());
        double scale = parm("Scale").toDouble();
        std::string   line;

        while(std::getline(file, line))
        {
            double x = std::stod(line.substr(0, line.find(',')));
            double y = std::stod(line.substr(line.find(',')+1, line.size()));
            CCIndex v = CCIndexFactory.getIndex();
            cs.addCell(v);
            indexAttr[v].pos = Point3d(x/scale, y/scale, 0);
        }
        return false;
    }
};*/

} // namespace ROOT


#endif // ROOT_HPP
back to top