====== A simple tomato plant model ====== In this tutorial we will create a simple model of a tomato plant architecture. ===== Preliminary steps ===== We start a new project by opening a new RGG template model (File -> New -> RGG Project). The simple model that we are going to create will have the following structure: // modules // parameters protected void init() [ Axiom ==> // initial structure ; ] public void run() [ // rewriting rule ] ===== Organs ===== As first, we specify a base module ''Organ'', having some properties that all type of organs in a plant will share. This module has an attribute ''length'', inherited from a module ''M''. This way, every type of organ derived from the ''Organ'' module will have attribute ''length''. It also has an attribute ''rank'' that we will use to describe the position of organs from the bottom of the plant to the top. module Organ(super.length) extends M(length) { int rank; { rank = 1; } } Now we can specify a number of modules for different organ types that we observe in a tomato plant. module Apex extends Organ; module Internode extends Organ; module Leaf extends Organ; module Truss extends Organ; ==== Model parameters ==== Outside of the modules, we define model parameters that will be used later to specify the shape of organs. // leaf parameters const double LEAF_LENGTH = 0.40; const double PETIOLE_WIDTH = 0.01; const double PHYLLOTAXIS_ANGLE = 137.51; const double LEAF_ANGLE = 60; // internode parameters const double INTERNODE_LENGTH = 0.08; const double INTERNODE_WIDTH = 0.02; // truss parameters const int NB_FRUITS = 5; const double TRUSS_LENGTH = 0.1; const double TRUSS_ANGLE = 60; const double FRUIT_RADIUS = 0.02; ==== Specifying organ geometry ==== We extend the modules' definition by adding geometric representation for each organ type. The geometry of organs will be specified using instantiation rules ''%%==>%%''. An ''Apex'' will be represented as a red sphere. {{ :tutorials:apex_crop.png?100|}} module Apex extends Organ ==> Sphere(0.01).(setShader(RED)) ; An ''Internode'' will be represented as a yellow cylinder. The length and width of the Internode will be specified inside the module, using the constants we defined above. {{ :tutorials:internode_crop.png?100|}} module Internode extends Organ { double width; { length = INTERNODE_LENGTH; width = INTERNODE_WIDTH; } } ==> Cylinder(length, width/2).(setShader(YELLOW)) ; For a leaf, we will start with a very simple representation. It will be made of a petiole (a green cylinder) and a rectangular blade. A turtle command ''RL'' will be used to specify the angle of the leaf to the plant stem. {{ :tutorials:leaf_crop.png?100|}} module Leaf extends Organ { double width; double angle; double petioleLength; double petioleWidth; { length = LEAF_LENGTH; width = 0.5*LEAF_LENGTH; angle = LEAF_ANGLE; petioleLength = 0.2*LEAF_LENGTH; petioleWidth = PETIOLE_WIDTH; } } ==> RL(angle) // petiole Cylinder(petioleLength, petioleWidth/2).(setShader(GREEN)) // leaf blade Parallelogram(length-petioleLength, width) ; Truss will be made of multiple fruits (''NB_FRUITS''), each represented by a red sphere, connected to the main segments of the truss (yellow cylinders) by small segments (blue cylinders). Different colours are used to help with understanding how the segments carrying the fruits are specified in the code. Turtle commands ''RL'', ''RH'', and ''RU'' will be used to add some curvature to the segments of the truss and to place the fruits left and right of the main segments of the truss. (In the truss figure, the individual segments are slightly longer than specified in the code example, for illustration purpose.) {{ :tutorials:truss_crop.png?150|}} module Truss extends Organ { {length = TRUSS_LENGTH;} } ==> RL(TRUSS_ANGLE) RH(90) for (int i:1:NB_FRUITS) ( Cylinder(length/NB_FRUITS,0.001).(setShader(YELLOW)) [ if(i%2==0) ( RH(90) ) else ( RH(-90) ) RU(60) Cylinder(0.02,0.001).(setShader(BLUE)) // fruit Sphere(FRUIT_RADIUS).(setShader(RED)) ] RU(-20) ) ; ===== The initial condition ===== The initial structure, the axiom, is an apex. protected void init() [ Axiom ==> Apex; ] ===== Rewriting rules ===== Now we can specify rewriting rules for apex. First, we specify rewriting rules for vegetative development of the primary shoot in tomato. {{ :tutorials:primaryshoot_crop.png?150|}} public void run() [ a:Apex ==> Internode [Leaf] RH(PHYLLOTAXIS_ANGLE) a ; ] The turtle command ''RH'' is used to create the phyllotactic pattern of leaves (resulting in their placement around the stem). Now that we have specified the ''Axiom'' inside the ''init()'' method and a rewriting rule for the ''Apex'' inside the ''run()'' method, we can run the model by clicking on the ''run'' button in the RGG Toolbar, placed above the 3D View. By applying the above rule to an apex 7 times, we create a small plant made of 7 internodes and 7 leaves. In tomato, after a certain number of phytomers (internodes with leaves), specified by a parameter ''NB_VEG_PHYTOMERS'', primary shoot turns into a sympodial shoot, that bears trusses with fruits. For this sympodial shoot, a repetitive pattern with 3 leaves and 1 truss with fruits is typical in many (high-wire) tomato cultivars. We first specify a new parameter for the number of phytomers in primary shoot: const int NB_VEG_PHYTOMERS = 8; Afterwards, we can add a condition inside our rule, as a switch between primary shoot and sympodial shoot. {{ :tutorials:simpletomato_crop.png?150|}} public void run() [ a:Apex ==> Internode // in primary shoot, every internode has a leaf if (a[rank] <= NB_VEG_PHYTOMERS) ( [Leaf] // in sympodial shoot, only the first in every four // internodes has a truss, otherwise it has a leaf ) else ( if ((a[rank] - NB_VEG_PHYTOMERS) % 4 == 1) ( [Truss] ) else ( [Leaf] ) ) RH(PHYLLOTAXIS_ANGLE) a {a[rank]++;} ; ] Note, that in this simple model, we assumed that tomato plants don't form side shoots (in the greenhouse production of high-wire tomatoes, the side shoots are normally removed). ===== Improving leaf structure ===== Having captured the basic architecture of tomato plants, we can now improve the leaf structure. Tomato has a composed leaf, with a number of leaflet pairs and a terminal leaflet. We also add leaf curvature, by slightly bending down each segment of the leaf. const int NB_LEAFLET_PAIRS = 3; {{ :tutorials:simpletomatoleaflets_crop.png?150|}} module Leaf extends Organ { ... } ==> RL(angle) // petiole Cylinder(petioleLength, petioleWidth/2).(setShader(GREEN)) { double segmentLength = (length-petioleLength)/(NB_LEAFLET_PAIRS+1); } // leaflet pairs for (int i:1:NB_LEAFLET_PAIRS) ( [RU(-80) RL(-30) Cylinder(0.04, 0.003) RL(40) Parallelogram(0.1, 0.05)] [RU( 80) RL(-30) Cylinder(0.04, 0.003) RL(40) Parallelogram(0.1, 0.05)] RL(20) Cylinder(segmentLength, 0.003) ) // terminal leaflet Parallelogram(segmentLength, 0.05) ; ===== Improving leaflet shape ===== So far, we used a simple parallelogram to approximate the shape of a leaflet. We can further refine the leaflet shape, for example by using a ''MeshNode'' instead. For an example on how to generate a leaflet mesh, see the tutorial [[:Tutorials:leaf-triangulation| Leaf triangulation ]]. We use the code from the leaf triangulation tutorial to create a leaflet mesh ''MeshNode.(setPolygons(polygonMesh))'', where the polygons (''polygonMesh'') are created from a list of predefined vertices (''vertexDataLeaflet''). To use the leaflet mesh in our tomato model, we create a new module for leaflet: module Leaflet(double length) ==> RL(90) Scale(length, length, 0.01) MeshNode.(setPolygons(polygonMesh), setShader(GREEN)) ; Then we replace ''Parallelogram''-s in the Leaf module by the newly defined ''Leaflet''-s: {{ :tutorials:simpletomatoleafletsmesh_crop.png?150|}} module Leaf extends Organ { ... } ==> ... // leaflet pairs for (int i:1:NB_LEAFLET_PAIRS) ( [RU(-80) RL(-30) Cylinder(0.04, 0.003) RL(40) Leaflet(0.1)] [RU( 80) RL(-30) Cylinder(0.04, 0.003) RL(40) Leaflet(0.1)] RL(20) Cylinder(segmentLength, 0.003) ) // terminal leaflet Leaflet(segmentLength) ;