====== Introducing leaf growth ======
//Open the file {{ :tutorials:LeafGrowth.gsz |LeafGrowth.gsz}}.//
Currently, the leaves and internodes of our plant just “pop up” with their final size, although in a real plant they would, of course, grow. Growth in length can be modelled easily: first, we need to give both ''Leaf'' and ''Internode'' several new parameters: ''length'' and ''age'' for ''Internode''; and ''length'', ''width'' and ''age'' for ''Leaf'' (L. 9 and L. 14).
module Internode(super.length) extends
Cylinder(length, 0.05) {
{setShader(internodemat);}
}
module Leaf(super.length, super.width, float al) extends
Box(length,width,0.01).
(setShader(new AlgorithmSwitchShader(new RGBAShader(0, 1, 0), GREEN)));
Then we initialize the ''length'' (and ''width'') with a small value (0.1 and 0.07), and set the initial ''age'' to 1 in the ''init()'' method:
Bud(r, p, o), (r < 10 && p == 0 && o <= 3) ==> RV(-0.1)
Internode(0.1, 1) Node [RL(BRANCH_ANGLE) Bud(r, PHYLLOCHRON, o+1) ]
[ RL(LEAF_ANGLE) Leaf(0.1, 0.07, 0)] RH(GOLDEN_ANGLE) RV(-0.1)
Internode(0.1) Bud(r+1, PHYLLOCHRON, o);
Next, we need to find an appropriate function that describes growth. The logistic function is actually quite accurate when it comes to description of organ length. It is one of the many sigmoid functions. Its derivative can be used to describe the growth rate as a function of organ age. We declare the derivative of this logistic function as a new public method in the code, before the ''init()'' method:
//logistic function, used to determine growth rate:
public float logistic (float maxdim, int time, float phylloM, float slope)
{
return(slope*maxdim*Math.exp(-slope*(time-phylloM)))/
((Math.exp(-slope*(time-phylloM))+1)**2);
}
The function has three parameters, for now we are only interested in organ age. This we declare as a further (integer) parameter in Leaf and Internode:
module Internode(super.length, int age) extends
Cylinder(length, 0.05) {
{setShader(internodemat);}
}
module Leaf(super.length, super.width, float al, int age) extends
Box(length,width,0.01).
(setShader(new AlgorithmSwitchShader(new RGBAShader(0, 1, 0), GREEN)));
Next, we need to initialize ''age'' with 1 in the ''run()'' method (L. 103).
Bud(r, p, o), (r < 10 && p == 0 && o <= 3) ==> RV(-0.1)
Internode(0.1, 1) Node [RL(BRANCH_ANGLE) Bud(r, PHYLLOCHRON, o+1) ]
[ RL(LEAF_ANGLE) Leaf(0.1, 0.07, 0, 1)] RH(GOLDEN_ANGLE) RV(-0.1)
Internode(0.1, 1) Bud(r+1, PHYLLOCHRON, o);
Then, we extend the ''Leaf'' rule in the method that we now rename to ''absorbAndGrow()'' to model leaf ageing and extension in length and width:
protected void absorbAndGrow()
[
lf:Leaf ::> {
lf[al] = lm.getAbsorbedPower3d(lf).integrate()*2.25;
//println(lf[al]);
lf.(setShader(new AlgorithmSwitchShader(new RGBAShader(lf[al]/5.0, lf[al]*2,lf[al]/100.0),GREEN)));
//ageing and growth of leaf:
lf[age]++;
lf[length] += logistic(3,lf[age],40,0.1);
lf[width] = lf[length]*0.7;
}
]
Finally, we add another execution (update) rule to model internode extension:
protected void absorbAndGrow()
[
lf:Leaf ::> {
lf[al] = lm.getAbsorbedPower3d(lf).integrate()*2.25;
//println(lf[al]);
lf.(setShader(new AlgorithmSwitchShader(new RGBAShader(lf[al]/5.0, lf[al]*2,lf[al]/100.0),GREEN)));
//ageing and growth of leaf:
lf[age]++;
lf[length] += logistic(3,lf[age],40,0.1);
lf[width] = lf[length]*0.7;
}
//extension of internode:
itn:Internode ::> {
itn[age]++;
itn[length] += logistic(1,itn[age],10,0.1);
}
]
//Task: Change the leaf growth parameters to obtain a very flat or else a rather steep light interception curve! (see figures below)//
{{tutorials:outputLG1.png?200*240}}{{tutorials:outputLG2.png?200*240}}{{tutorials:outputLG3.png?200*240}}