Skip to content

Commit c1decc6

Browse files
authored
Merge pull request #1 from OSUrobotics/Camp_summer_work
Merging work done during summer 2024 by Ellie Camp
2 parents c9efe30 + 9b8c08e commit c1decc6

File tree

7 files changed

+1392
-129
lines changed

7 files changed

+1392
-129
lines changed

README.rst

Lines changed: 258 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,271 @@
22
TreeSim_Lpy
33
============
44

5+
Description
6+
-----------
7+
8+
TreeSim_Lpy is a tree modelling tool which is built upon L-py with the added features of pruning and tying trees down to mimic different architectures. The tool uses python and prior knowledge of L-systems and L-Py is needed to work with this tool. This tool is ideal for researchers and developers working on botanical simulations and robotic harvesting applications.
59

6-
TreeSim_Lpy is a tree modelling tool which is built upon L-py with the added features of pruning
7-
and tying trees down to mimic different architectures. The tool uses python and prior knowledge of L-systems
8-
and L-Py is needed to work with this tool.
910

1011
Python version 3.9
1112

13+
Table of Contents
14+
-----------------
15+
16+
- `Installation <#installation>`__
17+
- `Tutorials <#tutorials>`__
18+
- `Usage <#usage>`__
19+
- `Features <#features>`__
20+
- `Contributing <#contributing>`__
21+
- `License <#license>`__
22+
- `Contact <#contact>`__
23+
- `Acknowledgments <#acknowledgments>`__
24+
25+
Documentation
26+
-------------
27+
28+
The documentation is provided at `Read the Docs <https://treesim-lpy.readthedocs.io/en/latest/>`__.
29+
30+
You can find the latest L-Py documentation at <https://lpy.readthedocs.io/en/latest>
31+
32+
33+
Installation
34+
------------
35+
36+
To install TreeSim_Lpy, follow these steps (adapted from the `L-Py documentation <https://treesim-lpy.readthedocs.io/en/latest/installation.html>`__):
37+
38+
1. **Install Conda**:
39+
40+
The L-Py distribution relies on the conda software environment management system. If you do not already have conda installed, you can find installation instructions on the `Conda Installation Page <https://docs.conda.io/projects/conda/en/latest/user-guide/install/>`__.
41+
42+
2. **Create a Conda Environment**:
43+
44+
Create an environment named `lpy`:
45+
46+
.. code-block:: sh
47+
48+
conda create -n lpy openalea.lpy -c fredboudon -c conda-forge
49+
50+
The package is retrieved from the `fredboudon` channel (development), and its dependencies will be taken from the `conda-forge` channel.
51+
52+
3. **Activate the L-Py Environment**:
53+
54+
.. code-block:: sh
55+
56+
conda activate lpy
57+
58+
4. **Install Required Packages**:
59+
60+
.. code-block:: sh
61+
62+
pip install -r requirements.txt
63+
64+
5. **Run L-Py**:
65+
66+
.. code-block:: sh
67+
68+
lpy
69+
70+
71+
Tutorials
72+
---------
73+
74+
There are many things you may want to modify as you grow your own trees. Here are some tutorials for some of the more common changes:
75+
76+
1. **Changing Apple Geometry:**
77+
78+
The call production of the apples happens in the ``grow_object(o)`` section:
79+
80+
.. code-block:: python
81+
82+
elif 'Apple' in o.name:
83+
produce [S(.1/2, .09/15)f(.1)&(180)A(.1, .09)]
84+
85+
86+
87+
The apple's base is generated with the ``A(bh, r)`` production rule seen below.
88+
89+
.. code-block:: python
90+
91+
A(bh, r):
92+
curves = make_apple_curve()
93+
base_curve = curves[0]
94+
top_curve = curves[1]
95+
nproduce SetColor(230,0,0) SectionResolution(60)
96+
produce nF(bh, .01, r, base_curve) ^(180) nF(bh/5, .1, r, top_curve)^(180)
97+
98+
The parameters represent the base height of the apple and the radius of the apple. If you wanted to create a completely new apple geometry, just replace the code in this A section. However, if you simply want to edit the existing shape of the apple, that can be done in the ``make_apple_curve()`` section.
99+
100+
The apple is made with two curves: a curve that marks the base of the apple, and a curve that marks the indentation on top of the apple. These curves are generated as different Curve2D objects, then turned into QuantisedFunction objects. This is necessary because of the way the apple is produced ``nF``. ``nF`` has an optional parameter ``radiusvariation`` which must be a quantized function. ``nF`` produces a cylinder in n steps, and these curves work by specifying how large the radius for the cylinder should be at each step.
101+
102+
Currently, the stem is produced separately from the apple base. The stem is created in a slightly different way than the apple. A NurbsCurve2D object is returned from the ``make_stem_curve()`` function. This curve is used in ``SetGuide`` to mark how the stem will be generated. ``nF`` is used to follow the guide while generating a cylinder, and there is no ``radiusvariation`` this time.
103+
104+
.. code-block:: python
105+
106+
S(sh,r):
107+
stem_curve = make_stem_curve()
108+
nproduce SetColor(100,65,23)
109+
produce SetGuide(stem_curve, sh) _(r)nF(sh, .1, r)
110+
111+
112+
2. **Changing Leaf Geometry:**
113+
114+
The call production of the leaves happens in the ``grow_object(o)`` section:
115+
116+
.. code-block:: python
117+
118+
elif 'Leaf' in o.name:
119+
produce L(.1)
120+
121+
Here, .1 is just a hard-coded value that doesn't have much significance.
122+
123+
The actual generation of the leaf can be seen in the ``L(l)`` production section:
124+
125+
.. code-block:: python
126+
127+
L(l):
128+
nproduce SetColor(0,225,0)
129+
130+
curves = make_leaf_guide()
131+
curve1 = curves[0]
132+
curve2 = curves[1]
133+
produce _(.0025) F(l/10){[SetGuide(curve1, l) _(.001).nF(l, .01)][SetGuide(curve2, l)_(.001).nF(l, .01)]}
134+
135+
The parameter here serves as the length of the leaf. To edit the shape of the leaf, edits can be made in the ``make_leaf_guide()`` function. In the ``make_leaf_guide()`` function, a BezierCurve2D and its inverse are generated. These are returned to be used as guides for the ``SetGuide`` function provided by L-Py. These curves are generated with random points from a set range in order to create leaves of different shape. These randomness of these points, or the range they are generated from, could be edited to change the leaf shape. However, a new geometry altogether could be made in the ``L(l)`` section.
136+
137+
138+
139+
140+
3. **Changing Bud Geometry:**
141+
142+
The call for the production of the buds occurs in the ``grow_one(o)`` section by addicting ``spiked_bud(o.thickness)`` to the ``produce`` call:
143+
144+
.. code-block:: python
145+
146+
if 'Spur' in o.name:
147+
produce I(o.growth_length, o.thickness, o) bud(ParameterSet(type=o, num_buds=0)) spiked_bud(o.thickness)grow_object(o)
148+
149+
The actual bud is produced in the ``spiked_bud`` production section:
150+
151+
.. code-block:: python
152+
153+
spiked_bud(r):
154+
base_height = r * 2
155+
top_height = r * 2
156+
num_sect = 20
157+
produce @g(Cylinder(r,base_height,1,num_sect))f(base_height)@g(Cone(r,top_height,0,num_sect))
158+
159+
This is one of the most basic objects generated on the tree model. As the buds on actual trees are little spikes, the bud geometry is made up of a cone on top of a cylinder. These are both produced with L-Py's basic ``@g`` primitive used to draw PglShapes. The height of the two shapes scale with the radius (provided as the parameter). The ``num_sect`` is used to determine how many sections each shape is made up of, and 20 was chosen as they appear circular without adding too many triangles to the model.
160+
161+
162+
163+
4. **Changing Branch Profile Curve:**
164+
165+
Every branch on the model has a unique profile curve. This is so the model doesn't appear to be perfectly cylindrical (as if made of PVC pipes). Every branch has its own unique curve that is generated when the branch is originally generated. This can be found in the actual class declaration at the beginning of the code:
166+
167+
.. code-block:: python
168+
169+
self.contour = create_noisy_circle_curve(1, .2, 30)
170+
171+
Every ``Trunk``, ``Branch``, and ``NonBranch`` object have a their own unique curve associated with them. However this curve is only applied in the ``grow_one(o)`` section:
172+
173+
.. code-block:: python
174+
175+
if 'Trunk' in o.name or 'Branch' in o.name:
176+
nproduce SetContour(o.contour)
177+
else:
178+
reset_contour()
179+
180+
This code targets every ``Trunk``, ``Branch``, and ``NonBranch`` object and sets their own curve when growing them. Whenever any other object is passed to the ``grow_object(o)`` function the call to ``reset_contour()`` sets the contour back to a perfect circle to ensure that no curves overlap.
181+
182+
The ``create_noisy_circle()`` function is included in the helper.py file. It works by creating a circle out of a given number of points, and then moving those points in the x and y direction according to a given noise factor. The function has two required parameters and two optional parameters. The two required parameters are ``radius`` and ``noise_factor``. The ``radius`` determines the radius of the generated circle. The ``noise_factor`` is used to set a range in which random points will be generated. The points making up the circle will then be moved a random amount in that range. This can be seen in the main ``for`` loop in the function:
183+
184+
.. code-block:: python
185+
186+
for angle in t:
187+
# Base circle points
188+
x = radius * cos(angle)
189+
y = radius * sin(angle)
190+
191+
# Add noise
192+
noise_x = uniform(-noise_factor, noise_factor)
193+
noise_y = uniform(-noise_factor, noise_factor)
194+
195+
noisy_x = x + noise_x
196+
noisy_y = y + noise_y
197+
198+
points.append((noisy_x, noisy_y))
199+
200+
The two optional parameters for the function are ``num_points`` and ``seed``. ``num_points`` is used to determine how many points make up the circle. If no value is given, it defaults to 100. ``seed`` is used to set the randomness of the circles. If a value is given, the random.seed is set to that value. For this model, seeds were not used.
201+
202+
5. **Changing Tertiary Branch Curves:**
203+
204+
Every tertiary branch on the model follows a unique curve. This curve is generated with the ``create_bezier_curve()`` function which can be found in the helper.py file. The function works by generating four points to be used as guide points for the Bézier curve. There is some control code to make sure that the x points are random but are still generated in a linear fashion.
205+
206+
The function takes four optional parameters: ``num_control_points``, ``x_range``, ``y_range``, and ``seed_val``. ``num_control_points`` defaults to four, the standard for Bézier curves. ``x_range`` and ``y_range`` default to a tuples designating the ranges: (0,10) and (-2,2) respectively. ``seed_val`` defaults to ``None``, however in the actual model ``time.time()`` is used as the seed.
12207

