2D Vertex Model

geometrical f(x)

In[]:=
(*getvertexinthelistbyccworder*)​​Clear[getCounterClockwise];​​getCounterClockwise[vertex_,vertices_]:=Block[{pos,v},​​pos=First@@Position[vertices,vertex];​​If[posLength[vertices],pos=1,pos+=1];​​vertices[[pos]]​​];
In[]:=
(*getvertexinthelistbycworder*)​​Clear[getClockwise];​​getClockwise[vertex_,vertices_]:=Block[{ls,pos},​​pos=First@@Position[vertices,vertex];​​If[pos1,pos=Length[vertices],pos-=1];​​vertices[[pos]]​​];
In[]:=
getCounterClockwise[{
x
i
,
y
i
},{{
x
i-1
,
y
i-1
},{
x
i
,
y
i
},{
x
i+1
,
y
i+1
},d,e}]
Out[]=
{
x
1+i
,
y
1+i
}
In[]:=
getClockwise[{
x
i
,
y
i
},{{
x
i-1
,
y
i-1
},{
x
i
,
y
i
},{
x
i+1
,
y
i+1
},d,e}]
Out[]=
{
x
-1+i
,
y
-1+i
}
In[]:=
Clear[areaOfPolygon];​​areaOfPolygon[cells_/;Head[cells]===Association]:=Map[Area@*Polygon,cells];
In[]:=
Clear[areaPolygon];​​areaPolygon[vertices_]:=Block[{edges},​​edges=Partition[vertices,2,1,1];​​0.5Abs@Total[(#[[1,1]]*#[[2,2]])-(#[[2,1]]*#[[1,2]])&/@edges]​​]
In[]:=
randpoly=Polygon
Number of points: 5
Embedding dimension: 2
Type:
simple polygon
Bounds:
{{0.05,0.98},{0.27,0.92}}
Area:
0.29
;
In[]:=
Area[randpoly]
Out[]=
0.287052
In[]:=
areaPolygon[MeshPrimitives[randpoly,0]/.PointSequence]
Out[]=
0.287052
In[]:=
Clear[perimeterOfPolygon];​​perimeterOfPolygon[cells_/;Head[cells]===Association]:=(Perimeter@*Polygon)/@cells;
In[]:=
Clear[perimeterPolygon];​​perimeterPolygon[vertices_]:=Block[{edges},​​edges=Partition[vertices,2,1,1];​​Total[Apply[EuclideanDistance]/@edges]​​]
In[]:=
Perimeter@randpoly
Out[]=
2.4033
In[]:=
perimeterPolygon[MeshPrimitives[randpoly,0]/.PointSequence]
Out[]=
2.4033
In[]:=
Clear[centroidPolygon];​​centroidPolygon[vertices_]:=Mean[vertices]
In[]:=
(*counterclockwisepolygonQ*)​​Block{signedarea=0,j,vertlen=5},​​Do​​j=Mod[i,vertlen]+1;​​signedarea+=(
x
i
y
j
-
x
j
y
i
),​​{i,vertlen};​​Echo
1
2
(signedarea)​​;
»
1
2
(-
x
2
y
1
+
x
5
y
1
+
x
1
y
2
-
x
3
y
2
+
x
2
y
3
-
x
4
y
3
+
x
3
y
4
-
x
5
y
4
-
x
1
y
5
+
x
4
y
5
)
In[]:=
Clear[polyCounterClockwiseQ];​​polyCounterClockwiseQ[poly_]:=Block[{area=0,j,vertLength=Length[poly]},​​Do[​​j=Mod[i,vertLength]+1;​​area+=poly[[i,1]]*poly[[j,2]];​​area-=poly[[j,1]]*poly[[i,2]],​​{i,vertLength}​​];​​(area/2.)>0​​]
In[]:=
(*Clear[sortCC];​​sortCC[polyinds_,indTopts_,ptsToInds_]:=Block[{cent,poly},​​poly=Lookup[indTopts,polyinds];​​Lookup[ptsToInds,​​DeleteDuplicates@Flatten[MeshPrimitives[ConvexHullMesh[poly],1]/.LineSequence,1]​​]​​];*)
In[]:=
(*sortpointsforaconvexpolygonincounter-clockwisedirection*)
In[]:=
Clear[sortPointsCC];​​sortPointsCC[polyinds_,indTopts_,ptsToInds_]:=Block[{cent,ordering,polyPoints},​​polyPoints=Lookup[indTopts,polyinds];​​cent=Mean[polyPoints];​​ordering=Ordering[ArcTan[#[[1]],#[[2]]]&@(#-cent)&/@polyPoints];​​Lookup[ptsToInds,Part[polyPoints,ordering]]​​]

mesh restructuring operations

In[]:=
bagged=CreateDataStructure["DynamicArray"]
Out[]=
DataStructure
Type:DynamicArray
Length:0


T1 transition

In[]:=
(*T1transition:neighbourswitching*)
In[]:=
Clear@edgesforT1;​​edgesforT1[edgeLs_,indToPts_,threshLength_:0.002]:=Block[{edges,dist,bool},​​edges=Lookup[indToPts,#]&/@edgeLs;​​dist=EuclideanDistance@@#&/@edges;​​bool=Thread[dist≤threshLength];​​{Pick[edges,bool,True],Pick[edgeLs,bool,True]}​​];
In[]:=
Clear@T1transitionFn;​​T1transitionFn[edges_,indToPtsAssoc_,vertexToCellG_,cellToVertexG_,dSep_:0.0075]:=Block{findEdges,edgeind,connectedcellKeys,edge,newpts,cellvertIndices,cellvertices,pos,cellpolys,memF,keyscellP,selcellKeys,ptToCell,newptsindices,indToPts=indToPtsAssoc,ptsToInds,PtIndToCell,keysToMap,cellindicesAssoc,f1,otherkeys,f2,polysharingEdge,bag=bagged,vertToCellG=vertexToCellG,cellToVertG=cellToVertexG,testpts,edgechanged,localtopology,ls,cs,cc,ccw,trimmedtopo,ordering,edgeordered},​​{edgechanged,findEdges}=edgesforT1[edges,indToPts];(*findingallpossibleedgesforT1transition*)​​​​IffindEdges≠{},​​Scan​​edgeind=#;​​IfContainsAll[Keys[indToPts],edgeind],​​(*shouldbeanedgenotconnectedtoanedgethathasalreadyundergoneaT1*)​​connectedcellKeys=DeleteDuplicates[Flatten@Lookup[vertToCellG,edgeind]];​​cellvertIndices=Lookup[cellToVertG,connectedcellKeys];​​edge=Lookup[indToPts,edgeind];​​IfLength[connectedcellKeys]1,​​(*edgethatisexposedtothevoidtobemergedasasinglevertex*)​​newpts=Mean[edge];​​newptsindices=Max[Keys@indToPts]+1;​​KeyDropFrom[indToPts,edgeind];​​AppendTo[indToPts,newptsindicesnewpts];​​bag["Append",edgeind];​​ptsToInds=AssociationMap[Reverse,indToPts];​​cellToVertG=MapAt[​​DeleteDuplicates[#/.(Alternatives@@edgeind)newptsindices]&,​​cellToVertG,Key[connectedcellKeys/.{z_Integer}z]​​],​​(*elseproceedwithT1transition*)​​newpts=With{midPt=Mean[edge]},​​midPt+dSepNormalize[(#-midPt)]&/@FlattenRotationTransform-
π
2
,midPt/@{edge},1​​;​​testpts=With[{midPt=Mean[edge]},​​midPt+0.000001Normalize[(#-midPt)]&/@newpts​​];​​pos=Position[cellvertIndices,{OrderlessPatternSequence[___,First@edgeind,___,Last@edgeind,___]},{1}];​​polysharingEdge=Extract[cellvertIndices,pos];​​(*theedgeshouldnotbepartofanyΔ*)​​If[(AllTrue[polysharingEdge,Length[#]≠3&])&&ContainsNone[edgeind,Union@*Flatten@*Normal@bag],​​cellvertices=Map[Lookup[indToPts,#]&,cellvertIndices];​​cellpolys=Polygon/@cellvertices;​​memF=Function[x,RegionMember@*DiscretizeRegion@x,Listable][Extract[cellpolys,pos]];​​keyscellP=Extract[connectedcellKeys,pos];​​selcellKeys=Thread[keyscellPmemF];​​ptToCell=Quiet[#First@@Select[selcellKeys,Function[x,Last[x][#]]]&/@testpts/.HoldPattern[_First[]]Nothing];(*pttocell*)​​ptToCell=ptToCell/.Thread[testptsnewpts];​​newptsindices=Range[#+1,#+2]&[Max[Keys@indToPts]];​​​​AppendTo[indToPts,Thread[newptsindicesnewpts]];​​ptsToInds=AssociationMap[Reverse,indToPts];​​bag["Append",edgeind];​​PtIndToCell=MapAt[ptsToInds,ptToCell,{All,1}]/.RuleList;(*indextocell*)​​keysToMap=MapAt[Key,PtIndToCell,{All,2}];​​cellindicesAssoc=AssociationThread[connectedcellKeys,cellvertIndices];​​​​f1=Fold[MapAt[Function[x,DeleteDuplicates[x/.Thread[edgeind#2[[1]]]]],#1,#2[[2]]]&,cellindicesAssoc,keysToMap];​​f1=Lookup[indToPts,#]&/@f1;​​​​KeyDropFrom[indToPts,edgeind];​​​​otherkeys=List@*Key/@Complement[connectedcellKeys,keyscellP];​​(*Print@Graphics[{EdgeForm[Black],FaceForm[Red],Polygon/@cellvertices},ImageSizeTiny];*)​​​​localtopology=AssociationThread[connectedcellKeyscellvertices];​​ls=Lookup[localtopology,First@@otherkeys];​​cs=FirstCase[ls,Alternatives@@edge];​​cc=getClockwise[cs,ls];​​ccw=getCounterClockwise[cs,ls];​​trimmedtopo=KeyDrop[localtopology,connectedcellKeys~Complement~keyscellP];​​ordering={FirstCase[Position[trimmedtopo,cc],x:{Key[First@keyscellP],_}|{Key[Last@keyscellP],_}First@@x],​​FirstCase[Position[trimmedtopo,ccw],x:{Key[First@keyscellP],_}|{Key[Last@keyscellP],_}First@@x]};​​​​If[Length[otherkeys]1,​​edgeordered=ordering/.Reverse[ptToCell,2];​​f2=MapAt[Replace[#,Alternatives@@edgeSplice[edgeordered],{1}]&,f1,First@otherkeys];​​(*Print@Graphics[{EdgeForm[Black],FaceForm[Red],Polygon/@Values@f2},ImageSizeTiny];*)​​,​​If[MatchQ[ordering,{_Missing,_}|{_,_Missing}],​​ordering=Flatten[ordering/._MissingComplement[newpts,Keys@ptToCell],1];​​edgeordered=ordering,​​edgeordered=ordering/.Reverse[ptToCell,2];​​];​​f2=MapAt[Replace[#,Alternatives@@edgeSplice[edgeordered],{1}]&,f1,First@otherkeys];​​f2=MapAt[Replace[#,Alternatives@@edgeSplice[Reverse[edgeordered]],{1}]&,f2,Last@otherkeys];​​(*Print@Graphics[{EdgeForm[Black],FaceForm[Red],Polygon/@Values@f2},ImageSizeTiny];*)​​];​​AppendTo[cellToVertG,Lookup[ptsToInds,#]&/@f2];​​];​​;​​vertToCellG=GroupBy[Flatten[(Reverse[#,2]&)@*Thread/@Normal@cellToVertG],FirstLast];​​&,findEdges​​;​​bagged["DropAll"];​​{edgechanged,indToPts,cellToVertG,vertToCellG}​​;

T2 transition

T3 transition

cell division

Difference from Farahdifar's paper: in his article he dilates the cells before dividing them. Here I randomly select a cell and divide it into two.

force computation

create mesh

run simulation 1

run simulation 2