1.3.49
 
Loading...
Searching...
No Matches
Assets.cpp
Go to the documentation of this file.
1
16#include "PlantArchitecture.h"
17#include "global.h"
18
19using namespace helios;
20
21uint GenericLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
22
23 // If OBJ model file is specified, load it and return the object ID
24 if (!prototype_parameters->OBJ_model_file.empty()) {
25 // Use unified file resolution to validate OBJ file existence
26 try {
27 std::filesystem::path resolved_path = helios::resolveFilePath(prototype_parameters->OBJ_model_file);
28 (void) resolved_path; // Suppress unused variable warning
29 } catch (const std::runtime_error &) {
30 helios_runtime_error("ERROR (PlantArchitecture): Leaf prototype OBJ file " + prototype_parameters->OBJ_model_file + " does not exist.");
31 }
32 return context_ptr->addPolymeshObject(context_ptr->loadOBJ(prototype_parameters->OBJ_model_file.c_str(), prototype_parameters->leaf_offset, 0, nullrotation, RGB::black, "ZUP", true));
33 }
34
35 std::string leaf_texture;
36 if (prototype_parameters->leaf_texture_file.empty()) {
37 helios_runtime_error("ERROR (PlantArchitecture): Leaf prototype texture file was not specified.");
38 } else if (prototype_parameters->leaf_texture_file.size() == 1) {
39 leaf_texture = prototype_parameters->leaf_texture_file.begin()->second;
40 } else if (prototype_parameters->leaf_texture_file.find(compound_leaf_index) == prototype_parameters->leaf_texture_file.end()) {
41 helios_runtime_error("ERROR (PlantArchitecture): Leaf prototype texture file for compound leaf index " + std::to_string(compound_leaf_index) + " was not found.");
42 } else {
43 leaf_texture = prototype_parameters->leaf_texture_file[compound_leaf_index];
44 }
45
46 // -- main leaf generation code -- //
47
48 std::vector<uint> UUIDs;
49
50 uint Nx = prototype_parameters->subdivisions; // number of leaf subdivisions in the x-direction (longitudinal)
51 uint Ny = ceil(prototype_parameters->leaf_aspect_ratio.val() * float(Nx)); // number of leaf subdivisions in the y-direction (lateral)
52
53 if (Ny % 2 != 0) { // Ny must be even
54 Ny = Ny + 1;
55 }
56
57 const float dx = 1.f / float(Nx); // length of leaf subdivision in the x-direction
58 const float dy = prototype_parameters->leaf_aspect_ratio.val() / float(Ny); // length of leaf subdivision in the y-direction
59
60 std::vector<std::vector<vec3>> vertices;
61 resize_vector(vertices, Nx + 1, Ny + 1);
62
63 for (int j = 0; j <= Ny; j++) {
64 float dtheta = 0;
65 for (int i = 0; i <= Nx; i++) {
66
67 const float x = float(i) * dx; // x-coordinate of leaf subdivision
68 const float y = float(j) * dy - 0.5f * prototype_parameters->leaf_aspect_ratio.val(); // y-coordinate of leaf subdivision
69
70 // midrib leaf folding
71 const float y_fold = cosf(0.5f * prototype_parameters->midrib_fold_fraction.val() * M_PI) * y;
72 const float z_fold = sinf(0.5f * prototype_parameters->midrib_fold_fraction.val() * M_PI) * fabs(y);
73
74 // x-curvature
75 float z_xcurve = prototype_parameters->longitudinal_curvature.val() * powf(x, 4);
76
77 // y-curvature
78 float z_ycurve = prototype_parameters->lateral_curvature.val() * powf(y / prototype_parameters->leaf_aspect_ratio.val(), 4);
79
80 // petiole roll
81 float z_petiole = 0;
82 if (prototype_parameters->petiole_roll.val() != 0.0f) {
83 z_petiole = fmin(0.1f, prototype_parameters->petiole_roll.val() * powf(7.f * y / prototype_parameters->leaf_aspect_ratio.val(), 4) * exp(-70.f * (x))) -
84 0.01 * prototype_parameters->petiole_roll.val() / fabs(prototype_parameters->petiole_roll.val());
85 }
86
87 // vertical displacement for leaf wave at each of the four subdivision vertices
88 float z_wave = 0;
89 if (prototype_parameters->wave_period.val() > 0.0f && prototype_parameters->wave_amplitude.val() > 0.0f) {
90 z_wave = (2.f * fabs(y) * prototype_parameters->wave_amplitude.val() * sinf((x + 0.5f * float(j >= 0.5 * Ny)) * M_PI / prototype_parameters->wave_period.val()));
91 }
92
93 vertices.at(j).at(i) = make_vec3(x, y_fold, z_fold + z_ycurve + z_wave + z_petiole);
94
95 if (prototype_parameters->longitudinal_curvature.val() != 0.0f && i > 0) {
96 dtheta -= atan(4.f * prototype_parameters->longitudinal_curvature.val() * powf(x, 3) * dx);
97 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), nullorigin, make_vec3(0, 1, 0), dtheta);
98 }
99
100 if (prototype_parameters->leaf_buckle_angle.val() > 0) {
101 const float xf = prototype_parameters->leaf_buckle_length.val();
102 if (x <= prototype_parameters->leaf_buckle_length.val() && x + dx > prototype_parameters->leaf_buckle_length.val()) {
103 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), make_vec3(xf, 0, 0), make_vec3(0, 1, 0), 0.5f * deg2rad(prototype_parameters->leaf_buckle_angle.val()));
104 } else if (x + dx > prototype_parameters->leaf_buckle_length.val()) {
105 vertices.at(j).at(i) = rotatePointAboutLine(vertices.at(j).at(i), make_vec3(xf, 0, 0), make_vec3(0, 1, 0), deg2rad(prototype_parameters->leaf_buckle_angle.val()));
106 }
107 }
108 }
109 }
110
111 for (int j = 0; j < Ny; j++) {
112 for (int i = 0; i < Nx; i++) {
113
114 const float x = float(i) * dx;
115 const float y = float(j) * dy - 0.5f * prototype_parameters->leaf_aspect_ratio.val();
116 vec2 uv0(x, (y + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
117 vec2 uv1(x + dx, (y + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
118 vec2 uv2(x + dx, (y + dy + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
119 vec2 uv3(x, (y + dy + 0.5f * prototype_parameters->leaf_aspect_ratio.val()) / prototype_parameters->leaf_aspect_ratio.val());
120
121 vec3 v0 = vertices.at(j).at(i);
122 vec3 v1 = vertices.at(j).at(i + 1);
123 vec3 v2 = vertices.at(j + 1).at(i + 1);
124 vec3 v3 = vertices.at(j + 1).at(i);
125
126 // Add triangle 1 and check if it has effective area (including texture transparency)
127 uint uuid1 = context_ptr->addTriangle(v0, v1, v2, leaf_texture.c_str(), uv0, uv1, uv2);
128 if (context_ptr->getPrimitiveArea(uuid1) > 0) {
129 UUIDs.push_back(uuid1);
130 } else {
131 context_ptr->deletePrimitive(uuid1);
132 }
133
134 // Add triangle 2 and check if it has effective area (including texture transparency)
135 uint uuid2 = context_ptr->addTriangle(v0, v2, v3, leaf_texture.c_str(), uv0, uv2, uv3);
136 if (context_ptr->getPrimitiveArea(uuid2) > 0) {
137 UUIDs.push_back(uuid2);
138 } else {
139 context_ptr->deletePrimitive(uuid2);
140 }
141 }
142 }
143
144 context_ptr->translatePrimitive(UUIDs, prototype_parameters->leaf_offset);
145
146 if (prototype_parameters->build_petiolule) {
147 std::vector<uint> UUIDs_petiolule = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/PetiolulePrototype.obj", make_vec3(0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
148 context_ptr->translatePrimitive(UUIDs, make_vec3(0.07, 0, 0.005));
149 UUIDs.insert(UUIDs.end(), UUIDs_petiolule.begin(), UUIDs_petiolule.end());
150 }
151
152 prototype_parameters->leaf_aspect_ratio.resample();
153 prototype_parameters->midrib_fold_fraction.resample();
154 prototype_parameters->longitudinal_curvature.resample();
155 prototype_parameters->lateral_curvature.resample();
156 prototype_parameters->petiole_roll.resample();
157 prototype_parameters->wave_period.resample();
158 prototype_parameters->wave_amplitude.resample();
159 prototype_parameters->leaf_buckle_length.resample();
160 prototype_parameters->leaf_buckle_angle.resample();
161
162 return context_ptr->addPolymeshObject(UUIDs);
163}
164
165uint GeneralSphericalFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
166 return context_ptr->addSphereObject(5, make_vec3(0.5f, 0, 0), 0.5f, RGB::red);
167}
168
169uint AlmondFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
170 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/AlmondHull.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
171 uint objID = context_ptr->addPolymeshObject(UUIDs);
172 return objID;
173}
174
175uint AlmondFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
176 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/AlmondFlower.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
177 uint objID = context_ptr->addPolymeshObject(UUIDs);
178 return objID;
179}
180
181void AlmondPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
182
183 if (phytomer->internode_length_max < 0.01) { // spurs
184 phytomer->setInternodeMaxRadius(0.005);
185 phytomer->setVegetativeBudState(BUD_DEAD);
186 phytomer->scaleLeafPrototypeScale(0.8);
187 phytomer->setFloralBudState(BUD_DEAD);
188 phytomer->parent_shoot_ptr->shoot_parameters.max_nodes_per_season = 7;
189 }
190
191 // blind nodes
192 // if( shoot_node_index<3 ){
193 // phytomer->setVegetativeBudState( BUD_DEAD );
194 // phytomer->setFloralBudState( BUD_DEAD );
195 // }
196}
197
198void AlmondPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
199}
200
201uint AppleFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
202 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/AppleFruit.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
203 uint objID = context_ptr->addPolymeshObject(UUIDs);
204 return objID;
205}
206
207uint AppleFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
208 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/AlmondFlower.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
209 uint objID = context_ptr->addPolymeshObject(UUIDs);
210 return objID;
211}
212
213void ApplePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
214
215 if (phytomer->internode_length_max < 0.01) { // spurs
216 phytomer->setInternodeMaxRadius(0.005);
217 phytomer->setVegetativeBudState(BUD_DEAD);
218 phytomer->scaleLeafPrototypeScale(0.8);
219 phytomer->setFloralBudState(BUD_DEAD);
220 phytomer->parent_shoot_ptr->shoot_parameters.max_nodes_per_season = 6;
221 }
222}
223
224void ApplePhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
225}
226
227uint AsparagusLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
228
229 float curve_magnitude = context_ptr->randu(0.f, 0.2f);
230
231 std::vector<vec3> nodes;
232 nodes.push_back(make_vec3(0, 0, 0));
233 nodes.push_back(make_vec3(context_ptr->randu(0.4f, 0.7f), 0, -0.25f * curve_magnitude));
234 nodes.push_back(make_vec3(0.95, 0, -0.9f * curve_magnitude));
235 nodes.push_back(make_vec3(1, 0, -curve_magnitude));
236
237 std::vector<float> radius;
238 radius.push_back(0.015);
239 radius.push_back(0.015);
240 radius.push_back(0.015);
241 radius.push_back(0.0);
242
243 std::vector<RGBcolor> colors;
244 colors.push_back(RGB::forestgreen);
245 colors.push_back(RGB::forestgreen);
246 colors.push_back(RGB::forestgreen);
247 colors.push_back(RGB::forestgreen);
248
249 uint objID = context_ptr->addTubeObject(8, nodes, radius, colors);
250 context_ptr->rotateObject(objID, context_ptr->randu(0, 2.f * M_PI), "x");
251 return objID;
252}
253
254void AsparagusPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
255
256 // blind nodes
257 if (shoot_node_index <= 2) {
258 phytomer->scaleLeafPrototypeScale(0.6);
259 phytomer->setVegetativeBudState(BUD_DEAD);
260 }
261}
262
263uint BeanLeafPrototype_unifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
264 std::vector<uint> UUIDs;
265 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanLeaf_unifoliate.obj", true);
266
267 uint objID = context_ptr->addPolymeshObject(UUIDs);
268 return objID;
269}
270
271uint BeanLeafPrototype_trifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
272 std::vector<uint> UUIDs;
273 if (compound_leaf_index == 0) {
274 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanLeaf_tip.obj", true);
275 } else if (compound_leaf_index < 0) {
276 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanLeaf_left.obj", true);
277 } else {
278 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanLeaf_right.obj", true);
279 }
280 uint objID = context_ptr->addPolymeshObject(UUIDs);
281 return objID;
282}
283
284uint BeanFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
285 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanPod.obj", true);
286 uint objID = context_ptr->addPolymeshObject(UUIDs);
287 return objID;
288}
289
290uint BeanFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
291 std::vector<uint> UUIDs;
292 if (flower_is_open) {
293 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanFlower_open_white.obj", true);
294 } else {
295 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanFlower_closed_white.obj", true);
296 }
297 uint objID = context_ptr->addPolymeshObject(UUIDs);
298 return objID;
299}
300
301void BeanPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
302
303 if (shoot_node_index > 5 || phytomer->rank > 1) {
304 phytomer->setVegetativeBudState(BUD_DEAD);
305 } else {
306 phytomer->setFloralBudState(BUD_DEAD);
307 }
308
309 // set leaf and internode scale based on position along the shoot
310 float leaf_scale = fmin(1.f, 0.6 + 0.4 * plant_age / 8.f);
311 phytomer->scaleLeafPrototypeScale(leaf_scale);
312
313 // set internode length based on position along the shoot
314 if (phytomer->rank == 0) {
315 float inode_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 10.f);
316 phytomer->scaleInternodeMaxLength(inode_scale);
317 }
318}
319
320uint BindweedFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
321 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BindweedFlower.obj", true);
322 uint objID = context_ptr->addPolymeshObject(UUIDs);
323 return objID;
324}
325
326uint CapsicumFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
327 std::string OBJ_file;
328 if (context_ptr->randn() < 0.4) {
329 OBJ_file = "CapsicumFruit_green.obj";
330 } else {
331 OBJ_file = "CapsicumFruit_red.obj";
332 }
333
334 std::vector<uint> UUIDs = context_ptr->loadOBJ(OBJ_file.c_str(), make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
335 return context_ptr->addPolymeshObject(UUIDs);
336}
337
338void CapsicumPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
339
340 if (shoot_node_index < 6 && phytomer->rank == 0) {
341 phytomer->setVegetativeBudState(BUD_DEAD);
342 phytomer->setFloralBudState(BUD_DEAD);
343 phytomer->removeLeaf();
344 }
345
346 if (phytomer->rank >= 2) {
347 phytomer->setVegetativeBudState(BUD_DEAD);
348 phytomer->setFloralBudState(BUD_DEAD);
349 }
350
351 // set leaf and internode scale based on position along the shoot
352 float leaf_scale = std::min(1.f, 0.6f + 0.4f * shoot_node_index / 5.f);
353 phytomer->scaleLeafPrototypeScale(leaf_scale);
354
355 // set internode length based on position along the shoot
356 if (phytomer->rank == 0) {
357 float inode_scale = std::min(1.f, 0.05f + 0.95f * plant_age / 15.f);
358 phytomer->scaleInternodeMaxLength(inode_scale);
359 }
360}
361
362uint CheeseweedLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
363 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CheeseweedLeaf.obj", true);
364 uint objID = context_ptr->addPolymeshObject(UUIDs);
365 return objID;
366}
367
368uint CowpeaLeafPrototype_unifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
369 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaLeaf_unifoliate.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
370
371 uint objID = context_ptr->addPolymeshObject(UUIDs);
372 return objID;
373}
374
375uint CowpeaLeafPrototype_trifoliate_OBJ(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
376 std::vector<uint> UUIDs;
377 if (compound_leaf_index < 0) {
378 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaLeaf_left_highres.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
379 } else if (compound_leaf_index == 0) {
380 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaLeaf_tip_highres.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
381 } else {
382 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaLeaf_right_highres.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
383 }
384 uint objID = context_ptr->addPolymeshObject(UUIDs);
385 return objID;
386}
387
388uint CowpeaFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
389 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaPod.obj", make_vec3(0., 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
390 uint objID = context_ptr->addPolymeshObject(UUIDs);
391 return objID;
392}
393
394uint CowpeaFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
395 std::vector<uint> UUIDs;
396 if (flower_is_open) {
397 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaFlower_open_yellow.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
398 } else {
399 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/CowpeaFlower_closed_yellow.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
400 }
401 uint objID = context_ptr->addPolymeshObject(UUIDs);
402 return objID;
403}
404
405void CowpeaPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
406
407 if (shoot_node_index > 5 || phytomer->rank > 1) {
408 phytomer->setVegetativeBudState(BUD_DEAD);
409 } else {
410 phytomer->setFloralBudState(BUD_DEAD);
411 }
412
413 // set leaf and internode scale based on position along the shoot
414 float leaf_scale = fmin(1.f, 0.6 + 0.4 * plant_age / 8.f);
415 phytomer->scaleLeafPrototypeScale(leaf_scale);
416
417 // set internode length based on position along the shoot
418 if (phytomer->rank == 0) {
419 float inode_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 10.f);
420 phytomer->scaleInternodeMaxLength(inode_scale);
421 }
422}
423
424// Function to generate random float between min and max
425float random_float(float min, float max) {
426 return min + static_cast<float>(rand()) / (static_cast<float>(RAND_MAX / (max - min)));
427}
428
429// Function to check if two spheres overlap
430bool spheres_overlap(const helios::vec3 &center1, float radius1, const helios::vec3 &center2, float radius2) {
431 float distance = std::sqrt(std::pow(center1.x - center2.x, 2) + std::pow(center1.y - center2.y, 2) + std::pow(center1.z - center2.z, 2));
432 return distance < (radius1 + radius2);
433}
434
435uint GrapevineFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
436
437 int num_grapes = 60;
438 float height = 5.0f; // Height of the cluster
439 float base_radius = 2.f; // Base radius of the cluster
440 float taper_factor = 0.6f; // Taper factor (higher means more taper)
441 float grape_radius = 0.25f; // Fixed radius for each grape
442
443 std::vector<std::pair<helios::vec3, float>> grapes;
444 float z_step = height / num_grapes;
445
446 // Place the first grape at the bottom center
447 helios::vec3 first_center(0.0f, 0.0f, 0.0f);
448 grapes.push_back({first_center, grape_radius});
449
450 // Attempt to place each subsequent grape close to an existing grape
451 int max_attempts = 100; // Number of retries to find a tight fit
452
453 for (int i = 1; i < num_grapes; ++i) {
454 float z = i * z_step;
455 // Tapered radius based on height (denser at the top, sparser at the bottom)
456 float taper_radius = base_radius * (1.0f - taper_factor * (z / height));
457
458 bool placed = false;
459 int attempts = 0;
460 while (!placed && attempts < max_attempts) {
461 // Randomly select an existing grape as the reference point
462 int reference_idx = rand() % grapes.size();
463 const helios::vec3 &reference_center = grapes[reference_idx].first;
464
465 // Pick a random offset direction from the reference grape
466 float angle = random_float(0, 2 * M_PI);
467 float distance = random_float(1.2 * grape_radius, 1.3 * grape_radius); // Keep grapes close but not overlapping
468
469 // Compute the new potential center for the grape
470 helios::vec3 new_center(reference_center.x + distance * cos(angle), reference_center.y + distance * sin(angle), random_float(z - 0.5f * z_step, z + 0.5f * z_step));
471
472 // Check that the new center is within the allowable radius (for tapering)
473 float new_center_distance = std::sqrt(new_center.x * new_center.x + new_center.y * new_center.y);
474 if (new_center_distance > taper_radius) {
475 attempts++;
476 continue; // Skip if the new position exceeds the tapered radius
477 }
478
479 // Check for collisions with existing grapes
480 bool collision = false;
481 for (const auto &grape: grapes) {
482 if (spheres_overlap(new_center, grape_radius, grape.first, grape.second)) {
483 collision = true;
484 break;
485 }
486 }
487
488 // If no collision, place the grape
489 if (!collision) {
490 grapes.push_back({new_center, grape_radius});
491 placed = true;
492 }
493
494 attempts++;
495 }
496 }
497
498 std::vector<uint> UUIDs;
499 for (const auto &grape: grapes) {
500 // std::vector<uint> UUIDs_tmp = context_ptr->addSphere( 10, grape.first, grape.second, "../../../plugins/plantarchitecture/assets/textures/GrapeBerry.jpg" );
501 std::vector<uint> UUIDs_tmp = context_ptr->addSphere(10, grape.first, grape.second, make_RGBcolor(0.053, 0.076, 0.098));
502 UUIDs.insert(UUIDs.end(), UUIDs_tmp.begin(), UUIDs_tmp.end());
503 }
504
505 context_ptr->rotatePrimitive(UUIDs, 0.5 * M_PI, "y");
506
507 context_ptr->setPrimitiveData(UUIDs, "object_label", "fruit");
508
509 uint objID = context_ptr->addPolymeshObject(UUIDs);
510 return objID;
511}
512
513// uint GrapevineFlowerPrototype( helios::Context* context_ptr, uint subdivisions, bool flower_is_open ){
514// std::vector<uint> UUIDs = context_ptr->loadOBJ( "plugins/plantarchitecture/assets/obj/OliveFlower_open.obj", make_vec3(0.0,0,0), 0,nullrotation, RGB::black, "ZUP", true );
515// uint objID = context_ptr->addPolymeshObject( UUIDs );
516// return objID;
517// }
518
519void GrapevinePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
520
521 // blind nodes
522 if (shoot_node_index >= 2) {
523 phytomer->setFloralBudState(BUD_DEAD);
524 }
525 if (phytomer->rank >= 1 && shoot_node_index >= 8) {
526 phytomer->setVegetativeBudState(BUD_DEAD);
527 }
528 if (phytomer->rank >= 2) {
529 phytomer->setVegetativeBudState(BUD_DEAD);
530 phytomer->setFloralBudState(BUD_DEAD);
531 }
532}
533
534// void GrapevinePhytomerCallbackFunction( std::shared_ptr<Phytomer> phytomer ){
535//
536// if( phytomer->isdormant ){
537// if( phytomer->shoot_index.x >= phytomer->shoot_index.y-1 ){
538// phytomer->setVegetativeBudState( BUD_DORMANT ); //first vegetative buds always break
539// }
540// if( phytomer->shoot_index.x <= phytomer->shoot_index.y-4 ){
541// phytomer->setFloralBudState( BUD_DORMANT ); //first vegetative buds always break
542// }
543// }
544//
545// }
546
547uint MaizeTasselPrototype(helios::Context *context_ptr, uint subdivisions) {
548
549 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/MaizeTassel.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
550 return context_ptr->addPolymeshObject(UUIDs);
551}
552
553uint MaizeEarPrototype(helios::Context *context_ptr, uint subdivisions) {
554
555 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/MaizeEar.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
556 return context_ptr->addPolymeshObject(UUIDs);
557}
558
559void MaizePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
560
561 // set leaf scale based on position along the shoot
562 float scale;
563 if (shoot_node_index <= 5) {
564 scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
565 phytomer->scaleInternodeMaxLength(scale);
566 } else if (shoot_node_index >= phytomer->shoot_index.z - 5) {
567 scale = fmin(1.f, 0.65 + 0.35 * float(phytomer->shoot_index.z - shoot_node_index) / 3.f);
568 }
569
570 phytomer->scaleLeafPrototypeScale(scale);
571
572 if (shoot_node_index > 8 && shoot_node_index < 12) {
573 phytomer->phytomer_parameters.inflorescence.flowers_per_peduncle = 1;
574 phytomer->phytomer_parameters.inflorescence.fruit_prototype_function = MaizeEarPrototype;
575 phytomer->phytomer_parameters.inflorescence.fruit_prototype_scale = 0.2;
576 phytomer->phytomer_parameters.peduncle.length = 0.05f;
577 phytomer->phytomer_parameters.peduncle.radius = 0.01;
578 phytomer->phytomer_parameters.peduncle.pitch = 5;
579 phytomer->setFloralBudState(BUD_ACTIVE);
580 } else {
581 phytomer->phytomer_parameters.inflorescence.fruit_prototype_function = MaizeTasselPrototype;
582 phytomer->setFloralBudState(BUD_DEAD);
583 }
584
585 // phytomer->setFloralBudState( BUD_DEAD );
586}
587
588uint OliveLeafPrototype(helios::Context *context_ptr, LeafPrototype *prototype_parameters, int compound_leaf_index) {
589
590 std::vector<uint> UUIDs_upper =
591 context_ptr->addTile(make_vec3(0.5, 0, 0), make_vec2(1, 0.2), nullrotation, make_int2(prototype_parameters->subdivisions, prototype_parameters->subdivisions), "plugins/plantarchitecture/assets/textures/OliveLeaf_upper.png");
592 std::vector<uint> UUIDs_lower =
593 context_ptr->addTile(make_vec3(0.5, 0, -1e-4), make_vec2(1, 0.2), nullrotation, make_int2(prototype_parameters->subdivisions, prototype_parameters->subdivisions), "plugins/plantarchitecture/assets/textures/OliveLeaf_lower.png");
594 context_ptr->rotatePrimitive(UUIDs_lower, M_PI, "x");
595
596 UUIDs_upper.insert(UUIDs_upper.end(), UUIDs_lower.begin(), UUIDs_lower.end());
597 uint objID = context_ptr->addPolymeshObject(UUIDs_upper);
598 return objID;
599}
600
601uint OliveFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
602 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/OliveFruit.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
603 uint objID = context_ptr->addPolymeshObject(UUIDs);
604 std::vector<uint> UUIDs_fruit = context_ptr->filterPrimitivesByData(context_ptr->getObjectPrimitiveUUIDs(objID), "object_label", "fruit");
605 context_ptr->setPrimitiveColor(UUIDs_fruit, make_RGBcolor(0.65, 0.7, 0.4)); // green
606 return objID;
607}
608
609uint OliveFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
610 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/OliveFlower_open.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
611 uint objID = context_ptr->addPolymeshObject(UUIDs);
612 return objID;
613}
614
615void OlivePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
616}
617
618void OlivePhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
619
620 if (phytomer->isdormant) {
621 if (phytomer->shoot_index.x < phytomer->shoot_index.y - 8) {
622 phytomer->setFloralBudState(BUD_DEAD);
623 }
624 }
625}
626
627uint PistachioFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
628 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/PistachioNut.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
629 uint objID = context_ptr->addPolymeshObject(UUIDs);
630 return objID;
631}
632
633uint PistachioFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
634 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/OliveFlower_open.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
635 uint objID = context_ptr->addPolymeshObject(UUIDs);
636 return objID;
637}
638
639void PistachioPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
640
641 // blind nodes
642 if (shoot_node_index == 0) {
643 phytomer->setVegetativeBudState(BUD_DEAD);
644 phytomer->setFloralBudState(BUD_DEAD);
645 }
646}
647
648void PistachioPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
649
650 if (phytomer->isdormant) {
651 if (phytomer->shoot_index.x <= phytomer->shoot_index.y - 4) {
652 phytomer->setFloralBudState(BUD_DORMANT);
653 }
654 }
655}
656
657uint PuncturevineFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
658 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/PuncturevineFlower.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
659 uint objID = context_ptr->addPolymeshObject(UUIDs);
660 return objID;
661}
662
663uint RedbudFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
664 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/RedbudFlower_open.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
665 return context_ptr->addPolymeshObject(UUIDs);
666}
667
668uint RedbudFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
669 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/RedbudPod.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
670 return context_ptr->addPolymeshObject(UUIDs);
671}
672
673void RedbudPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
674}
675
676void RedbudPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
677
678 // redbud has the shoot pattern that the first few nodes on the shoot are vegetative, then the rest are floral
679 if (phytomer->isdormant) {
680 int Nchild_shoots = randu(2, 4);
681 if (phytomer->shoot_index.x < phytomer->shoot_index.y - Nchild_shoots) {
682 phytomer->setVegetativeBudState(BUD_DEAD);
683 } else {
684 phytomer->setFloralBudState(BUD_DEAD);
685 }
686 }
687}
688
689uint RiceSpikePrototype(helios::Context *context_ptr, uint subdivisions) {
690 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/RiceGrain.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
691 uint objID = context_ptr->addPolymeshObject(UUIDs);
692 return objID;
693}
694
695void RicePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
696
697 // set leaf scale based on position along the shoot
698 float scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
699 phytomer->scaleLeafPrototypeScale(scale);
700
701 // set internode length based on position along the shoot
702 phytomer->scaleInternodeMaxLength(scale);
703}
704
705void ButterLettucePhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
706
707 float fact = float(shoot_max_nodes - shoot_node_index) / float(shoot_max_nodes);
708
709 // set leaf scale based on position along the shoot
710 // float scale = fmin(1.f, 1 + 0.1*fact);
711 // phytomer->scaleLeafPrototypeScale(scale);
712
713 // phytomer->rotateLeaf( 0, 0, make_AxisRotation(-deg2rad(15)*fact, 0, 0));
714 phytomer->rotateLeaf(0, 0, make_AxisRotation(-deg2rad(60) * fact, 0, 0));
715}
716
717uint SorghumPaniclePrototype(helios::Context *context_ptr, uint subdivisions) {
718
719 if (subdivisions <= 1) {
720 subdivisions = 3;
721 }
722
723 float panicle_height = 1;
724 float panicle_width = 0.08;
725 float width_seed = 0.08;
726 float height_seed = 0.25;
727 float seed_tilt = 50;
728 subdivisions = 6;
729
730 std::string seed_texture_file = "plugins/plantarchitecture/assets/textures/SorghumSeed.jpeg";
731 RGBcolor stem_color(0.45, 0.55, 0.42);
732
733 std::vector<uint> UUIDs;
734
735 panicle_height -= 0.8 * height_seed;
736
737 std::vector<vec3> nodes_panicle;
738 std::vector<float> radius_panicle;
739
740 for (int n = 0; n < subdivisions; n++) {
741 float x = 0;
742 float y = 0;
743 float z;
744 if (n == 0) {
745 z = 0.5f * height_seed / float(subdivisions - 1);
746 } else if (n == subdivisions - 1) {
747 z = (subdivisions - 1.5f) * height_seed / float(subdivisions - 1);
748 } else {
749 z = n * height_seed / float(subdivisions - 1);
750 }
751
752 float angle = float(n) * M_PI / float(subdivisions - 1);
753 float dr = std::fmax(0.f, 0.5f * width_seed * sin(angle));
754
755 nodes_panicle.push_back(make_vec3(x, y, z));
756 radius_panicle.push_back(dr);
757 }
758
759 std::vector<uint> UUIDs_seed_ptype = context_ptr->addTube(subdivisions, nodes_panicle, radius_panicle, seed_texture_file.c_str());
760
761 int Ntheta = ceil(6.f * panicle_height / height_seed);
762 int Nphi = ceil(2.f * M_PI * panicle_width / width_seed);
763
764 for (int j = 0; j < Nphi; j++) {
765 for (int i = 0; i < Ntheta; i++) {
766
767 if (i == 0 && j == 0) {
768 continue;
769 }
770
771 std::vector<uint> UUIDs_copy = context_ptr->copyPrimitive(UUIDs_seed_ptype);
772 context_ptr->scalePrimitive(UUIDs_copy, make_vec3(1, 1, 1) * context_ptr->randu(0.9f, 1.1f));
773
774 float phi = 2.f * M_PI * float(j + 0.5f * float(i % 2)) / float(Nphi);
775 float theta = acos(1 - 2 * float(i + float(j) / float(Nphi)) / float(Ntheta));
776 float x = sin(theta) * cos(phi);
777 float y = sin(theta) * sin(phi);
778 float z = 0.5f + 0.5f * cos(theta);
779
780 x *= 0.5f * panicle_width;
781 y *= 0.5f * panicle_width;
782 z *= panicle_height;
783
784 float tilt = -deg2rad(seed_tilt) * sqrtf(1.f - z / panicle_height);
785
786 context_ptr->rotatePrimitive(UUIDs_copy, tilt, "x");
787 context_ptr->rotatePrimitive(UUIDs_copy, phi - 0.5f * M_PI, "z");
788
789 context_ptr->translatePrimitive(UUIDs_copy, make_vec3(x, y, z));
790 UUIDs.insert(UUIDs.end(), UUIDs_copy.begin(), UUIDs_copy.end());
791 }
792 }
793
794 context_ptr->deletePrimitive(UUIDs_seed_ptype);
795
796 std::vector<uint> UUIDs_sphere = context_ptr->addSphere(10, make_vec3(0, 0, 0.5 * panicle_height), 0.5f, seed_texture_file.c_str());
797 context_ptr->scalePrimitiveAboutPoint(UUIDs_sphere, make_vec3(1.9 * panicle_width, 1.9 * panicle_width, 0.8 * panicle_height), make_vec3(0, 0, 0.5 * panicle_height));
798 UUIDs.insert(UUIDs.end(), UUIDs_sphere.begin(), UUIDs_sphere.end());
799
800 context_ptr->rotatePrimitive(UUIDs, 0.5f * M_PI, "y");
801 context_ptr->translatePrimitive(UUIDs, make_vec3(-0.2, 0, 0));
802
803 uint objID = context_ptr->addPolymeshObject(UUIDs);
804 return objID;
805}
806
807void SorghumPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
808
809 // set leaf scale based on position along the shoot
810 float scale = fmin(1.f, 0.7 + 0.3 * float(shoot_node_index) / 5.f);
811 phytomer->scaleLeafPrototypeScale(scale);
812
813 // set internode length based on position along the shoot
814 phytomer->scaleInternodeMaxLength(scale);
815}
816
817uint SoybeanFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
818 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/SoybeanPod.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
819 uint objID = context_ptr->addPolymeshObject(UUIDs);
820 return objID;
821}
822
823uint SoybeanFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
824 std::vector<uint> UUIDs;
825 if (flower_is_open) {
826 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/SoybeanFlower_open_white.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
827 } else {
828 UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/BeanFlower_closed_white.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
829 }
830 uint objID = context_ptr->addPolymeshObject(UUIDs);
831 return objID;
832}
833
834void SoybeanPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
835
836 if (shoot_node_index > 5 || phytomer->rank > 1) {
837 phytomer->setVegetativeBudState(BUD_DEAD);
838 } else {
839 phytomer->setFloralBudState(BUD_DEAD);
840 }
841
842 // set leaf and internode scale based on position along the shoot
843 float leaf_scale = fmin(1.f, 0.2 + 0.8 * plant_age / 15.f);
844 phytomer->scaleLeafPrototypeScale(leaf_scale);
845
846 // set internode length based on position along the shoot
847 float inode_scale = fmin(1.f, 0.1 + 0.9 * plant_age / 15.f);
848 phytomer->scaleInternodeMaxLength(inode_scale);
849}
850
851uint StrawberryFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
852 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/StrawberryFlower.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
853 uint objID = context_ptr->addPolymeshObject(UUIDs);
854 return objID;
855}
856
857uint StrawberryFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
858 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/StrawberryFruit.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
859 uint objID = context_ptr->addPolymeshObject(UUIDs);
860 return objID;
861}
862
863uint TomatoFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
864 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/TomatoFruit.obj", make_vec3(0., 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
865 uint objID = context_ptr->addPolymeshObject(UUIDs);
866 return objID;
867}
868
869uint TomatoFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
870 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/TomatoFlower.obj", make_vec3(0.0, 0, 0), 0.75, nullrotation, RGB::black, "ZUP", true);
871 uint objID = context_ptr->addPolymeshObject(UUIDs);
872 return objID;
873}
874
875void TomatoPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
876
877 if (shoot_node_index < 8 && phytomer->rank == 0) {
878 phytomer->setFloralBudState(BUD_DEAD);
879 }
880 if (phytomer->rank > 1) {
881 phytomer->setFloralBudState(BUD_DEAD);
882 phytomer->setVegetativeBudState(BUD_DEAD);
883 }
884 if (phytomer->rank > 1) {
885 phytomer->setFloralBudState(BUD_DEAD);
886 phytomer->setVegetativeBudState(BUD_DEAD);
887 }
888
889 // set leaf and internode scale based on position along the shoot
890 float leaf_scale = fmin(1.f, 0.5 + 0.5 * plant_age / 10.f);
891 phytomer->scaleLeafPrototypeScale(leaf_scale);
892
893 // set internode length based on position along the shoot
894 float inode_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 10.f);
895 phytomer->scaleInternodeMaxLength(inode_scale);
896}
897
898void CherryTomatoPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
899
900 if (shoot_node_index < 8 || phytomer->rank > 1) {
901 phytomer->setFloralBudState(BUD_DEAD);
902 phytomer->setVegetativeBudState(BUD_DEAD);
903 }
904
905 // set leaf and internode scale based on position along the shoot
906 float leaf_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 15.f);
907 phytomer->scaleLeafPrototypeScale(leaf_scale);
908
909 // set internode length based on position along the shoot
910 float inode_scale = fmin(1.f, 0.7 + 0.3 * plant_age / 10.f);
911 phytomer->scaleInternodeMaxLength(inode_scale);
912}
913
914void CherryTomatoPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
915
916 float pruning_height = 1.f;
917 float pruning_day = 101.f;
918
919 float plant_age = phytomer->parent_shoot_ptr->plantarchitecture_ptr->getPlantAge(phytomer->plantID);
920
921 if (phytomer->hasLeaf() && plant_age >= pruning_day) {
922 float height = phytomer->getInternodeNodePositions().at(0).z;
923 if (height < pruning_height) {
924 phytomer->removeLeaf();
925 }
926 }
927}
928
929uint WalnutFruitPrototype(helios::Context *context_ptr, uint subdivisions) {
930 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/WalnutHull.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
931 uint objID = context_ptr->addPolymeshObject(UUIDs);
932 return objID;
933}
934
935uint WalnutFlowerPrototype(helios::Context *context_ptr, uint subdivisions, bool flower_is_open) {
936 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/AlmondFlower.obj", make_vec3(0.0, 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
937 uint objID = context_ptr->addPolymeshObject(UUIDs);
938 return objID;
939}
940
941void WalnutPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
942
943 // blind nodes
944 if (shoot_node_index < 4) {
945 phytomer->setVegetativeBudState(BUD_DEAD);
946 phytomer->setFloralBudState(BUD_DEAD);
947 }
948}
949
950void WalnutPhytomerCallbackFunction(std::shared_ptr<Phytomer> phytomer) {
951}
952
953uint WheatSpikePrototype(helios::Context *context_ptr, uint subdivisions) {
954 std::vector<uint> UUIDs = context_ptr->loadOBJ("plugins/plantarchitecture/assets/obj/WheatSpike.obj", make_vec3(0., 0, 0), 0, nullrotation, RGB::black, "ZUP", true);
955 uint objID = context_ptr->addPolymeshObject(UUIDs);
956 return objID;
957}
958
959void WheatPhytomerCreationFunction(std::shared_ptr<Phytomer> phytomer, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) {
960
961 // set leaf scale based on position along the shoot
962 float scale = std::fmin(1.f, 0.7f + 0.3f * float(shoot_node_index) / 5.f);
963 phytomer->scaleLeafPrototypeScale(scale);
964
965 // set internode length based on position along the shoot
966 phytomer->scaleInternodeMaxLength(scale);
967}