13-
The documentation is provided at https://treesim-lpy.readthedocs.io/en/latest/
14-
You can find the L-Py documentation at
15-
<https://lpy.readthedocs.io/en/latest>
208+
The actual designation of the curves for the tertiary branches can be seen in the ``bud(t)`` section:
209+
210+
.. code-block:: python
211+
212+
if 'NonTrunk' in new_object.name:
213+
import time
214+
curve = create_bezier_curve(seed_val=time.time())
215+
nproduce [SetGuide(curve, new_object.max_length)
216+
217+
A curve is generated with the ``create_bezier_curve()`` and it is used as the guide for the ``SetGuide`` function provided by L-Py.
218+
219+
6. **Changing color ID system:**
220+
221+
Every object on the tree has its own unique color code to act as an ID. The implementation of this can be seen in the ``grow_object(o)`` section:
222+
223+
.. code-block:: python
224+
225+
r, g, b = o.color
226+
nproduce SetColor(r, g, b)
227+
228+
smallest_color = [r, g, b].index(min([r, g, b]))
229+
o.color[smallest_color] += 1
230+
231+
This works by finding the smallest value of the objects RGB code and incrementing it by one. This is to avoid any kind of error where a color value is greater than 255. To get rid of the color IDs, simply comment out the second two lines of the code above.
232+
233+
234+
235+
7. **Changing Apple and Leaf ratio:**
236+
237+
The generation of apples and leaves is random. If something is to grow off of the bud, there is a 90% chance it will be a leaf, and a 10% chance it will be an apple. These percentages are set in the ``Spur`` class declaration:
238+
239+
.. code-block:: python
240+
241+
# From Spur class
242+
def create_branch(self):
243+
if self.num_leaves < self.max_leaves:
244+
self.num_leaves += 1
245+
if rd.random()<0.9:
246+
new_ob = Leaf(copy_from = self.prototype_dict['leaf'])
247+
else:
248+
new_ob = Apple(copy_from = self.prototype_dict['apple'])
249+
else:
250+
new_ob = None
251+
252+
return new_ob
253+
254+
255+
256+
257+
Features
258+
--------
259+
260+
- **Pruning:** Remove unwanted branches to simulate pruning.
261+
- **Branch Tying:** Simulate branches being tied down to mimic different orchard architectures.
262+
- **Model Class Types:** The model generated is built with classes of different material type.
16263
17264
========
18265
Gallery
19266
========
20-
.. figure:: media/envy.png
267+
.. figure:: media/envy_model.png
21268
:width: 500
22-
:height: 300
269+
:height: 500
23270
24271
Example of a labelled, pruned and tied envy tree system using TreeSim_Lpy
25272
@@ -30,15 +277,13 @@ Gallery
30277
:height: 300
31278
32279
Example of a labelled, pruned and tied UFO tree system using TreeSim_Lpy
33-
34280
35281
282+
Contact
283+
-------
36284
37-
=============
38-
Documentation
39-
=============
285+
For any questions or issues, please contact us through **GitHub Issues**.
40286
41-
Documentation is available at `<https://treesim-lpy.readthedocs.io/en/latest/>`_
42287
43288
Help and Support
44289
----------------

0 commit comments

Comments
 (0)