User Tools

Site Tools


02_user_tutorials:02_growth_modelling:leaf-triangulation

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
02_user_tutorials:02_growth_modelling:leaf-triangulation [2025/01/24 15:41] – removed - external edit (Unknown date) 127.0.0.102_user_tutorials:02_growth_modelling:leaf-triangulation [2025/01/24 16:31] (current) tim2
Line 1: Line 1:
 +{{howhard>2}}
 +====== Leaf triangulation ======
 +
 +Triangulation is the process where a (ordered) set of triangles is generated out of a (unordered) set of points. The result is a set of triangles, now called faces, that can be directly drawn.
 +
 +
 +== ==
 +
 +Let's see the following set of 2D point:
 +
 +s={(0, 0), (-2.85, 4), (0, 10), (2.85, 3.1)}
 +
 +{{ :tutorials:leaflet1-01.png?direct&400 |}}
 +
 +When connecting the points (P_0, P_1, P_2) and (P_1, P_3, P_2) we obtain two triangles. And by doing so, we already did our first (2D) triangulation. In computer graphics such a set of triangles - from now on called faces - is called mesh.
 +
 +Note: Triangulation is not unique. Meaning there can be different triangulations for the same set of points.
 +
 +{{ :tutorials:leaflet1-02.png?direct&400 |}}
 +
 +To draw a mesh in GroIMP, all we need to do is to define the point and the faces. The used geometrical object we need to visualize the so-called //MeshNode//.
 +
 +The code to generate a simple two faces mesh could look like this:
 +
 +<code java>
 +import de.grogra.xl.util.IntList;
 +import de.grogra.xl.util.FloatList;
 +
 +const float[] p0 = {0,0,0};
 +const float[] p1 = {-2.85,4,0};
 +const float[] p2 = {0,10,0};
 +const float[] p3 = {2.85,3.1,0};
 +
 +const FloatList vertexDataLeaflet = new FloatList(new float[] {
 +//left
 +p0[0], p0[1], p0[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2],//T1
 +//right
 +p0[0], p0[1], p0[2], p2[0], p2[1], p2[2], p3[0], p3[1], p3[2]//T2
 +});
 +
 +const PolygonMesh polygonMesh = new PolygonMesh();
 +static {
 +    int[] tmp = new int[vertexDataLeaflet.size()/3];
 +    for(int i = 0; i<tmp.length; i++) tmp[i]=i;
 +    // set a list of the indices of the used list of vertices
 +    // normally = {0,1,2,3,...,n}, where n is the number of used vertices minus one 
 +    polygonMesh.setIndexData(new IntList(tmp));
 +    // set the list of vertices
 +    polygonMesh.setVertexData(vertexDataLeaflet);
 +}
 +
 +protected void init() [
 +    Axiom ==> Scale(0.007)  MeshNode.(setPolygons(polygonMesh), setShader(GREEN)); 
 +]
 +</code>
 +
 +The output of the above code could then look like this. Note: The blue lines to highlight the edges are just added to increase understanding - they will be not shown when running the above code.:
 +
 +{{ :tutorials:leaf_s.png?direct&400 |}}
 +
 +For convex shapes, the //Library// function //leaf3d// can be used to generate a triangulation like the above one. It assumes that the first point is the origin of all faces and builds a series of triangles from this point on. In XL this could look like this:
 +
 +<code java>
 +const float[] pointlist = new float[] {0,0,0,-2.85,4,0,0,10,0,2.85,3.1,0};
 +
 +protected void init() [
 + Axiom ==> leaf3d(pointlist);
 +]
 +</code>
 +
 +Coming back to the //PolygonMesh// version, the //Library// function //getMesh// takes a //FloatList// of triangulated points and does the whole conversion to //PolygonMesh// and //MeshNode// for us.
 +
 +<code java>
 +static MeshNode LeafletMesh;
 +static {
 +    LeafletMesh = getMesh(vertexDataLeaflet);
 +}
 +
 +protected void init() [
 +    Axiom ==> Scale(0.007) LeafletMesh.(setShader(GREEN));
 +]
 +</code>
 +
 +
 +Now, let's take a tomato leaflet for example,
 +
 +{{ :tutorials:leaflet.png?direct&200 |}}
 +
 +and when placed behind our points, the above triangulation could be already a very rough simplification of this particular leaflet.
 +
 +{{ :tutorials:leaflet1-03.png?direct&400 |}}
 +
 +Well, a very rough indeed. So, let's add some more points and also add the the third dimension to give the leaflet some 3D shape.
 +
 +{{ :tutorials:leaflet1-04.png?direct&400 |}}
 +
 +For the seek of laziness/simplification, the right side of triangles is assumed to be mirror symmetric to the left hand side.
 +
 +To do so within GroIMP, we just need to add the new points and apply some values for the Z-dimension. A simple curve like this may do:
 +
 +{{ :tutorials:leaflet1-06.png?direct&400 |}}
 +
 +
 +The complete code to generate the leaflet mesh looks like this:
 +
 +<code java>
 +import de.grogra.xl.util.IntList;
 +import de.grogra.xl.util.FloatList;
 +
 +const float[] p0 = {0,0,0};
 +const float[] p1 = {-0.1,-0.066,0.3};
 +const float[] p2 = {-0.2,0.06,0};
 +const float[] p3 = {-0.25,0.2,0};
 +const float[] p4 = {0,0.2,-0.4};
 +const float[] p5 = {-0.285,0.4,0};
 +const float[] p6 = {0,0.4,-0.5};
 +const float[] p7 = {-0.22,0.53,0};
 +const float[] p8 = {0,0.53,-0.3};
 +const float[] p9 = {0,1,1.0};
 +
 +const FloatList vertexDataLeaflet = new FloatList(new float[] {
 +//left
 +p0[0], p0[1], p0[2], p1[0], p1[1], p1[2], p2[0], p2[1], p2[2],//T1
 +p0[0], p0[1], p0[2], p2[0], p2[1], p2[2], p3[0], p3[1], p3[2],//T2
 +p0[0], p0[1], p0[2], p3[0], p3[1], p3[2], p4[0], p4[1], p4[2],//T3
 +p3[0], p3[1], p3[2], p4[0], p4[1], p4[2], p5[0], p5[1], p5[2],//T4
 +p4[0], p4[1], p4[2], p5[0], p5[1], p5[2], p6[0], p6[1], p6[2],//T5
 +p5[0], p5[1], p5[2], p6[0], p6[1], p6[2], p7[0], p7[1], p7[2],//T6
 +p6[0], p6[1], p6[2], p7[0], p7[1], p7[2], p8[0], p8[1], p8[2],//T7
 +p7[0], p7[1], p7[2], p8[0], p8[1], p8[2], p9[0], p9[1], p9[2],//T8
 +//right
 +-p0[0], p0[1], p0[2], -p1[0], p1[1], p1[2], -p2[0], p2[1], p2[2],//T9
 +-p0[0], p0[1], p0[2], -p2[0], p2[1], p2[2], -p3[0], p3[1], p3[2],//T10
 +-p0[0], p0[1], p0[2], -p3[0], p3[1], p3[2], -p4[0], p4[1], p4[2],//T11
 +-p3[0], p3[1], p3[2], -p4[0], p4[1], p4[2], -p5[0], p5[1], p5[2],//T12
 +-p4[0], p4[1], p4[2], -p5[0], p5[1], p5[2], -p6[0], p6[1], p6[2],//T13
 +-p5[0], p5[1], p5[2], -p6[0], p6[1], p6[2], -p7[0], p7[1], p7[2],//T14
 +-p6[0], p6[1], p6[2], -p7[0], p7[1], p7[2], -p8[0], p8[1], p8[2],//T15
 +-p7[0], p7[1], p7[2], -p8[0], p8[1], p8[2], -p9[0], p9[1], p9[2]//T16
 +});
 +
 +static MeshNode LeafletMesh;
 +static {
 +    LeafletMesh = getMesh(vertexDataLeaflet);
 +}
 +
 +protected void init() [
 +    Axiom ==> Scale(0.07, 0.07, 0.01) LeafletMesh.(setShader(GREEN)); 
 +]
 +</code>
 +
 +The output of the above code could then look like this:
 +
 +{{ :tutorials:leaflet_model.png?direct&400 |}}
 +
 +The last step would now be to pack the leaflet mesh into a module and integrate it further into a leaf module (not shown here). For the first aspect, we can define a module like this:
 +
 +<code java>
 +module Leaflet(float length) ==>
 +    Scale(length, length, 0.01) MeshNode.(setPolygons(polygonMesh), setShader(GREEN));
 +</code>
 +
 +After integration in to a leaf module the final 3D result could look like this
 +
 +{{ :tutorials:fig06a4.png?direct&400 |}}
 +
 +as it was used within Zhang //et al.// 2021 (High resolution 3D simulation of light climate and thermal performance of a solar greenhouse model under tomato canopy structure; //Renewable Energy//, 160, 730-745, doi: [[https://doi.org/10.1016/j.renene.2020.06.144]]).
 +
 +GroIMP provides a few predefined 3D leaves that can be accessed using the Library function leaf3d(id). There are shapes for: "artificial", "maple", "undefined", "poplar", "cotton", "birch", "undefined", "oak". The identifier starts from zero for the first "artificial" shape and goes up to seven for the oak leaf.
 +
 +The code below will generate them one by one next to each other.
 +
 +<code java>
 +protected void init() [
 +  Axiom ==>
 +    for(int i=0; i<DEFAULT_LEAF3D.length; i++) (
 +      [ Null(i*0.3,0,0) leaf3d(i) ]
 +    )
 +]
 +</code>
 +
 +The predefined 3D leaf shapes will look as shown below:
 +
 +{{ :tutorials:leaf3d.png?direct&625 |}}
 +
 +
 +
 +