https://github.com/PDLABCBGP/ROOTMODEL-PBD
Tip revision: 3251ec9fb61c1d726b2960195e15f74fe2dd9249 authored by PDLAB on 27 October 2021, 16:12:49 UTC
Merge pull request #2 from PDLABCBGP/add-license-2
Merge pull request #2 from PDLABCBGP/add-license-2
Tip revision: 3251ec9
tissue.hpp
#ifndef TISSUE_HPP
#define TISSUE_HPP
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_linalg.h>
#include <Process.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>
using namespace mdx;
const double EPS = 1e-6;
const double BIG_VAL = 100000;
// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
const std::string currentDateTime() ;
void SVDDecompX(const Matrix3d &M, Matrix3d &U, Matrix3d &S, Matrix3d &V);
void PolarDecompX(Matrix3d &M,Matrix3d &S, Matrix3d &U);
Point3d Rotate(Point3d v, double angle);
bool PointInPolygon(Point3d point, std::vector<Point3d> points) ;
Point3d shortenLength(Point3d A, float reductionLength) ;
float DistancePtLine(Point3d a, Point3d b, Point3d p) ;
Point3d findClosestLineToLine(Point3d targetLine,
Point3d line1, Point3d line2) ;
std::vector<double> softmax(std::vector<double> v) ;
void MBR(std::vector<Point3d> points, std::vector<Point3d>& rect) ;
// extended version of the official one
void neighborhood2D(Mesh& mesh,
const CCStructure& cs,
const CCIndexDataAttr& indexAttr,
std::map<IntIntPair, double>& wallAreas,
std::map<IntIntPair, std::set<CCIndex>>& wallEdges);
class Tissue : public Process, public virtual TissueParms {
public:
Tissue(const Process& process)
: Process(process) {
setName("Model/Root/03 Cell Tissue");
addParm("Set Global Attr Process",
"Name of Set Global Attributes Process",
"Model/Root/23 Set Global Attr");
setIcon(QIcon(":/images/CellDivide.png"));
addParm("Draw Velocity Rescale", "Draw Velocity Rescale", "0.0");
addParm("Draw Strain Tensors Rescale", "Draw Strain Tensors Rescale", "0");
addParm("Draw MF",
"Draw MF",
"True",
QStringList() << "False"
<< "True");
addParm("Draw Bounding Box",
"Draw Bounding Box",
"False",
QStringList() << "False"
<< "True");
addParm("Draw Auxin-Flux Rescale",
"Draw Auxin-Flux Rescale",
"1");
addParm("Draw PGD Rescale",
"Draw PGD Rescale",
"1");
addParm("Draw PINs",
"Draw PINs",
"0.1");
addParm("Verbose", "Verbose", "False",
QStringList() << "False"
<< "True");
}
struct CellData;
struct FaceData;
struct EdgeData;
struct VertexData;
typedef AttrMap<int, CellData> CellDataAttr;
typedef AttrMap<CCIndex, FaceData> FaceDataAttr;
typedef AttrMap<CCIndex, EdgeData> EdgeDataAttr;
typedef AttrMap<CCIndex, VertexData> VertexDataAttr;
struct Data {
int dim = -1; // for the chemical solver
};
enum VertexType { NoVertexType, Inside, Border, Visual };
static const char* ToString(VertexType v) {
switch(v) {
case NoVertexType:
return "NoVertexType";
case Inside:
return "Inside";
case Border:
return "Border";
case Visual:
return "Visual";
default:
throw(QString("Bad vertex type %1").arg(v));
}
}
// Vertex Data
struct VertexData : Data {
// attributes worth saving
VertexType type = NoVertexType;
double invmass = -1;
Point3d restPos, prevPos, lastPos;
Point3d velocity, prevVelocity;
Data* dualCell = 0; // in case this is a vertex of the dual graph
bool divisionPoint = false;
std::map<std::pair<CCIndex, CCIndex>, double> angle;
// other dynamically loaded attributes
Point3u dirichlet = Point3u(0, 0, 0); // fixed Dirichlet boundary in x, y, z
int clusters = 0;
std::map<const char*, Point3d> corrections;
Point3d dampedVelocity;
bool substrate = false, source = false;
Point3d force;
// just for debug, these variable have no effect
std::vector<std::tuple<int, const char*, Point3d>> forces;
CCIndex V_v0, V_v1, V_e;
VertexData() {
dim = 0;
}
void resetChems() {}
void resetMechanics() {
restPos = prevPos = lastPos; // ?
}
void restore(CCIndex v, const CCStructure& cs,
const CCIndexDataAttr &indexAttr,const CellDataAttr &cellAttr,
const EdgeDataAttr &edgeAttr) {
type = Tissue::Inside;
for(CCIndex e : cs.incidentCells(v, 1))
if(edgeAttr[e].type == Wall)
type = Border;
substrate = false, source = false;
std::set<int> labels;
for(CCIndex f : cs.incidentCells(v, 2)) {
labels.insert(indexAttr[f].label);
if(cellAttr[indexAttr[f].label].type==Substrate)
substrate = true;
if(cellAttr[indexAttr[f].label].type==Source)
source = true;
}
clusters = labels.size();
if(norm(restPos) == 0)
restPos = indexAttr[v].pos;
}
// careful when you change these
bool read(const QByteArray& ba, size_t& pos) {
readPOD<VertexType>(type, ba, pos);
readPOD<Data*>(dualCell, ba, pos);
readPOD<double>(invmass, ba, pos);
readPOD<Point3d>(restPos, ba, pos);
readPOD<Point3d>(prevPos, ba, pos);
readPOD<Point3d>(lastPos, ba, pos);
readPOD<Point3d>(velocity, ba, pos);
readPOD<Point3d>(prevVelocity, ba, pos);
readPOD<bool>(divisionPoint, ba, pos);
return true;
}
// attribute must be write and read in the same order
bool write(QByteArray& ba) {
writePOD<VertexType>(type, ba);
writePOD<Data*>(dualCell, ba);
writePOD<double>(invmass, ba);
writePOD<Point3d>(restPos, ba);
writePOD<Point3d>(prevPos, ba);
writePOD<Point3d>(lastPos, ba);
writePOD<Point3d>(velocity, ba);
writePOD<Point3d>(prevVelocity, ba);
writePOD<bool>(divisionPoint, ba);
return true;
}
bool operator==(const VertexData& other) const {
if(dualCell == other.dualCell)
return true;
return false;
}
};
// Cell Data
enum CellType {
Undefined,
QC,
Columella,
ColumellaInitial,
CEI,
CEID,
Cortex,
Endodermis,
Pericycle,
Vascular,
VascularInitial,
EpLrcInitial,
Epidermis,
LRC,
Substrate,
Source
};
static CellType stringToCellType(const QString& str) {
if(str == "Undefined")
return (Undefined);
else if(str == "QC")
return (QC);
else if(str == "Columella")
return (Columella);
else if(str == "ColumellaInitial")
return (ColumellaInitial);
else if(str == "CEI")
return (CEI);
else if(str == "CEID")
return (CEID);
else if(str == "Cortex")
return (Cortex);
else if(str == "Endodermis")
return (Endodermis);
else if(str == "Vascular")
return (Vascular);
else if(str == "VascularInitial")
return (VascularInitial);
else if(str == "Pericycle")
return (Pericycle);
else if(str == "EpLrcInitial")
return (EpLrcInitial);
else if(str == "Epidermis")
return (Epidermis);
else if(str == "LRC")
return (LRC);
else if(str == "Substrate")
return (Substrate);
else if(str == "Source")
return (Source);
else
throw(QString("Bad cell type %1").arg(str));
}
static const char* ToString(CellType v) {
switch(v) {
case Undefined:
return "Undefined";
case QC:
return "QC";
case Columella:
return "Columella";
case ColumellaInitial:
return "ColumellaInitial";
case CEI:
return "CEI";
case CEID:
return "CEID";
case Cortex:
return "Cortex";
case Endodermis:
return "Endodermis";
case VascularInitial:
return "VascularInitial";
case Vascular:
return "Vascular";
case Pericycle:
return "Pericycle";
case EpLrcInitial:
return "EpLrcInitial";
case Epidermis:
return "Epidermis";
case LRC:
return "LRC";
case Substrate:
return "Substrate";
case Source:
return "Source";
default:
throw(QString("Bad cell type %1").arg(v));
}
}
struct CellData : Data {
// attributes worth saving
Tissue *tissue = 0;
int label = -1;
CellType type = Undefined;
int lineage = 0;
CCIndex dualVertex;
std::set<CCIndex>* cellFaces = 0;
std::vector<CCIndex> cellVertices;
std::vector<CCIndex> perimeterFaces, perimeterVertices, perimeterEdges;
double area = 0, prevArea = 0, restArea = 0;
double invmassVertices = 1;
Point3d a1 = Point3d(0, EPS, 0), a2 = Point3d(EPS, 0, 0);
double mfRORate = -1;
double growthFactor = 0;
double lastDivision = 0;
double divisionCount = 0;
Point3d axisMin, axisMax, prev_axisMin, prev_axisMax;
bool periclinalDivision = false;
int divAlg = -1;
bool shapeInit = false;
double cellMaxArea = 1000;
Point3d restCm;
Matrix3d invRestMat;
std::vector<Point3d> restX0;
double auxin = 0, prevAuxin = 0;
double Pin1 = 0;
double Aux1 = 0;
double PINOID = 0;
double PP2A = 0;
double divInhibitor = 0, divPromoter = 0;
double lifeTime = 0;
double pressure = 1, pressureMax = -1;
double auxinProdRate = -1;
double auxinDecayRate = 0;
double pinProdRate = -1;
double pinInducedRate = -1;
double aux1ProdRate = -1;
double aux1InducedRate = -1;
double aux1MaxEdge = -1;
// other dinamically loaded attribute
bool selected = false;
std::pair<int, int> daughters;
Point3d divVector = Point3d(1., 0., 0.);
bool mfDelete = false;
double perimeter = 0;
double wallStress = -1;
std::vector<Point3d> box;
Point3d centroid;
std::map<CCIndex, double> perimeterAngles;
double growthRate = 0, axisMin_growthRate = 0, axisMax_growthRate = 0;
std::vector<double> growthRates, axisMin_grs, axisMax_grs;
Matrix3d G, E, U, M, M0, S, F = Matrix3d().identity(), R = Matrix3d().identity();
double gMax = 0, gMin = 0, gAngle = 0, sMax = 0, sMin = 0, sAngle = 0;
bool divisionAllowed = true;
double divProb = 0;
std::map<int, Point3d> auxinFluxes;
Point3d auxinFluxVector;
Point3d auxinGradientVector;
// visuals
CCIndex a1_v1, a1_v2, a1_e, a2_v1, a2_v2, a2_e,
auxinFlux_v1, auxinFlux_v2, auxinFlux_v3, auxinFlux_v4, auxinFlux_e, auxinFlux_el, auxinFlux_er, auxinFlux_eb, auxinFlux_f;
CCIndex axisMin_v, axisMax_v, axisCenter_v, axisMin_e, axisMax_e;
CCIndex PDGmax_e, PDGmax_v1, PDGmax_v2, PDGmin_e, PDGmin_v1, PDGmin_v2;
CCIndex box_v[4], box_e[4];
CellData()
: cellFaces(new std::set<CCIndex>()) {
dim = 5;
box.reserve(4);
if(type == CEID)
periclinalDivision = true;
}
~CellData() {
}
// BE CAREFUL, every time you change these two functions, you will have to manually reset the attibutes
bool read(const QByteArray& ba, size_t& pos) {
readPOD<Tissue*>(tissue, ba, pos);
readPOD<int>(label, ba, pos);
readPOD<CellType>(type, ba, pos);
readPOD<CCIndex>(dualVertex, ba, pos);
readPOD<double>(area, ba, pos);
readPOD<double>(prevArea, ba, pos);
readPOD<double>(restArea, ba, pos);
readPOD<double>(invmassVertices, ba, pos);
readPOD<Point3d>(a1, ba, pos);
readPOD<Point3d>(a2, ba, pos);
readPOD<double>(mfRORate, ba, pos);
readPOD<double>(auxin, ba, pos);
readPOD<double>(Aux1, ba, pos);
readPOD<double>(Pin1, ba, pos);
readPOD<double>(auxinProdRate, ba, pos);
readPOD<double>(auxinDecayRate, ba, pos);
// cellFaces
std::vector<CCIndex> v;
size_t sz;
readPOD<size_t>(sz, ba, pos);
v.resize(sz);
readChar(v.data(), sz * sizeof(CCIndex), ba, pos);
cellFaces->clear();
for(CCIndex i : v)
cellFaces->insert(i);
readPOD<double>(lastDivision, ba, pos);
readPOD<bool>(periclinalDivision, ba, pos);
readPOD<Point3d>(divVector, ba, pos);
readPOD<double>(cellMaxArea, ba, pos);
readPOD<Point3d>(axisMax, ba, pos);
readPOD<Point3d>(axisMin, ba, pos);
// cellVertices
std::vector<CCIndex> cv;
size_t cv_sz;
readPOD<size_t>(cv_sz, ba, pos);
cv.resize(cv_sz);
readChar(cv.data(), cv_sz * sizeof(CCIndex), ba, pos);
cellVertices.clear();
for(CCIndex i : cv)
cellVertices.push_back(i);
// perimeterFaces
std::vector<CCIndex> pf;
size_t pf_sz;
readPOD<size_t>(pf_sz, ba, pos);
pf.resize(pf_sz);
readChar(pf.data(), pf_sz * sizeof(CCIndex), ba, pos);
perimeterFaces.clear();
for(CCIndex i : pf)
perimeterFaces.push_back(i);
// perimeterEdges
std::vector<CCIndex> pe;
size_t pe_sz;
readPOD<size_t>(pe_sz, ba, pos);
pe.resize(pe_sz);
readChar(pe.data(), pe_sz * sizeof(CCIndex), ba, pos);
perimeterEdges.clear();
for(CCIndex i : pe)
perimeterEdges.push_back(i);
// perimeterVertices
std::vector<CCIndex> pv;
size_t pv_sz;
readPOD<size_t>(pv_sz, ba, pos);
pv.resize(pv_sz);
readChar(pv.data(), pv_sz * sizeof(CCIndex), ba, pos);
perimeterVertices.clear();
for(CCIndex i : pv)
perimeterVertices.push_back(i);
// invRestMatrix
readPOD<Point3d>(invRestMat[0], ba, pos);
readPOD<Point3d>(invRestMat[1], ba, pos);
readPOD<Point3d>(invRestMat[2], ba, pos);
// restX0
std::vector<Point3d> rX0;
size_t rX0_sz;
readPOD<size_t>(rX0_sz, ba, pos);
rX0.resize(rX0_sz);
readChar(rX0.data(), rX0_sz * sizeof(Point3d), ba, pos);
restX0.clear();
for(Point3d i : rX0)
restX0.push_back(i);
readPOD<Point3d>(restCm, ba, pos);
//
readPOD<double>(lifeTime, ba, pos);
readPOD<double>(pressure, ba, pos);
readPOD<double>(pressureMax, ba, pos);
readPOD<bool>(shapeInit, ba, pos);
readPOD<int>(divAlg, ba, pos);
return true;
}
// attribute must be write and read in the same order
bool write(QByteArray& ba) {
writePOD<Tissue*>(tissue, ba);
writePOD<int>(label, ba);
writePOD<CellType>(type, ba);
writePOD<CCIndex>(dualVertex, ba);
writePOD<double>(area, ba);
writePOD<double>(prevArea, ba);
writePOD<double>(restArea, ba);
writePOD<double>(invmassVertices, ba);
writePOD<Point3d>(a1, ba);
writePOD<Point3d>(a2, ba);
writePOD<double>(mfRORate, ba);
writePOD<double>(auxin, ba);
writePOD<double>(Aux1, ba);
writePOD<double>(Pin1, ba);
writePOD<double>(auxinProdRate, ba);
writePOD<double>(auxinDecayRate, ba);
// cellFaces
std::vector<CCIndex> v(cellFaces->begin(), cellFaces->end());
size_t sz = v.size();
writePOD<size_t>(sz, ba);
writeChar(v.data(), sz * sizeof(CCIndex), ba);
writePOD<double>(lastDivision, ba);
writePOD<bool>(periclinalDivision, ba);
writePOD<Point3d>(divVector, ba);
writePOD<double>(cellMaxArea, ba);
writePOD<Point3d>(axisMax, ba);
writePOD<Point3d>(axisMin, ba);
// cellVertices
std::vector<CCIndex> cv(cellVertices.begin(), cellVertices.end());
size_t cv_sz = cv.size();
writePOD<size_t>(cv_sz, ba);
writeChar(cv.data(), cv_sz * sizeof(CCIndex), ba);
// perimeterFaces
std::vector<CCIndex> pf(perimeterFaces.begin(), perimeterFaces.end());
size_t pf_sz = pf.size();
writePOD<size_t>(pf_sz, ba);
writeChar(pf.data(), pf_sz * sizeof(CCIndex), ba);
// perimeterEdges
std::vector<CCIndex> pe(perimeterEdges.begin(), perimeterEdges.end());
size_t pe_sz = pe.size();
writePOD<size_t>(pe_sz, ba);
writeChar(pe.data(), pe_sz * sizeof(CCIndex), ba);
// perimeterVertices
std::vector<CCIndex> pv(perimeterVertices.begin(), perimeterVertices.end());
size_t pv_sz = pv.size();
writePOD<size_t>(pv_sz, ba);
writeChar(pv.data(), pv_sz * sizeof(CCIndex), ba);
// invRestMatrix
writePOD<Point3d>(invRestMat[0], ba);
writePOD<Point3d>(invRestMat[1], ba);
writePOD<Point3d>(invRestMat[2], ba);
// restX0
std::vector<Point3d> rx0(restX0.begin(), restX0.end());
size_t rx0_sz = rx0.size();
writePOD<size_t>(rx0_sz, ba);
writeChar(rx0.data(), rx0_sz * sizeof(Point3d), ba);
writePOD<Point3d>(restCm, ba);
//
writePOD<double>(lifeTime, ba);
writePOD<double>(pressure, ba);
writePOD<double>(pressureMax, ba);
writePOD<bool>(shapeInit, ba);
writePOD<int>(divAlg, ba);
return true;
}
bool operator==(const CellData& other) const {
if(area == other.area)
return true;
return false;
}
// reset chemicals
void resetChems() {
auxin = 0;
Pin1 = 0;
Aux1 = 0;
PP2A = 0;
PINOID = 0;
divPromoter = 0;
divInhibitor = 0;
auxinFluxes.clear();
auxinFluxVector = Point3d(0, 0, 0);
}
void resetMechanics(FaceDataAttr& faceAttr) {
a1 = Point3d(1, 1, 0) * EPS, a2 = Point3d(1, 1, 0) * EPS;
restArea = area;
restCm = centroid;
pressureMax = -1;
shapeInit = false;
invmassVertices = 1;
mfRORate = -1;
wallStress = -1;
for(CCIndex f : *cellFaces){
FaceData &fD = faceAttr[f];
fD.resetMechanics();
}
growthRates.clear();
}
void updateGeom(const CCIndexDataAttr& indexAttr, FaceDataAttr& faceAttr, EdgeDataAttr& edgeAttr, double Dt) {
prevArea = area;
area = 0;
centroid = Point3d(0, 0, 0);
// deformation gradient and green strain tensor
G = 0, E = 0, F = 0;
int i = 0;
for(CCIndex f : *cellFaces) {
FaceData fD = faceAttr[f];
area += indexAttr[f].measure;
centroid += indexAttr[f].pos;
if(fD.type == Membrane) {
E += fD.E;
G += fD.G;
F += fD.F;
i++;
}
}
centroid /= cellFaces->size();
E /= i;
G /= i;
F /= i;
// perimeter length
perimeter = 0;
for(CCIndex e : perimeterEdges)
perimeter += edgeAttr[e].length;
// cell growth rate
if(Dt > 0)
growthRates.push_back((area - prevArea) / (prevArea * Dt));
if(growthRates.size() > 100)
growthRates.erase(growthRates.begin());
growthRate = 0;
int grs = 1;
for(double gr : growthRates)
if(gr > 0) {
growthRate += gr;
grs++;
}
growthRate /= grs;
// principal components of the green strain tensor
gAngle = gMax = gMin = 0;
if((G[0][0] - G[1][1]) != 0) {
gAngle = (0.5 * atan(2 * G[0][1] / (G[0][0] - G[1][1]) )) ;
double left = (G[0][0] + G[1][1]) / 2;
double right = sqrt(pow((G[0][0] - G[1][1]) / 2, 2) + pow(G[0][1]/2, 2));
gMax = left + right;
gMin = left - right;
}
if(G[0][0] < G[1][1])
if(gAngle > -(M_PI/4) && gAngle < (M_PI/4))
gAngle += (M_PI/2);
}
void restore(Mesh* mesh, Tissue* tissue,
const CCStructure& cs,
const CCStructure& csDual,
CCIndexDataAttr& indexAttr,
FaceDataAttr& faceAttr,
EdgeDataAttr& edgeAttr,
VertexDataAttr& vMAttr) {
this->tissue = tissue;
if(lineage == 0)
lineage = label;
// find my faces
uint old_cellFaces = cellFaces->size();
cellFaces->clear();
for(CCIndex f : cs.faces())
if(indexAttr[f].label == label) {
cellFaces->insert(f);
faceAttr[f].owner = this;
}
if(old_cellFaces != cellFaces->size())
shapeInit = false;
// update the geometry
updateGeom(indexAttr, faceAttr, edgeAttr, 0);
// find my dual vertex
if(csDual.vertices().size() > 0) {
//throw(QString("CellData::restore empty dual graph"));
CCIndex closest = csDual.vertices()[0];
for(CCIndex v : csDual.vertices())
if(norm(indexAttr[v].pos - centroid) < norm(indexAttr[closest].pos - centroid))
closest = v;
indexAttr[closest].label = label;
vMAttr[closest].dualCell = this;
dualVertex = closest;
}
// set perimeter edges and faces, and set average restLength for edges
cellVertices.clear();
perimeterFaces.clear();
perimeterEdges.clear();
std::vector<CCIndex> tmpEdges, tmpVertices;
perimeterVertices.clear();
for(CCIndex f : *cellFaces)
for(CCIndex e : cs.incidentCells(f, 1)) {
if(std::find(cellVertices.begin(), cellVertices.end(), cs.edgeBounds(e).first) == cellVertices.end())
cellVertices.push_back(cs.edgeBounds(e).first);
if(std::find(cellVertices.begin(), cellVertices.end(), cs.edgeBounds(e).second) == cellVertices.end())
cellVertices.push_back(cs.edgeBounds(e).second);
if(vMAttr[cs.edgeBounds(e).first].invmass == -1)
vMAttr[cs.edgeBounds(e).first].invmass = invmassVertices;
if(vMAttr[cs.edgeBounds(e).second].invmass == -1)
vMAttr[cs.edgeBounds(e).second].invmass = invmassVertices;
std::set<CCIndex> edgeFaces = cs.incidentCells(e, 2);
CCIndex f1 = *(edgeFaces.begin());
CCIndex f2;
if(edgeFaces.size() > 1)
f2 = *(++edgeFaces.begin());
if(mesh->getLabel(indexAttr[f1].label) == mesh->getLabel(indexAttr[f2].label))
continue;
if(std::find(perimeterFaces.begin(), perimeterFaces.end(), f) == perimeterFaces.end())
perimeterFaces.push_back(f);
if(std::find(tmpEdges.begin(), tmpEdges.end(), e) == tmpEdges.end())
tmpEdges.push_back(e);
if(std::find(tmpVertices.begin(), tmpVertices.end(), cs.edgeBounds(e).first) == tmpVertices.end())
tmpVertices.push_back(cs.edgeBounds(e).first);
if(std::find(tmpVertices.begin(), tmpVertices.end(), cs.edgeBounds(e).second) == tmpVertices.end())
tmpVertices.push_back(cs.edgeBounds(e).second);
}
// sort the perimeter edges and vertices
std::vector<std::pair<Point3d,Point3d> > polygonSegs;
std::vector<Point3d> polygonPoints;
for(CCIndex e : tmpEdges) {
auto eb = cs.edgeBounds(e);
polygonSegs.push_back(make_pair(indexAttr[eb.first].pos, indexAttr[eb.second].pos));
}
for(CCIndex v : tmpVertices)
polygonPoints.push_back(indexAttr[v].pos);
std::vector<std::pair<Point3d,Point3d> > polygonSegsOrig = polygonSegs;
std::vector<Point3d> polygonPointsOrdered = mdx::orderPolygonSegs(polygonSegs, true);
for(auto p : polygonSegs) {
auto it = std::find(polygonSegsOrig.begin(), polygonSegsOrig.end(), p);
if(it == polygonSegsOrig.end()) {
Point3d tmp = p.first;
p.first = p.second;
p.second = tmp;
it = std::find(polygonSegsOrig.begin(), polygonSegsOrig.end(), p);
}
perimeterEdges.push_back(tmpEdges[std::distance(polygonSegsOrig.begin(), std::find(polygonSegsOrig.begin(), polygonSegsOrig.end(), p))]);
}
for(auto p : polygonPointsOrdered)
perimeterVertices.push_back(tmpVertices[std::distance(polygonPoints.begin(), std::find(polygonPoints.begin(), polygonPoints.end(), p))]);
// set the perimeter vertices' angles
int n = perimeterVertices.size();
for(int i = 0; i < n; i++) {
CCIndex v = perimeterVertices[i];
if(perimeterAngles.find(v) == perimeterAngles.end()){
int prev = i-1;
if(prev == -1)
prev = n-1;
int next = i+1;
if(next == n)
next = 0;
Point3d p0 = indexAttr[perimeterVertices[prev]].pos - indexAttr[perimeterVertices[i]].pos;
Point3d p1 = indexAttr[perimeterVertices[next]].pos - indexAttr[perimeterVertices[i]].pos;
perimeterAngles[v] = mdx::angle(p0, p1);
}
CCIndex e1, e2;
for(CCIndex e : cs.incidentCells(v, 1))
if(find(perimeterEdges.begin(), perimeterEdges.end(), e) != perimeterEdges.end()){
if(e1.isPseudocell())
e1 = e;
else
e2 = e;
}
if(e1.isPseudocell() || e2.isPseudocell())
throw(QString("Something wrong here"));
int prev = i-1;
if(prev == -1) prev = n-1;
int next = i+1;
if(next == n) next = 0;
Point3d p0 = indexAttr[perimeterVertices[prev]].pos - indexAttr[perimeterVertices[i]].pos;
Point3d p1 = indexAttr[perimeterVertices[next]].pos - indexAttr[perimeterVertices[i]].pos;
if(vMAttr[v].angle[make_pair(e1, e2)] == 0)
vMAttr[v].angle[make_pair(e1, e2)] = mdx::angle(p0, p1);
}
}
void
update(const CCStructure& cs, const CCIndexDataAttr& indexAttr, FaceDataAttr& faceAttr, EdgeDataAttr &edgeAttr, VertexDataAttr& vMAttr, double Dt) {
// Geometry
updateGeom(indexAttr, faceAttr, edgeAttr, Dt);
// Misc updates
lastDivision += Dt;
if(perimeterVertices.size() > 0) {
// bounding box
std::vector<Point3d> points;
for(auto v : perimeterVertices)
points.push_back(indexAttr[v].pos);
MBR(points, box);
prev_axisMax = axisMax;
prev_axisMin = axisMin;
axisMax = box[0] - box[1];
axisMin = box[1] - box[2];
if(norm(axisMax) < norm(axisMin)) {
Point3d tmp = axisMin;
axisMin = axisMax;
axisMax = tmp;
}
// Axis growth rates, needed for PGD visualization
if(Dt > EPS) {
double axisMax_gr = (norm(axisMax) - norm(prev_axisMax)) / Dt;
double axisMin_gr = (norm(axisMin) - norm(prev_axisMin)) / Dt;
if(axisMax_gr > 0 && axisMin_gr > 0) {
axisMax_grs.insert(axisMax_grs.begin(), axisMax_gr);
axisMin_grs.insert(axisMin_grs.begin(), axisMin_gr);
}
uint max_values = 30;
if(axisMax_grs.size() > max_values || axisMin_grs.size() > max_values) {
axisMax_grs.resize(max_values);
axisMin_grs.resize(max_values);
}
axisMax_growthRate = 0, axisMin_growthRate = 0;
for(uint i = 0; i < axisMax_grs.size(); i++) {
axisMax_growthRate += axisMax_grs[i];
axisMin_growthRate += axisMin_grs[i] ;
}
if(axisMax_grs.size() > 0)
axisMax_growthRate /= axisMax_grs.size();
if(axisMin_grs.size() > 0)
axisMin_growthRate /= axisMin_grs.size();
}
/*
// rest shape tensor
M0 = 0;
for(auto v : perimeterVertices) {
M0[0][0] += pow(vMAttr[v].restPos[0] - restCm[0], 2);
M0[1][1] += pow(vMAttr[v].restPos[1] - restCm[1], 2);
M0[0][1] += (vMAttr[v].restPos[0] - restCm[0]) * (vMAttr[v].restPos[1] - restCm[1]);
M0[1][0] += (vMAttr[v].restPos[0] - restCm[0]) * (vMAttr[v].restPos[1] - restCm[1]);
}
M0 /= perimeterVertices.size();
// current shape tensor
M = 0;
for(auto v : perimeterVertices) {
M[0][0] += pow(indexAttr[v].pos[0] - centroid[0], 2);
M[1][1] += pow(indexAttr[v].pos[1] - centroid[1], 2);
M[0][1] += (indexAttr[v].pos[0] - centroid[0]) * (indexAttr[v].pos[1] - centroid[1]);
M[1][0] += (indexAttr[v].pos[0] - centroid[0]) * (indexAttr[v].pos[1] - centroid[1]);
}
M /= perimeterVertices.size();
// shape tensor strain
S = (M - M0);
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
S[i][j] = S[i][j] / transpose(M0)[i][j];
// principal components of the shape strain tensor
sAngle = sMax = sMin = 0;
if((S[0][0] - S[1][1]) != 0) {
sAngle = (0.5 * atan(2 * S[0][1] / (S[0][0] - S[1][1]) )) ;
double left = (S[0][0] + S[1][1]) / 2;
double right = sqrt(pow((S[0][0] - S[1][1]) / 2, 2) + pow(S[0][1]/2, 2));
sMax = left + right;
sMin = left - right;
}
if(S[0][0] < S[1][1])
if(sAngle > -(M_PI/4) && sAngle < (M_PI/4))
sAngle += (M_PI/2);
*/
/*if(axisMax[1] < 0)
axisMax *= -1;
if(axisMin[1] < 0)
axisMin *= -1;*/
} else
mdxInfo << "Cell " << label << " has no vertices!?!?" << endl;
}
void setType(CellType newType, VertexDataAttr& vMAttr) {
type = newType;
for(CCIndex v : cellVertices) {
vMAttr[v].substrate = newType == Substrate;
vMAttr[v].source = newType == Source;
}
}
void division(const CCStructure &cs, CellDataAttr& cellAttr,
FaceDataAttr& faceAttr, EdgeDataAttr &edgeAttr, CellData& cD1, CellData& cD2, std::map<CellType, int> maxAreas, bool ignoreCellType=false) ;
};
// Edge Data
enum EdgeType { NoEdgeType, Shear, Wall };
static const char* ToString(EdgeType v) {
switch(v) {
case Shear:
return "Shear";
case Wall:
return "Wall";
case NoEdgeType:
return "NoEdgeType";
default:
throw(QString("Bad edge type %1").arg(v));
}
}
struct EdgeData : Data {
// attributes worth saving
EdgeType type = NoEdgeType;
double restLength = 0;
double prevLength = 0, length = 0;
double prev_strain = 0, strain = 0;
double strainRate = 0;
double eStiffness = -1;
double cStiffness = -1;
double intercellularAuxin = 0;
std::map<int, double> Aux1;
std::map<int, double> Pin1;
std::map<int, double> PINOID;
std::map<int, double> PP2A;
// other dynamical attrs
std::map<int, double> auxinRatio;
std::map<int, double> auxinGrad;
Point3d midPoint;
std::map<int, double> angle;
std::map<CCIndex, Point3d> outwardNormal;
std::map<int, double> auxinFluxImpact, MFImpact, geomImpact;
std::map<int, double> pin1Sensitivity, pin1SensitivityRaw;
double sigmaEv = 0, sigmaEe = 0;
Point3d sigmaForce = Point3d(0, 0, 0);
std::vector<Point3d> sigmaForces;
Point3d pressureForce = Point3d(0, 0, 0);
CCIndex pin_v1_1, pin_v2_1, pin_v3_1, pin_v4_1, pin_e1_1, pin_e2_1, pin_e3_1, pin_e4_1, pin_f_1;
CCIndex pin_v1_2, pin_v2_2, pin_v3_2, pin_v4_2, pin_e1_2, pin_e2_2, pin_e3_2, pin_e4_2, pin_f_2;
EdgeData() {
dim = 1;
} // for the chemical solver
// reset chemicals
void resetChems() {
intercellularAuxin = 0;
Aux1.clear();
Pin1.clear();
PINOID.clear();
PP2A.clear();
auxinFluxImpact.clear();
pin1Sensitivity.clear();
}
void resetMechanics() {
restLength = length;
prevLength = length;
}
void restore(CCIndex e,
const CCStructure& cs,
const CCIndexDataAttr& indexAttr,
const CellDataAttr& cellAttr) {
auto eb = cs.edgeBounds(e);
length = norm(indexAttr[eb.first].pos - indexAttr[eb.second].pos);
if(restLength <= 0)
restLength = length;
// determine whether we are a wall or internal edge
std::vector<int> incident_labels;
for(CCIndex f : cs.incidentCells(e, 2)) {
int label = indexAttr[f].label;
incident_labels.push_back(label);
}
if((incident_labels.size() == 2 && incident_labels[0] != incident_labels[1]) ||
cs.onBorder(e))
this->type = Tissue::Wall;
else
this->type = Tissue::Shear;
}
// to be called at each step
void update(CCIndex e,
const CCStructure& cs,
const CCIndexDataAttr& indexAttr,
VertexDataAttr& vMAttr,
FaceDataAttr& faceAttr) {
auto eb = cs.edgeBounds(e);
Point3d vPos = indexAttr[eb.first].pos;
Point3d nPos = indexAttr[eb.second].pos;
prevLength = length;
length = norm(vPos - nPos);
prev_strain = strain;
strain = (length - restLength) / restLength;
Point3d versor = vPos - nPos;
Point3d dv = vMAttr[eb.first].velocity - vMAttr[eb.second].velocity;
strainRate = (versor * dv) / prevLength;
midPoint = (nPos + (vPos - nPos) / 2.);
outwardNormal.clear();
std::vector<int> incident_labels;
for(CCIndex f : cs.incidentCells(e, 2)) {
incident_labels.push_back(indexAttr[f].label);
Point3d versor = (midPoint - indexAttr[f].pos);
outwardNormal[f] = Point3d(0., 0., 1.) ^ (vPos - nPos);
outwardNormal[f] *= 1. / norm(outwardNormal[f]);
if(outwardNormal[f] * versor < 0.)
outwardNormal[f] *= -1.;
Point3d eVector = vPos - nPos;
// angle is relative to MF a1 orientation
/*
* 90º
* ^
* |
* |
* |
* |
* 0º <-----------o---------------> 180º
* |
* |
* |
* |
* v
* 90º
*/
FaceData& fD = faceAttr[f];
if(!fD.owner)
throw(QString("Tissue::EdgeData::update: orphan face, something wrong with tissue restore or cell division?"));
// angle[fD.owner->label] = fabs(atan2(eVector[1], eVector[0])) *
// 180./M_PI;
angle[indexAttr[f].label] =
acos((eVector * fD.a1) / (norm(eVector) * norm(fD.a1))) *
(180. / M_PI);
}
}
bool read(const QByteArray& ba, size_t& pos) {
readPOD<EdgeType>(type, ba, pos);
readPOD<double>(length, ba, pos);
readPOD<double>(prevLength, ba, pos);
readPOD<double>(prev_strain, ba, pos);
readPOD<double>(strain, ba, pos);
readPOD<double>(strainRate, ba, pos);
readPOD<double>(restLength, ba, pos);
readPOD<double>(intercellularAuxin, ba, pos);
readPOD<double>(eStiffness, ba, pos);
readPOD<double>(cStiffness, ba, pos);
// PIN
std::vector<int> k; std::vector<double>v;
size_t szk, szv;
readPOD<size_t>(szk, ba, pos);
readPOD<size_t>(szv, ba, pos);
k.resize(szk);
v.resize(szv);
readChar(k.data(), szk * sizeof(int), ba, pos);
readChar(v.data(), szv * sizeof(double), ba, pos);
Pin1.clear();
for(uint i = 0; i < k.size(); i++)
Pin1[k[i]] = v[i];
// AUX1
k.clear(); v.clear();
readPOD<size_t>(szk, ba, pos);
readPOD<size_t>(szv, ba, pos);
k.resize(szk);
v.resize(szv);
readChar(k.data(), szk * sizeof(int), ba, pos);
readChar(v.data(), szv * sizeof(double), ba, pos);
Aux1.clear();
for(uint i = 0; i < k.size(); i++)
Aux1[k[i]] = v[i];
// PP2A
k.clear(); v.clear();
readPOD<size_t>(szk, ba, pos);
readPOD<size_t>(szv, ba, pos);
k.resize(szk);
v.resize(szv);
readChar(k.data(), szk * sizeof(int), ba, pos);
readChar(v.data(), szv * sizeof(double), ba, pos);
PP2A.clear();
for(uint i = 0; i < k.size(); i++)
PP2A[k[i]] = v[i];
// PINOID
k.clear(); v.clear();
readPOD<size_t>(szk, ba, pos);
readPOD<size_t>(szv, ba, pos);
k.resize(szk);
v.resize(szv);
readChar(k.data(), szk * sizeof(int), ba, pos);
readChar(v.data(), szv * sizeof(double), ba, pos);
PINOID.clear();
for(uint i = 0; i < k.size(); i++)
PINOID[k[i]] = v[i];
return true;
}
bool write(QByteArray& ba) {
writePOD<EdgeType>(type, ba);
writePOD<double>(length, ba);
writePOD<double>(prevLength, ba);
writePOD<double>(prev_strain, ba);
writePOD<double>(strain, ba);
writePOD<double>(strainRate, ba);
writePOD<double>(restLength, ba);
writePOD<double>(intercellularAuxin, ba);
writePOD<double>(eStiffness, ba);
writePOD<double>(cStiffness, ba);
// PIN
std::vector<int> k; std::vector<double>v;
for( auto it = Pin1.begin(); it != Pin1.end(); ++it ) {
v.push_back( it->second );
k.push_back(it->first);
}
size_t szv = v.size();
size_t szk = k.size();
writePOD<size_t>(szk, ba);
writePOD<size_t>(szv, ba);
writeChar(k.data(), szk * sizeof(int), ba);
writeChar(v.data(), szv * sizeof(double), ba);
// AUX1
k.clear(); v.clear();
for( auto it = Aux1.begin(); it != Aux1.end(); ++it ) {
v.push_back( it->second );
k.push_back(it->first);
}
szv = v.size();
szk = k.size();
writePOD<size_t>(szk, ba);
writePOD<size_t>(szv, ba);
writeChar(k.data(), szk * sizeof(int), ba);
writeChar(v.data(), szv * sizeof(double), ba);
// PINOID
k.clear(); v.clear();
for( auto it = PINOID.begin(); it != PINOID.end(); ++it ) {
v.push_back( it->second );
k.push_back(it->first);
}
szv = v.size();
szk = k.size();
writePOD<size_t>(szk, ba);
writePOD<size_t>(szv, ba);
writeChar(k.data(), szk * sizeof(int), ba);
writeChar(v.data(), szv * sizeof(double), ba);
// PP2A
k.clear(); v.clear();
for( auto it = PP2A.begin(); it != PP2A.end(); ++it ) {
v.push_back( it->second );
k.push_back(it->first);
}
szv = v.size();
szk = k.size();
writePOD<size_t>(szk, ba);
writePOD<size_t>(szv, ba);
writeChar(k.data(), szk * sizeof(int), ba);
writeChar(v.data(), szv * sizeof(double), ba);
return true;
}
bool operator==(const EdgeData& other) const {
if(restLength == other.restLength and
cStiffness == other.cStiffness)
return true;
return false;
}
};
// FaceData
enum FaceType { NoFaceType, Internal, Membrane };
static const char* ToString (FaceType v) {
switch(v) {
case Internal:
return "Internal";
case Membrane:
return "Membrane";
case NoFaceType:
return "NoFaceType";
default:
throw(QString("Bad edge type %1").arg(v));
}
}
struct FaceData : Data {
// attributes worth saving
FaceType type = NoFaceType;
Point3d a1 = Point3d(0, 1, 0), a2 = Point3d(0, -1, 0);
double restAreaFace = 0;
Point3d restPos[3];
Matrix2d invRestMat;
CellData* owner = 0;
double area = 0;
double prevArea = 0;
// dynamical attributes
// just a copy of owner's, only relevant for visuals
double stress = 0;
double pressure = 0;
double edgeStrain = 0;
double edgeStiffness = 0;
double MFImpact = 0, auxinFluxImpact = 0, geomImpact = 0, auxinRatio = 0, auxinGrad = 0;
double pin1Sensitivity = 0, pin1SensitivityRaw = 0;
Matrix3d E, G, V, dV, F = Matrix3d().identity(), R = Matrix3d().identity(), U = Matrix3d().identity(), Ev, F1, F2, sigmaA;
Point3d divVector = Point3d(1., 0., 0.);
double auxin = 0, Aux1Cyt = 0, Pin1Cyt = 0, divInhibitorCyt = 0,
Aux1Mem = 0, Pin1Mem = 0, intercellularAuxin = 0, PINOIDMem = 0, PP2AMem = 0,
growthRate = 0;
CCIndex G_v0, G_v1, G_v2, G_e1, G_e2, a1_v1, a1_v2, a1_e, a2_v1, a2_v2, a2_e;
FaceData() {
dim = 2;
E[0] = Point3d(1., 0., 0.);
E[1] = Point3d(0., 1., 0.);
E[2] = Point3d(0., 0., 1.);
}
// reset chemicals, not really needed as faces just copy owner's
void resetChems() {}
void resetMechanics() {
a1 = Point3d(0, EPS, 0), a2 = Point3d(EPS, 0, 0);
restAreaFace = area;
for(Point3d p : restPos)
p = 0;
invRestMat = 0;
}
void restore(CCIndex f, const CCStructure& cs, const CCIndexDataAttr& indexAttr) {
a1 = owner->a1;
a2 = owner->a2;
area = indexAttr[f].measure;
if(restAreaFace == 0)
restAreaFace = area;
type = Internal;
if(cs.onBorder(f))
type = Membrane;
for(CCIndex fn : cs.neighbors(f))
if(indexAttr[fn].label != indexAttr[f].label)
type = Tissue::Membrane;
}
void update(CCIndex f,
const CCStructure &cs, const CCIndexDataAttr& indexAttr, VertexDataAttr &vMAttr) {
prevArea = area;
area = indexAttr[f].measure;
growthRate = owner->growthRate;
std::vector<CCIndex> vs = faceVertices(cs, f);
Point3d x_p[3] = {vMAttr[vs[0]].prevPos,vMAttr[vs[1]].prevPos,vMAttr[vs[2]].prevPos}; ////// NW
Point3d X_p[3] = {indexAttr[vs[0]].pos, indexAttr[vs[1]].pos, indexAttr[vs[2]].pos};
F = DefGradient(x_p, X_p);
F[2][2] = 1;
G = 0.5 * (transpose(F) * F - F.identity());
//Point3d v_p[3] = {vMAttr[vs[0]].prevVelocity,vMAttr[vs[1]].prevVelocity,vMAttr[vs[2]].prevVelocity};
Point3d V_p[3] = {vMAttr[vs[0]].velocity,vMAttr[vs[1]].velocity,vMAttr[vs[2]].velocity};
V = DefGradient(X_p, V_p);
E = 0.5 * (transpose((V) + V));
//PolarDecompX(F, R, U);
}
bool read(const QByteArray& ba, size_t& pos) {
readPOD<Point3d>(a1, ba, pos);
readPOD<Point3d>(a2, ba, pos);
readPOD<double>(restAreaFace, ba, pos);
readPOD<double>(area, ba, pos);
readPOD<double>(prevArea, ba, pos);
readPOD<FaceType>(type, ba, pos);
readPOD<CellData*>(owner, ba, pos);
readPOD<Point3d>(restPos[0], ba, pos);
readPOD<Point3d>(restPos[1], ba, pos);
readPOD<Point3d>(restPos[2], ba, pos);
readPOD<Point2d>(invRestMat[0], ba, pos);
readPOD<Point2d>(invRestMat[1], ba, pos);
readPOD<Point2d>(invRestMat[2], ba, pos);
return true;
}
bool write(QByteArray& ba) {
writePOD<Point3d>(a1, ba);
writePOD<Point3d>(a2, ba);
writePOD<double>(restAreaFace, ba);
writePOD<double>(area, ba);
writePOD<double>(prevArea, ba);
writePOD<FaceType>(type, ba);
writePOD<CellData*>(owner, ba);
writePOD<Point3d>(restPos[0], ba);
writePOD<Point3d>(restPos[1], ba);
writePOD<Point3d>(restPos[2], ba);
writePOD<Point2d>(invRestMat[0], ba);
writePOD<Point2d>(invRestMat[1], ba);
writePOD<Point2d>(invRestMat[2], ba);
return true;
}
bool operator==(const FaceData& other) const {
if(area == other.area)
return true;
return false;
}
};
class Subdivide : virtual public mdx::Subdivide {
public:
Subdivide() {}
Subdivide(CCIndexDataAttr& _indexAttr,
Tissue::VertexDataAttr& _vtxAttr,
Tissue::EdgeDataAttr& _edgeAttr,
Tissue::FaceDataAttr& _faceAttr,
Tissue::CellDataAttr& _cellAttr)
: indexAttr(&_indexAttr)
, cellAttr(&_cellAttr)
, edgeAttr(&_edgeAttr)
, faceAttr(&_faceAttr)
, vtxAttr(&_vtxAttr) {}
void splitCellUpdate(Dimension dim,
const CCStructure& cs,
const CCStructure::SplitStruct& ss,
CCIndex otherP = CCIndex(),
CCIndex otherN = CCIndex(),
double interpPos = 0.5, bool cellDivision=false);
private:
Mesh* mesh = 0;
CCIndexDataAttr* indexAttr = 0;
CellDataAttr* cellAttr = 0;
EdgeDataAttr* edgeAttr = 0;
FaceDataAttr* faceAttr = 0;
VertexDataAttr* vtxAttr = 0;
};
// Initialize tissue, called from GUI thread
bool initialize(QWidget* parent) {
Mesh* mesh = getMesh("Mesh 1");
if(!mesh)
throw(QString("CellTissueProcess::initialize No current mesh"));
return initialize();
}
bool initialize(bool create_dual = true, bool extended_dual = true);
void restore(CCStructure csCurr);
bool step(double Dt);
void createDualExtended(CCStructure &cs, CCStructure &csDual);
CellTissue& tissue() {
return cellTissue;
}
const QString& tissueName() const {
return TissueName;
}
const QString& tissueDualName() const {
return TissueDualName;
}
void resetChems() {
Mesh* mesh = getMesh("Mesh 1");
if(!mesh)
throw(QString("Tissue::resetChems No current mesh"));
Tissue::CellDataAttr& cellAttr =
mesh->attributes().attrMap<int, Tissue::CellData>("CellData");
Tissue::EdgeDataAttr& edgeAttr =
mesh->attributes().attrMap<CCIndex, Tissue::EdgeData>("EdgeData");
Tissue::FaceDataAttr& faceAttr =
mesh->attributes().attrMap<CCIndex, Tissue::FaceData>("FaceData");
CCStructure& cs =mesh->ccStructure("Tissue");
for(auto c : cellAttr) {
Tissue::CellData& cD = cellAttr[c.first];
cD.resetChems();
}
for(CCIndex f : cs.faces()) {
Tissue::FaceData& fD = faceAttr[f];
fD.resetChems();
}
for(CCIndex e : cs.edges()) {
Tissue::EdgeData& eD = edgeAttr[e];
eD.resetChems();
}
intercellularChems.clear();
}
void resetMechanics() {
Mesh* mesh = getMesh("Mesh 1");
if(!mesh)
throw(QString("Tissue::resetMechanics No current mesh"));
Tissue::CellDataAttr& cellAttr =
mesh->attributes().attrMap<int, Tissue::CellData>("CellData");
Tissue::EdgeDataAttr& edgeAttr =
mesh->attributes().attrMap<CCIndex, Tissue::EdgeData>("EdgeData");
Tissue::FaceDataAttr& faceAttr =
mesh->attributes().attrMap<CCIndex, Tissue::FaceData>("FaceData");
CCStructure& cs = mesh->ccStructure("Tissue");
for(auto c : cellAttr) {
Tissue::CellData& cD = cellAttr[c.first];
cD.resetMechanics(faceAttr);
}
for(CCIndex f : cs.faces()) {
Tissue::FaceData& fD = faceAttr[f];
fD.resetMechanics();
}
for(CCIndex e : cs.edges()) {
Tissue::EdgeData& eD = edgeAttr[e];
eD.resetMechanics();
}
/*for(CCIndex e : cs.vertices()) {
Tissue::VertexData& vD = vMAttr[e];
vD.resetMechanics();
}*/
}
QString TissueName;
QString TissueDualName;
Process* rootProcess = 0;
CellTissue cellTissue;
std::map<IntIntPair, double> wallAreas;
std::map<IntIntPair, std::set<CCIndex>> wallEdges;
std::map<IntIntPair, std::map<string, double>> intercellularChems;
};
// Functions needed to serialized the structs attributes above,
// as the simple writechar cannot save more that primitive types
bool inline readAttr(Tissue::CellData& m, const QByteArray& ba, size_t& pos) {
return m.read(ba, pos);
}
bool inline writeAttr(Tissue::CellData& m, QByteArray& ba) {
return m.write(ba);
}
bool inline readAttr(Tissue::FaceData& m, const QByteArray& ba, size_t& pos) {
return m.read(ba, pos);
}
bool inline writeAttr( Tissue::FaceData& m, QByteArray& ba) {
return m.write(ba);
}
bool inline readAttr(Tissue::EdgeData& m, const QByteArray& ba, size_t& pos) {
return m.read(ba, pos);
}
bool inline writeAttr(Tissue::EdgeData& m, QByteArray& ba) {
return m.write(ba);
}
bool inline readAttr(Tissue::VertexData& m, const QByteArray& ba, size_t& pos) {
return m.read(ba, pos);
}
bool inline writeAttr( Tissue::VertexData& m, QByteArray& ba) {
return m.write(ba);
}
#endif // TISSUE_HPP