1.3.64
 
Loading...
Searching...
No Matches
PlantLibrary.cpp
Go to the documentation of this file.
1
16#include "CollisionDetection.h"
17#include "PlantArchitecture.h"
18
19using namespace helios;
20
21float PlantArchitecture::getParameterValue(const std::map<std::string, float> &build_parameters, const std::string &parameter_name, float default_value, float min_value, float max_value, const std::string &parameter_description) const {
22
23 // Check if parameter is specified in the map
24 auto it = build_parameters.find(parameter_name);
25 if (it == build_parameters.end()) {
26 // Parameter not specified, use default
27 return default_value;
28 }
29
30 float value = it->second;
31
32 // Validate parameter is within valid range
33 if (value < min_value || value > max_value) {
34 helios_runtime_error("ERROR (PlantArchitecture::getParameterValue): build parameter '" + parameter_name + "' (" + parameter_description + ") has value " + std::to_string(value) + " which is outside the valid range [" +
35 std::to_string(min_value) + ", " + std::to_string(max_value) + "].");
36 }
37
38 return value;
39}
40
41void PlantArchitecture::initializePlantModelRegistrations() {
42 // Register all available plant models
43 registerPlantModel("almond", [this]() { initializeAlmondTreeShoots(); }, [this](const helios::vec3 &pos) { return buildAlmondTree(pos); }, "tree");
44
45 registerPlantModel("almond_aldrich", [this]() { initializeAlmondTreeAldrichShoots(); }, [this](const helios::vec3 &pos) { return buildAlmondTreeAldrich(pos); }, "tree");
46
47 registerPlantModel("almond_wood_colony", [this]() { initializeAlmondTreeWoodColonyShoots(); }, [this](const helios::vec3 &pos) { return buildAlmondTreeWoodColony(pos); }, "tree");
48
49 registerPlantModel("apple", [this]() { initializeAppleTreeShoots(); }, [this](const helios::vec3 &pos) { return buildAppleTree(pos); }, "tree");
50
51 registerPlantModel("apple_fruitingwall", [this]() { initializeAppleFruitingWallShoots(); }, [this](const helios::vec3 &pos) { return buildAppleFruitingWall(pos); }, "tree");
52
53 registerPlantModel("asparagus", [this]() { initializeAsparagusShoots(); }, [this](const helios::vec3 &pos) { return buildAsparagusPlant(pos); });
54
55 registerPlantModel("bindweed", [this]() { initializeBindweedShoots(); }, [this](const helios::vec3 &pos) { return buildBindweedPlant(pos); }, "weed");
56
57 registerPlantModel("bean", [this]() { initializeBeanShoots(); }, [this](const helios::vec3 &pos) { return buildBeanPlant(pos); });
58
59 registerPlantModel("bougainvillea", [this]() { initializeBougainvilleaShoots(); }, [this](const helios::vec3 &pos) { return buildBougainvilleaPlant(pos); });
60
61 registerPlantModel("capsicum", [this]() { initializeCapsicumShoots(); }, [this](const helios::vec3 &pos) { return buildCapsicumPlant(pos); });
62
63 registerPlantModel("cheeseweed", [this]() { initializeCheeseweedShoots(); }, [this](const helios::vec3 &pos) { return buildCheeseweedPlant(pos); }, "weed");
64
65 registerPlantModel("cowpea", [this]() { initializeCowpeaShoots(); }, [this](const helios::vec3 &pos) { return buildCowpeaPlant(pos); });
66
67 registerPlantModel("grapevine_VSP", [this]() { initializeGrapevineVSPShoots(); }, [this](const helios::vec3 &pos) { return buildGrapevineVSP(pos); });
68
69 registerPlantModel("grapevine_Wye", [this]() { initializeGrapevineWyeShoots(); }, [this](const helios::vec3 &pos) { return buildGrapevineWye(pos); });
70
71 registerPlantModel("groundcherryweed", [this]() { initializeGroundCherryWeedShoots(); }, [this](const helios::vec3 &pos) { return buildGroundCherryWeedPlant(pos); }, "weed");
72
73 registerPlantModel("maize", [this]() { initializeMaizeShoots(); }, [this](const helios::vec3 &pos) { return buildMaizePlant(pos); });
74
75 registerPlantModel("olive", [this]() { initializeOliveTreeShoots(); }, [this](const helios::vec3 &pos) { return buildOliveTree(pos); }, "tree");
76
77 registerPlantModel("pistachio", [this]() { initializePistachioTreeShoots(); }, [this](const helios::vec3 &pos) { return buildPistachioTree(pos); }, "tree");
78
79 registerPlantModel("puncturevine", [this]() { initializePuncturevineShoots(); }, [this](const helios::vec3 &pos) { return buildPuncturevinePlant(pos); }, "weed");
80
81 registerPlantModel("easternredbud", [this]() { initializeEasternRedbudShoots(); }, [this](const helios::vec3 &pos) { return buildEasternRedbudPlant(pos); }, "tree");
82
83 registerPlantModel("rice", [this]() { initializeRiceShoots(); }, [this](const helios::vec3 &pos) { return buildRicePlant(pos); });
84
85 registerPlantModel("butterlettuce", [this]() { initializeButterLettuceShoots(); }, [this](const helios::vec3 &pos) { return buildButterLettucePlant(pos); });
86
87 registerPlantModel("sorghum", [this]() { initializeSorghumShoots(); }, [this](const helios::vec3 &pos) { return buildSorghumPlant(pos); });
88
89 registerPlantModel("soybean", [this]() { initializeSoybeanShoots(); }, [this](const helios::vec3 &pos) { return buildSoybeanPlant(pos); });
90
91 registerPlantModel("strawberry", [this]() { initializeStrawberryShoots(); }, [this](const helios::vec3 &pos) { return buildStrawberryPlant(pos); });
92
93 registerPlantModel("sugarbeet", [this]() { initializeSugarbeetShoots(); }, [this](const helios::vec3 &pos) { return buildSugarbeetPlant(pos); });
94
95 registerPlantModel("tomato", [this]() { initializeTomatoShoots(); }, [this](const helios::vec3 &pos) { return buildTomatoPlant(pos); });
96
97 registerPlantModel("cherrytomato", [this]() { initializeCherryTomatoShoots(); }, [this](const helios::vec3 &pos) { return buildCherryTomatoPlant(pos); });
98
99 registerPlantModel("walnut", [this]() { initializeWalnutTreeShoots(); }, [this](const helios::vec3 &pos) { return buildWalnutTree(pos); }, "tree");
100
101 registerPlantModel("wheat", [this]() { initializeWheatShoots(); }, [this](const helios::vec3 &pos) { return buildWheatPlant(pos); });
102}
103
104void PlantArchitecture::loadPlantModelFromLibrary(const std::string &plant_label) {
105
106 current_plant_model = plant_label;
107 initializeDefaultShoots(plant_label);
108}
109
110std::vector<std::string> PlantArchitecture::getAvailablePlantModels() const {
111 std::vector<std::string> models;
112 models.reserve(shoot_initializers.size());
113 for (const auto &pair: shoot_initializers) {
114 models.push_back(pair.first);
115 }
116 return models;
117}
118
119uint PlantArchitecture::buildPlantInstanceFromLibrary(const helios::vec3 &base_position, float age, const std::map<std::string, float> &build_parameters) {
120
121 if (current_plant_model.empty()) {
122 helios_runtime_error("ERROR (PlantArchitecture::buildPlantInstanceFromLibrary): current plant model has not been initialized from library. You must call loadPlantModelFromLibrary() first.");
123 }
124
125 // Find the plant builder function
126 auto builder_it = plant_builders.find(current_plant_model);
127 if (builder_it == plant_builders.end()) {
128 helios_runtime_error("ERROR (PlantArchitecture::buildPlantInstanceFromLibrary): plant label of " + current_plant_model + " does not exist in the library.");
129 }
130
131 // Set current build parameters so builder functions can access them
132 current_build_parameters = build_parameters;
133
134 // Call the builder function
135 uint plantID = builder_it->second(base_position);
136
137 // Clear build parameters after use
138 current_build_parameters.clear();
139
140 plant_instances.at(plantID).plant_name = current_plant_model;
141
142 // Update plant_name object data if enabled (geometry was built with temporary "custom" name)
143 if (output_object_data.at("plant_name")) {
144 std::vector<uint> plant_primitives = getAllPlantObjectIDs(plantID);
145 for (uint objID: plant_primitives) {
146 if (context_ptr->doesObjectDataExist(objID, "plant_name")) {
147 context_ptr->setObjectData(objID, "plant_name", current_plant_model);
148 }
149 }
150 }
151
152 // Set plant_type object data if enabled
153 if (output_object_data.at("plant_type")) {
154 std::string plant_type = "herbaceous"; // Default type
155 auto type_it = plant_type_map.find(current_plant_model);
156 if (type_it != plant_type_map.end()) {
157 plant_type = type_it->second;
158 }
159 std::vector<uint> plant_primitives = getAllPlantObjectIDs(plantID);
160 context_ptr->setObjectData(plant_primitives, "plant_type", plant_type);
161 }
162
163 // Register plant with per-tree BVH collision detection if enabled
164 if (collision_detection_enabled && collision_detection_ptr != nullptr && collision_detection_ptr->isTreeBasedBVHEnabled()) {
165 std::vector<uint> plant_primitives = getPlantCollisionRelevantObjectIDs(plantID);
166 if (!plant_primitives.empty()) {
167 collision_detection_ptr->registerTree(plantID, plant_primitives);
168 }
169 }
170
171 if (age > 0) {
172 advanceTime(plantID, age);
173 }
174
175 return plantID;
176}
177
179
180 if (shoot_types.find(shoot_type_label) == shoot_types.end()) {
181 helios_runtime_error("ERROR (PlantArchitecture::getCurrentShootParameters): shoot type label of " + shoot_type_label + " does not exist in the current shoot parameters.");
182 }
183
184 return shoot_types.at(shoot_type_label);
185}
186
187std::map<std::string, ShootParameters> PlantArchitecture::getCurrentShootParameters() {
188 if (shoot_types.empty()) {
189 std::cerr
190 << "WARNING (PlantArchitecture::getCurrentShootParameters): No plant models have been loaded. You need to first load a plant model from the library (see loadPlantModelFromLibrary()) or manually add shoot parameters (see updateCurrentShootParameters())."
191 << std::endl;
192 }
193 return shoot_types;
194}
195
196std::map<std::string, PhytomerParameters> PlantArchitecture::getCurrentPhytomerParameters() {
197 if (shoot_types.empty()) {
198 std::cerr
199 << "WARNING (PlantArchitecture::getCurrentPhytomerParameters): No plant models have been loaded. You need to first load a plant model from the library (see loadPlantModelFromLibrary()) or manually add shoot parameters (see updateCurrentShootParameters())."
200 << std::endl;
201 }
202 std::map<std::string, PhytomerParameters> phytomer_parameters;
203 for (const auto &type: shoot_types) {
204 phytomer_parameters[type.first] = type.second.phytomer_parameters;
205 }
206 return phytomer_parameters;
207}
208
209std::vector<std::string> PlantArchitecture::listShootTypeLabels() const {
210 if (shoot_types.empty()) {
212 "ERROR (PlantArchitecture::listShootTypeLabels): No plant model has been loaded. You must call loadPlantModelFromLibrary() first, or use listShootTypeLabels(plant_model_name) to query a specific model, or use listShootTypeLabels(plantID) to query a plant instance.");
213 }
214
215 std::vector<std::string> labels;
216 labels.reserve(shoot_types.size());
217
218 for (const auto &pair: shoot_types) {
219 labels.push_back(pair.first);
220 }
221
222 return labels;
223}
224
225std::vector<std::string> PlantArchitecture::listShootTypeLabels(const std::string &plant_model_name) {
226 // Validate model exists before modifying state
227 auto init_it = shoot_initializers.find(plant_model_name);
228 if (init_it == shoot_initializers.end()) {
229 helios_runtime_error("ERROR (PlantArchitecture::listShootTypeLabels): plant model '" + plant_model_name + "' does not exist in the library. Use getAvailablePlantModels() to see available plant models.");
230 }
231
232 // Save current state
233 std::string saved_plant_model = current_plant_model;
234 std::map<std::string, ShootParameters> saved_shoot_types = shoot_types;
235
236 // Load target plant model to extract shoot types
237 try {
238 initializeDefaultShoots(plant_model_name);
239
240 // Extract shoot type labels
241 std::vector<std::string> labels;
242 labels.reserve(shoot_types.size());
243 for (const auto &pair: shoot_types) {
244 labels.push_back(pair.first);
245 }
246
247 // Restore original state
248 current_plant_model = saved_plant_model;
249 shoot_types = saved_shoot_types;
250
251 return labels;
252 } catch (...) {
253 // Restore state even on exception
254 current_plant_model = saved_plant_model;
255 shoot_types = saved_shoot_types;
256 throw;
257 }
258}
259
260void PlantArchitecture::updateCurrentShootParameters(const std::string &shoot_type_label, const ShootParameters &params) {
261 shoot_types[shoot_type_label] = params;
262}
263
264void PlantArchitecture::updateCurrentShootParameters(const std::map<std::string, ShootParameters> &params) {
265 shoot_types = params;
266}
267
268void PlantArchitecture::initializeDefaultShoots(const std::string &plant_label) {
269
270 // Clear existing shoot types to prevent contamination between plant models
271 shoot_types.clear();
272
273 // Find the shoot initializer function
274 auto init_it = shoot_initializers.find(plant_label);
275 if (init_it == shoot_initializers.end()) {
276 helios_runtime_error("ERROR (PlantArchitecture::loadPlantModelFromLibrary): plant label of " + plant_label + " does not exist in the library.");
277 }
278
279 // Call the initializer function
280 init_it->second();
281}
282
283void PlantArchitecture::registerPlantModel(const std::string &name, std::function<void()> shoot_init, std::function<uint(const helios::vec3 &)> plant_build, const std::string &plant_type) {
284 shoot_initializers[name] = shoot_init;
285 plant_builders[name] = plant_build;
286 plant_type_map[name] = plant_type;
287}
288
289void PlantArchitecture::initializeAlmondTreeShoots() {
290
291 // ---- Leaf Prototype ---- //
292
293 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
294 leaf_prototype.leaf_texture_file[0] = "AlmondLeaf.png";
295 leaf_prototype.leaf_aspect_ratio = 0.33f;
296 leaf_prototype.midrib_fold_fraction = 0.1f;
297 leaf_prototype.longitudinal_curvature = 0.05;
298 leaf_prototype.lateral_curvature = 0.1f;
299 leaf_prototype.subdivisions = 1;
300 leaf_prototype.unique_prototypes = 1;
301
302 // ---- Phytomer Parameters ---- //
303
304 PhytomerParameters phytomer_parameters_almond(context_ptr->getRandomGenerator());
305
306 phytomer_parameters_almond.internode.pitch = 3;
307 phytomer_parameters_almond.internode.phyllotactic_angle.uniformDistribution(120, 160);
308 phytomer_parameters_almond.internode.radius_initial = 0.002;
309 phytomer_parameters_almond.internode.length_segments = 1;
310 phytomer_parameters_almond.internode.image_texture = "AlmondBark.jpg";
311 phytomer_parameters_almond.internode.max_floral_buds_per_petiole = 1; //
312
313 phytomer_parameters_almond.petiole.petioles_per_internode = 1;
314 phytomer_parameters_almond.petiole.pitch.uniformDistribution(-145, -90);
315 phytomer_parameters_almond.petiole.taper = 0.1;
316 phytomer_parameters_almond.petiole.curvature = 0;
317 phytomer_parameters_almond.petiole.length = 0.04;
318 phytomer_parameters_almond.petiole.radius = 0.0005;
319 phytomer_parameters_almond.petiole.length_segments = 1;
320 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
321 phytomer_parameters_almond.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
322
323 phytomer_parameters_almond.leaf.leaves_per_petiole = 1;
324 phytomer_parameters_almond.leaf.roll.uniformDistribution(-10, 10);
325 phytomer_parameters_almond.leaf.prototype_scale = 0.12;
326 phytomer_parameters_almond.leaf.prototype = leaf_prototype;
327
328 phytomer_parameters_almond.peduncle.length = 0.002;
329 phytomer_parameters_almond.peduncle.radius = 0.0005;
330 phytomer_parameters_almond.peduncle.pitch = 80;
331 phytomer_parameters_almond.peduncle.roll = 90;
332 phytomer_parameters_almond.peduncle.length_segments = 1;
333 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
334
335 phytomer_parameters_almond.inflorescence.flowers_per_peduncle = 1;
336 phytomer_parameters_almond.inflorescence.pitch = 0;
337 phytomer_parameters_almond.inflorescence.roll = 0;
338 phytomer_parameters_almond.inflorescence.flower_prototype_scale = 0.04;
339 phytomer_parameters_almond.inflorescence.flower_prototype_function = AlmondFlowerPrototype;
340 phytomer_parameters_almond.inflorescence.fruit_prototype_scale = 0.04;
341 phytomer_parameters_almond.inflorescence.fruit_prototype_function = AlmondFruitPrototype;
342
343 // ---- Shoot Parameters ---- //
344
345 // Trunk
346 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
347 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_almond;
348 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
349 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
350 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.005;
351 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
352 shoot_parameters_trunk.max_nodes = 20;
353 shoot_parameters_trunk.girth_area_factor = 10.f;
354 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
355 shoot_parameters_trunk.vegetative_bud_break_time = 0;
356 shoot_parameters_trunk.tortuosity = 1;
357 shoot_parameters_trunk.internode_length_max = 0.04;
358 shoot_parameters_trunk.internode_length_decay_rate = 0;
359 shoot_parameters_trunk.defineChildShootTypes({"scaffold"}, {1});
360
361 // Proleptic shoots
362 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
363 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_almond;
364 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
365 shoot_parameters_proleptic.phytomer_parameters.internode.radial_subdivisions = 5;
366 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = AlmondPhytomerCreationFunction;
367 shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = AlmondPhytomerCallbackFunction;
368 shoot_parameters_proleptic.max_nodes = 25;
369 shoot_parameters_proleptic.max_nodes_per_season = 20;
370 shoot_parameters_proleptic.phyllochron_min = 1;
371 shoot_parameters_proleptic.elongation_rate_max = 0.3;
372 shoot_parameters_proleptic.girth_area_factor = 6.f;
373 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.15;
374 shoot_parameters_proleptic.vegetative_bud_break_probability_max = 1.0;
375 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.6;
376 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
377 shoot_parameters_proleptic.gravitropic_curvature = 200;
378 shoot_parameters_proleptic.tortuosity = 3;
379 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(25, 30);
380 shoot_parameters_proleptic.insertion_angle_decay_rate = 15;
381 shoot_parameters_proleptic.internode_length_max = 0.015;
382 shoot_parameters_proleptic.internode_length_min = 0.002;
383 shoot_parameters_proleptic.internode_length_decay_rate = 0.002;
384 shoot_parameters_proleptic.fruit_set_probability = 0.4;
385 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
386 shoot_parameters_proleptic.max_terminal_floral_buds = 3;
387 shoot_parameters_proleptic.flowers_require_dormancy = true;
388 shoot_parameters_proleptic.growth_requires_dormancy = true;
389 shoot_parameters_proleptic.determinate_shoot_growth = false;
390 shoot_parameters_proleptic.defineChildShootTypes({"proleptic", "sylleptic"}, {1.0, 0.});
391
392 // Sylleptic shoots
393 ShootParameters shoot_parameters_sylleptic = shoot_parameters_proleptic;
394 // shoot_parameters_sylleptic.phytomer_parameters.internode.color = RGB::red;
395 shoot_parameters_sylleptic.phytomer_parameters.internode.image_texture = "";
396 shoot_parameters_sylleptic.phytomer_parameters.leaf.prototype_scale = 0.14;
397 shoot_parameters_sylleptic.phytomer_parameters.leaf.pitch.uniformDistribution(-45, -20);
398 shoot_parameters_sylleptic.insertion_angle_tip = 0;
399 shoot_parameters_sylleptic.insertion_angle_decay_rate = 0;
400 shoot_parameters_sylleptic.phyllochron_min = 1;
401 shoot_parameters_sylleptic.vegetative_bud_break_probability_min = 0.0;
402 shoot_parameters_sylleptic.vegetative_bud_break_probability_max = 0.7;
403 shoot_parameters_sylleptic.gravitropic_curvature = 600;
404 shoot_parameters_sylleptic.internode_length_max = 0.02;
405 shoot_parameters_sylleptic.flowers_require_dormancy = true;
406 shoot_parameters_sylleptic.growth_requires_dormancy = true;
407 shoot_parameters_sylleptic.defineChildShootTypes({"proleptic"}, {1.0});
408
409 // Main scaffolds
410 ShootParameters shoot_parameters_scaffold = shoot_parameters_proleptic;
411 // shoot_parameters_scaffold.phytomer_parameters.internode.color = RGB::blue;
412 shoot_parameters_scaffold.phytomer_parameters.internode.radial_subdivisions = 10;
413 shoot_parameters_scaffold.max_nodes = 15;
414 shoot_parameters_scaffold.gravitropic_curvature = 150;
415 shoot_parameters_scaffold.internode_length_max = 0.02;
416 shoot_parameters_scaffold.tortuosity = 1.;
417 shoot_parameters_scaffold.defineChildShootTypes({"proleptic"}, {1.0});
418
419 defineShootType("trunk", shoot_parameters_trunk);
420 defineShootType("scaffold", shoot_parameters_scaffold);
421 defineShootType("proleptic", shoot_parameters_proleptic);
422 defineShootType("sylleptic", shoot_parameters_sylleptic);
423}
424
425uint PlantArchitecture::buildAlmondTree(const helios::vec3 &base_position) {
426
427 if (shoot_types.empty()) {
428 // automatically initialize almond tree shoots
429 initializeAlmondTreeShoots();
430 }
431
432 // Get training system parameters (with defaults matching original hard-coded values)
433 auto trunk_height = getParameterValue(current_build_parameters, "trunk_height", 0.6f, 0.1f, 3.f, "total trunk height in meters");
434 auto num_scaffolds = uint(getParameterValue(current_build_parameters, "num_scaffolds", 4.f, 2.f, 8.f, "number of scaffold branches"));
435 auto scaffold_angle = getParameterValue(current_build_parameters, "scaffold_angle", 40.f, 20.f, 70.f, "scaffold branch angle in degrees");
436
437 // Calculate trunk nodes based on desired height and internode length
438 float trunk_internode_length = 0.03f; // Default internode length for almond
439 uint trunk_nodes = uint(trunk_height / trunk_internode_length);
440 if (trunk_nodes < 1)
441 trunk_nodes = 1;
442
443 // Fixed training parameters (not user-customizable)
444 float trunk_radius = 0.015f;
445 float scaffold_radius = 0.007f;
446 float scaffold_length = 0.06f;
447
448 uint plantID = addPlantInstance(base_position, 0);
449
450 // enableEpicormicChildShoots(plantID,"sylleptic",0.001);
451
452 uint uID_trunk = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), 0.f * M_PI), trunk_radius, trunk_internode_length, 1.f, 1.f, 0, "trunk");
453 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0.01, 0.01, 1, 1);
454
455 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
456
457 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
458 for (const auto &phytomer: phytomers) {
459 phytomer->removeLeaf();
460 phytomer->setVegetativeBudState(BUD_DEAD);
461 phytomer->setFloralBudState(BUD_DEAD);
462 }
463
464 // Hard-coded scaffold node range (not user-customizable)
465 uint scaffold_nodes_min = 2;
466 uint scaffold_nodes_max = 5;
467
468 for (int i = 0; i < num_scaffolds; i++) {
469 float pitch = deg2rad(scaffold_angle) + context_ptr->randu(-0.1f, 0.1f); // Small randomness around specified angle
470 uint scaffold_nodes = context_ptr->randu(int(scaffold_nodes_min), int(scaffold_nodes_max));
471 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, scaffold_nodes, make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(num_scaffolds) * 2 * M_PI, 0), scaffold_radius,
472 scaffold_length, 1.f, 1.f, 0.5, "scaffold", 0);
473 }
474
475 makePlantDormant(plantID);
476
477 setPlantPhenologicalThresholds(plantID, 90, -1, 3, 7, 20, 275, false);
478 plant_instances.at(plantID).max_age = 1825;
479
480 return plantID;
481}
482
483void PlantArchitecture::initializeAlmondTreeAldrichShoots() {
484
485 // ---- Leaf Prototype ---- //
486
487 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
488 leaf_prototype.leaf_texture_file[0] = helios::resolvePluginAsset("plantarchitecture", "assets/textures/AlmondLeaf.png").string().c_str();
489 leaf_prototype.leaf_aspect_ratio = 0.33f;
490 leaf_prototype.midrib_fold_fraction = 0.1f;
491 leaf_prototype.longitudinal_curvature = 0.05;
492 leaf_prototype.lateral_curvature = 0.1f;
493 leaf_prototype.subdivisions = 1;
494 leaf_prototype.unique_prototypes = 1;
495
496 // ---- Phytomer Parameters ---- //
497
498 PhytomerParameters phytomer_parameters_almond(context_ptr->getRandomGenerator());
499
500 phytomer_parameters_almond.internode.pitch = 3;
501 phytomer_parameters_almond.internode.phyllotactic_angle.uniformDistribution(120, 160);
502 phytomer_parameters_almond.internode.radius_initial = 0.002;
503 phytomer_parameters_almond.internode.length_segments = 1;
504 phytomer_parameters_almond.internode.image_texture = helios::resolvePluginAsset("plantarchitecture", "assets/textures/AlmondBark.jpg").string().c_str();
505 phytomer_parameters_almond.internode.max_floral_buds_per_petiole = 1; //
506
507 phytomer_parameters_almond.petiole.petioles_per_internode = 1;
508 phytomer_parameters_almond.petiole.pitch.uniformDistribution(-145, -90);
509 phytomer_parameters_almond.petiole.taper = 0.1;
510 phytomer_parameters_almond.petiole.curvature = 0;
511 phytomer_parameters_almond.petiole.length = 0.04;
512 phytomer_parameters_almond.petiole.radius = 0.0005;
513 phytomer_parameters_almond.petiole.length_segments = 1;
514 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
515 phytomer_parameters_almond.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
516
517 phytomer_parameters_almond.leaf.leaves_per_petiole = 1;
518 phytomer_parameters_almond.leaf.roll.uniformDistribution(-10, 10);
519 phytomer_parameters_almond.leaf.prototype_scale = 0.12;
520 phytomer_parameters_almond.leaf.prototype = leaf_prototype;
521
522 phytomer_parameters_almond.peduncle.length = 0.002;
523 phytomer_parameters_almond.peduncle.radius = 0.0005;
524 phytomer_parameters_almond.peduncle.pitch = 80;
525 phytomer_parameters_almond.peduncle.roll = 90;
526 phytomer_parameters_almond.peduncle.length_segments = 1;
527 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
528
529 phytomer_parameters_almond.inflorescence.flowers_per_peduncle = 1;
530 phytomer_parameters_almond.inflorescence.pitch = 0;
531 phytomer_parameters_almond.inflorescence.roll = 0;
532 phytomer_parameters_almond.inflorescence.flower_prototype_scale = 0.04;
533 phytomer_parameters_almond.inflorescence.flower_prototype_function = AlmondFlowerPrototype;
534 phytomer_parameters_almond.inflorescence.fruit_prototype_scale = 0.04;
535 phytomer_parameters_almond.inflorescence.fruit_prototype_function = AlmondFruitPrototype;
536
537 // ---- Shoot Parameters ---- //
538
539 // Trunk
540 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
541 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_almond;
542 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
543 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
544 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.005;
545 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
546 shoot_parameters_trunk.max_nodes = 20;
547 shoot_parameters_trunk.girth_area_factor = 8.f;
548 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
549 shoot_parameters_trunk.vegetative_bud_break_time = 0;
550 shoot_parameters_trunk.tortuosity = .5;
551 shoot_parameters_trunk.internode_length_max = 0.05;
552 shoot_parameters_trunk.internode_length_decay_rate = 0;
553 shoot_parameters_trunk.defineChildShootTypes({"scaffold"}, {1});
554
555 // Proleptic shoots
556 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
557 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_almond;
558 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
559 shoot_parameters_proleptic.phytomer_parameters.internode.radial_subdivisions = 5;
560 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = AlmondPhytomerCreationFunction;
561 shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = AlmondPhytomerCallbackFunction;
562 shoot_parameters_proleptic.max_nodes = 25;
563 shoot_parameters_proleptic.max_nodes_per_season = 15;
564 shoot_parameters_proleptic.phyllochron_min = 1;
565 shoot_parameters_proleptic.elongation_rate_max = 0.3;
566 shoot_parameters_proleptic.girth_area_factor = 8.f;
567 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.0;
568 shoot_parameters_proleptic.vegetative_bud_break_probability_max = 1.0;
569 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.6;
570 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
571 shoot_parameters_proleptic.gravitropic_curvature = 450;
572 shoot_parameters_proleptic.tortuosity = 2.5;
573 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(5, 30);
574 shoot_parameters_proleptic.insertion_angle_decay_rate = 5;
575 shoot_parameters_proleptic.internode_length_max = 0.025;
576 shoot_parameters_proleptic.internode_length_min = 0.002;
577 shoot_parameters_proleptic.internode_length_decay_rate = 0.002;
578 shoot_parameters_proleptic.fruit_set_probability = 0.4;
579 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
580 shoot_parameters_proleptic.max_terminal_floral_buds = 3;
581 shoot_parameters_proleptic.flowers_require_dormancy = true;
582 shoot_parameters_proleptic.growth_requires_dormancy = true;
583 shoot_parameters_proleptic.determinate_shoot_growth = false;
584 shoot_parameters_proleptic.defineChildShootTypes({"proleptic", "sylleptic"}, {1.0, 0.});
585
586 // Sylleptic shoots
587 ShootParameters shoot_parameters_sylleptic = shoot_parameters_proleptic;
588 // shoot_parameters_sylleptic.phytomer_parameters.internode.color = RGB::red;
589 shoot_parameters_sylleptic.phytomer_parameters.internode.image_texture = "";
590 shoot_parameters_sylleptic.phytomer_parameters.leaf.prototype_scale = 0.12;
591 shoot_parameters_sylleptic.phytomer_parameters.leaf.pitch.uniformDistribution(-45, -20);
592 shoot_parameters_sylleptic.insertion_angle_tip = 0;
593 shoot_parameters_sylleptic.insertion_angle_decay_rate = 0;
594 shoot_parameters_sylleptic.phyllochron_min = 1;
595 shoot_parameters_sylleptic.vegetative_bud_break_probability_min = 0.0;
596 shoot_parameters_proleptic.vegetative_bud_break_probability_max = 0.6;
597 shoot_parameters_sylleptic.gravitropic_curvature = 600;
598 shoot_parameters_sylleptic.internode_length_max = 0.02;
599 shoot_parameters_sylleptic.flowers_require_dormancy = true;
600 shoot_parameters_sylleptic.growth_requires_dormancy = true;
601 shoot_parameters_sylleptic.defineChildShootTypes({"proleptic"}, {1.0});
602
603 // Main scaffolds
604 ShootParameters shoot_parameters_scaffold = shoot_parameters_proleptic;
605 // shoot_parameters_scaffold.phytomer_parameters.internode.color = RGB::blue;
606 shoot_parameters_scaffold.phytomer_parameters.internode.radial_subdivisions = 10;
607 shoot_parameters_scaffold.max_nodes = 20;
608 shoot_parameters_scaffold.gravitropic_curvature = 200;
609 shoot_parameters_scaffold.internode_length_max = 0.02;
610 shoot_parameters_scaffold.tortuosity = 0.5;
611 shoot_parameters_scaffold.defineChildShootTypes({"proleptic"}, {1.0});
612
613 defineShootType("trunk", shoot_parameters_trunk);
614 defineShootType("scaffold", shoot_parameters_scaffold);
615 defineShootType("proleptic", shoot_parameters_proleptic);
616 defineShootType("sylleptic", shoot_parameters_sylleptic);
617}
618
619uint PlantArchitecture::buildAlmondTreeAldrich(const helios::vec3 &base_position) {
620
621 if (shoot_types.empty()) {
622 // automatically initialize almond tree shoots
623 initializeAlmondTreeShoots();
624 }
625
626 uint plantID = addPlantInstance(base_position, 0);
627
628 // enableEpicormicChildShoots(plantID,"sylleptic",0.001);
629
630 uint uID_trunk = addBaseStemShoot(plantID, 19, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), 0.f * M_PI), 0.015, 0.03, 1.f, 1.f, 0, "trunk");
631 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0.01, 0.01, 1, 1);
632
633 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
634
635 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
636 for (const auto &phytomer: phytomers) {
637 phytomer->removeLeaf();
638 phytomer->setVegetativeBudState(BUD_DEAD);
639 phytomer->setFloralBudState(BUD_DEAD);
640 }
641
642 uint Nscaffolds = 4; // context_ptr->randu(4,5);
643
644 for (int i = 0; i < Nscaffolds; i++) {
645 float pitch = context_ptr->randu(deg2rad(5), deg2rad(35));
646 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, context_ptr->randu(7, 9), make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(Nscaffolds) * 2 * M_PI, 0), 0.007, 0.06,
647 1.f, 1.f, 0.5, "scaffold", 0);
648 }
649
650 makePlantDormant(plantID);
651
652 setPlantPhenologicalThresholds(plantID, 90, -1, 3, 7, 20, 275, false);
653 plant_instances.at(plantID).max_age = 1825;
654
655 return plantID;
656}
657
658
659void PlantArchitecture::initializeAlmondTreeWoodColonyShoots() {
660
661 // ---- Leaf Prototype ---- //
662
663 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
664 leaf_prototype.leaf_texture_file[0] = helios::resolvePluginAsset("plantarchitecture", "assets/textures/AlmondLeaf.png").string().c_str();
665 leaf_prototype.leaf_aspect_ratio = 0.33f;
666 leaf_prototype.midrib_fold_fraction = 0.1f;
667 leaf_prototype.longitudinal_curvature = 0.05;
668 leaf_prototype.lateral_curvature = 0.1f;
669 leaf_prototype.subdivisions = 1;
670 leaf_prototype.unique_prototypes = 1;
671
672 // ---- Phytomer Parameters ---- //
673
674 PhytomerParameters phytomer_parameters_almond(context_ptr->getRandomGenerator());
675
676 phytomer_parameters_almond.internode.pitch = 3;
677 phytomer_parameters_almond.internode.phyllotactic_angle.uniformDistribution(120, 160);
678 phytomer_parameters_almond.internode.radius_initial = 0.002;
679 phytomer_parameters_almond.internode.length_segments = 1;
680 phytomer_parameters_almond.internode.image_texture = helios::resolvePluginAsset("plantarchitecture", "assets/textures/AlmondBark.jpg").string().c_str();
681 phytomer_parameters_almond.internode.max_floral_buds_per_petiole = 1; //
682
683 phytomer_parameters_almond.petiole.petioles_per_internode = 1;
684 phytomer_parameters_almond.petiole.pitch.uniformDistribution(-145, -90);
685 phytomer_parameters_almond.petiole.taper = 0.1;
686 phytomer_parameters_almond.petiole.curvature = 0;
687 phytomer_parameters_almond.petiole.length = 0.04;
688 phytomer_parameters_almond.petiole.radius = 0.0005;
689 phytomer_parameters_almond.petiole.length_segments = 1;
690 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
691 phytomer_parameters_almond.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
692
693 phytomer_parameters_almond.leaf.leaves_per_petiole = 1;
694 phytomer_parameters_almond.leaf.roll.uniformDistribution(-10, 10);
695 phytomer_parameters_almond.leaf.prototype_scale = 0.12;
696 phytomer_parameters_almond.leaf.prototype = leaf_prototype;
697
698 phytomer_parameters_almond.peduncle.length = 0.002;
699 phytomer_parameters_almond.peduncle.radius = 0.0005;
700 phytomer_parameters_almond.peduncle.pitch = 80;
701 phytomer_parameters_almond.peduncle.roll = 90;
702 phytomer_parameters_almond.peduncle.length_segments = 1;
703 phytomer_parameters_almond.petiole.radial_subdivisions = 3;
704
705 phytomer_parameters_almond.inflorescence.flowers_per_peduncle = 1;
706 phytomer_parameters_almond.inflorescence.pitch = 0;
707 phytomer_parameters_almond.inflorescence.roll = 0;
708 phytomer_parameters_almond.inflorescence.flower_prototype_scale = 0.04;
709 phytomer_parameters_almond.inflorescence.flower_prototype_function = AlmondFlowerPrototype;
710 phytomer_parameters_almond.inflorescence.fruit_prototype_scale = 0.04;
711 phytomer_parameters_almond.inflorescence.fruit_prototype_function = AlmondFruitPrototype;
712
713 // ---- Shoot Parameters ---- //
714
715 // Trunk
716 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
717 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_almond;
718 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
719 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
720 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.005;
721 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
722 shoot_parameters_trunk.max_nodes = 15;
723 shoot_parameters_trunk.girth_area_factor = 10.f;
724 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
725 shoot_parameters_trunk.vegetative_bud_break_time = 0;
726 shoot_parameters_trunk.tortuosity = 1;
727 shoot_parameters_trunk.internode_length_max = 0.0325;
728 shoot_parameters_trunk.internode_length_decay_rate = 0;
729 shoot_parameters_trunk.defineChildShootTypes({"scaffold"}, {1});
730
731 // Proleptic shoots
732 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
733 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_almond;
734 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
735 shoot_parameters_proleptic.phytomer_parameters.internode.radial_subdivisions = 5;
736 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = AlmondPhytomerCreationFunction;
737 shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = AlmondPhytomerCallbackFunction;
738 shoot_parameters_proleptic.max_nodes = 20;
739 shoot_parameters_proleptic.max_nodes_per_season = 15;
740 shoot_parameters_proleptic.phyllochron_min = 1;
741 shoot_parameters_proleptic.elongation_rate_max = 0.3;
742 shoot_parameters_proleptic.girth_area_factor = 8.f;
743 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.0;
744 shoot_parameters_proleptic.vegetative_bud_break_probability_max = 1.0;
745 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.6;
746 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
747 shoot_parameters_proleptic.gravitropic_curvature = 150;
748 shoot_parameters_proleptic.tortuosity = 3;
749 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(30, 75);
750 shoot_parameters_proleptic.insertion_angle_decay_rate = 17.5;
751 shoot_parameters_proleptic.internode_length_max = 0.02;
752 shoot_parameters_proleptic.internode_length_min = 0.002;
753 shoot_parameters_proleptic.internode_length_decay_rate = 0.002;
754 shoot_parameters_proleptic.fruit_set_probability = 0.4;
755 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
756 shoot_parameters_proleptic.max_terminal_floral_buds = 3;
757 shoot_parameters_proleptic.flowers_require_dormancy = true;
758 shoot_parameters_proleptic.growth_requires_dormancy = true;
759 shoot_parameters_proleptic.determinate_shoot_growth = false;
760 shoot_parameters_proleptic.defineChildShootTypes({"proleptic", "sylleptic"}, {1.0, 0.});
761
762 // Sylleptic shoots
763 ShootParameters shoot_parameters_sylleptic = shoot_parameters_proleptic;
764 // shoot_parameters_sylleptic.phytomer_parameters.internode.color = RGB::red;
765 shoot_parameters_sylleptic.phytomer_parameters.internode.image_texture = "";
766 shoot_parameters_sylleptic.phytomer_parameters.leaf.prototype_scale = 0.12;
767 shoot_parameters_sylleptic.phytomer_parameters.leaf.pitch.uniformDistribution(-45, -20);
768 shoot_parameters_sylleptic.insertion_angle_tip = 0;
769 shoot_parameters_sylleptic.insertion_angle_decay_rate = 0;
770 shoot_parameters_sylleptic.phyllochron_min = 1;
771 shoot_parameters_sylleptic.vegetative_bud_break_probability_min = 0.0;
772 shoot_parameters_proleptic.vegetative_bud_break_probability_max = 0.6;
773 shoot_parameters_sylleptic.gravitropic_curvature = 600;
774 shoot_parameters_sylleptic.internode_length_max = 0.02;
775 shoot_parameters_sylleptic.flowers_require_dormancy = true;
776 shoot_parameters_sylleptic.growth_requires_dormancy = true;
777 shoot_parameters_sylleptic.defineChildShootTypes({"proleptic"}, {1.0});
778
779 // Main scaffolds
780 ShootParameters shoot_parameters_scaffold = shoot_parameters_proleptic;
781 // shoot_parameters_scaffold.phytomer_parameters.internode.color = RGB::blue;
782 shoot_parameters_scaffold.phytomer_parameters.internode.radial_subdivisions = 10;
783 shoot_parameters_scaffold.max_nodes = 15;
784 shoot_parameters_scaffold.gravitropic_curvature = 100;
785 shoot_parameters_scaffold.internode_length_max = 0.02;
786 shoot_parameters_scaffold.tortuosity = 1.;
787 shoot_parameters_scaffold.defineChildShootTypes({"proleptic"}, {1.0});
788
789 defineShootType("trunk", shoot_parameters_trunk);
790 defineShootType("scaffold", shoot_parameters_scaffold);
791 defineShootType("proleptic", shoot_parameters_proleptic);
792 defineShootType("sylleptic", shoot_parameters_sylleptic);
793}
794
795uint PlantArchitecture::buildAlmondTreeWoodColony(const helios::vec3 &base_position) {
796
797 if (shoot_types.empty()) {
798 // automatically initialize almond tree shoots
799 initializeAlmondTreeShoots();
800 }
801
802 uint plantID = addPlantInstance(base_position, 0);
803
804 // enableEpicormicChildShoots(plantID,"sylleptic",0.001);
805
806 uint uID_trunk = addBaseStemShoot(plantID, 14, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), 0.f * M_PI), 0.015, 0.03, 1.f, 1.f, 0, "trunk");
807 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0.01, 0.01, 1, 1);
808
809 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
810
811 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
812 for (const auto &phytomer: phytomers) {
813 phytomer->removeLeaf();
814 phytomer->setVegetativeBudState(BUD_DEAD);
815 phytomer->setFloralBudState(BUD_DEAD);
816 }
817
818 uint Nscaffolds = 5; // context_ptr->randu(4,5);
819
820 for (int i = 0; i < Nscaffolds; i++) {
821 float pitch = context_ptr->randu(deg2rad(45), deg2rad(60));
822 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, context_ptr->randu(7, 9), make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(Nscaffolds) * 2 * M_PI, 0), 0.007, 0.06,
823 1.f, 1.f, 0.5, "scaffold", 0);
824 }
825
826 makePlantDormant(plantID);
827
828 setPlantPhenologicalThresholds(plantID, 90, -1, 3, 7, 20, 275, false);
829 plant_instances.at(plantID).max_age = 1825;
830
831 return plantID;
832}
833
834void PlantArchitecture::initializeAppleTreeShoots() {
835
836 // ---- Leaf Prototype ---- //
837
838 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
839 leaf_prototype.leaf_texture_file[0] = "AppleLeaf.png";
840 leaf_prototype.leaf_aspect_ratio = 0.6f;
841 leaf_prototype.midrib_fold_fraction = 0.4f;
842 leaf_prototype.longitudinal_curvature = -0.3f;
843 leaf_prototype.lateral_curvature = 0.1f;
844 leaf_prototype.subdivisions = 3;
845 leaf_prototype.unique_prototypes = 1;
846
847 // ---- Phytomer Parameters ---- //
848
849 PhytomerParameters phytomer_parameters_apple(context_ptr->getRandomGenerator());
850
851 phytomer_parameters_apple.internode.pitch = 0;
852 phytomer_parameters_apple.internode.phyllotactic_angle.uniformDistribution(130, 145);
853 phytomer_parameters_apple.internode.radius_initial = 0.004;
854 phytomer_parameters_apple.internode.length_segments = 1;
855 phytomer_parameters_apple.internode.image_texture = "AppleBark.jpg";
856 phytomer_parameters_apple.internode.max_floral_buds_per_petiole = 1;
857
858 phytomer_parameters_apple.petiole.petioles_per_internode = 1;
859 phytomer_parameters_apple.petiole.pitch.uniformDistribution(-40, -25);
860 phytomer_parameters_apple.petiole.taper = 0.1;
861 phytomer_parameters_apple.petiole.curvature = 0;
862 phytomer_parameters_apple.petiole.length = 0.04;
863 phytomer_parameters_apple.petiole.radius = 0.00075;
864 phytomer_parameters_apple.petiole.length_segments = 1;
865 phytomer_parameters_apple.petiole.radial_subdivisions = 3;
866 phytomer_parameters_apple.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
867
868 phytomer_parameters_apple.leaf.leaves_per_petiole = 1;
869 phytomer_parameters_apple.leaf.prototype_scale = 0.12;
870 phytomer_parameters_apple.leaf.prototype = leaf_prototype;
871
872 phytomer_parameters_apple.peduncle.length = 0.04;
873 phytomer_parameters_apple.peduncle.radius = 0.001;
874 phytomer_parameters_apple.peduncle.pitch = 90;
875 phytomer_parameters_apple.peduncle.roll = 90;
876 phytomer_parameters_apple.peduncle.length_segments = 1;
877
878 phytomer_parameters_apple.inflorescence.flowers_per_peduncle = 1;
879 phytomer_parameters_apple.inflorescence.pitch = 0;
880 phytomer_parameters_apple.inflorescence.roll = 0;
881 phytomer_parameters_apple.inflorescence.flower_prototype_scale = 0.03;
882 phytomer_parameters_apple.inflorescence.flower_prototype_function = AppleFlowerPrototype;
883 phytomer_parameters_apple.inflorescence.fruit_prototype_scale = 0.1;
884 phytomer_parameters_apple.inflorescence.fruit_prototype_function = AppleFruitPrototype;
885 phytomer_parameters_apple.inflorescence.fruit_gravity_factor_fraction = 0.5;
886
887 // ---- Shoot Parameters ---- //
888
889 // Trunk
890 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
891 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_apple;
892 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
893 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.01;
894 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
895 shoot_parameters_trunk.max_nodes = 20;
896 shoot_parameters_trunk.girth_area_factor = 5.f;
897 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
898 shoot_parameters_trunk.vegetative_bud_break_time = 0;
899 shoot_parameters_trunk.tortuosity = 1;
900 shoot_parameters_trunk.internode_length_max = 0.05;
901 shoot_parameters_trunk.internode_length_decay_rate = 0;
902 shoot_parameters_trunk.defineChildShootTypes({"proleptic"}, {1});
903
904 // Proleptic shoots
905 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
906 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_apple;
907 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
908 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = ApplePhytomerCreationFunction;
909 shoot_parameters_proleptic.max_nodes = 40;
910 shoot_parameters_proleptic.max_nodes_per_season = 20;
911 shoot_parameters_proleptic.phyllochron_min = 2.0;
912 shoot_parameters_proleptic.elongation_rate_max = 0.15;
913 shoot_parameters_proleptic.girth_area_factor = 7.f;
914 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.1;
915 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.4;
916 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
917 shoot_parameters_proleptic.gravitropic_curvature.uniformDistribution(450, 500);
918 shoot_parameters_proleptic.tortuosity = 3;
919 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(30, 40);
920 shoot_parameters_proleptic.insertion_angle_decay_rate = 20;
921 shoot_parameters_proleptic.internode_length_max = 0.04;
922 shoot_parameters_proleptic.internode_length_min = 0.01;
923 shoot_parameters_proleptic.internode_length_decay_rate = 0.004;
924 shoot_parameters_proleptic.fruit_set_probability = 0.3;
925 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
926 shoot_parameters_proleptic.max_terminal_floral_buds = 1;
927 shoot_parameters_proleptic.flowers_require_dormancy = true;
928 shoot_parameters_proleptic.growth_requires_dormancy = true;
929 shoot_parameters_proleptic.determinate_shoot_growth = false;
930 shoot_parameters_proleptic.defineChildShootTypes({"proleptic"}, {1.0});
931
932 defineShootType("trunk", shoot_parameters_trunk);
933 defineShootType("proleptic", shoot_parameters_proleptic);
934}
935
936uint PlantArchitecture::buildAppleTree(const helios::vec3 &base_position) {
937
938 if (shoot_types.empty()) {
939 // automatically initialize apple tree shoots
940 initializeAppleTreeShoots();
941 }
942
943 // Get training system parameters (with defaults matching original hard-coded values)
944 auto trunk_height = getParameterValue(current_build_parameters, "trunk_height", 0.8f, 0.1f, 3.f, "total trunk height in meters");
945 auto num_scaffolds = uint(getParameterValue(current_build_parameters, "num_scaffolds", 4.f, 2.f, 8.f, "number of scaffold branches"));
946 auto scaffold_angle = getParameterValue(current_build_parameters, "scaffold_angle", 40.f, 20.f, 70.f, "scaffold branch angle in degrees");
947
948 // Calculate trunk nodes based on desired height and internode length
949 float trunk_internode_length = 0.04f; // Default internode length for apple
950 uint trunk_nodes = uint(trunk_height / trunk_internode_length);
951 if (trunk_nodes < 1)
952 trunk_nodes = 1;
953
954 // Fixed training parameters (not user-customizable)
955 float trunk_radius = 0.015f;
956 float scaffold_radius = 0.005f;
957 float scaffold_length = 0.04f;
958
959 uint plantID = addPlantInstance(base_position, 0);
960
961 uint uID_trunk = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), 0.f * M_PI), trunk_radius, trunk_internode_length, 1.f, 1.f, 0, "trunk");
962 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0, 0.01, 1, 1);
963
964 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
965
966 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
967 for (const auto &phytomer: phytomers) {
968 phytomer->removeLeaf();
969 phytomer->setVegetativeBudState(BUD_DEAD);
970 phytomer->setFloralBudState(BUD_DEAD);
971 }
972
973 // Hard-coded scaffold node range (not user-customizable)
974 uint scaffold_nodes_min = 7;
975 uint scaffold_nodes_max = 9;
976
977 for (int i = 0; i < num_scaffolds; i++) {
978 float pitch = deg2rad(scaffold_angle) + context_ptr->randu(-0.1f, 0.1f); // Small randomness around specified angle
979 uint scaffold_nodes = context_ptr->randu(int(scaffold_nodes_min), int(scaffold_nodes_max));
980 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, scaffold_nodes, make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(num_scaffolds) * 2 * M_PI, 0), scaffold_radius,
981 scaffold_length, 1.f, 1.f, 0.5, "proleptic", 0);
982 }
983
984 makePlantDormant(plantID);
985
986 setPlantPhenologicalThresholds(plantID, 165, -1, 3, 7, 30, 200, false);
987 plant_instances.at(plantID).max_age = 1460;
988
989 return plantID;
990}
991
992void PlantArchitecture::initializeAppleFruitingWallShoots() {
993
994 // ---- Leaf Prototype ---- //
995
996 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
997 leaf_prototype.leaf_texture_file[0] = "AppleLeaf.png";
998 leaf_prototype.leaf_aspect_ratio = 0.6f;
999 leaf_prototype.midrib_fold_fraction = 0.4f;
1000 leaf_prototype.longitudinal_curvature = -0.3f;
1001 leaf_prototype.lateral_curvature = 0.1f;
1002 leaf_prototype.subdivisions = 3;
1003 leaf_prototype.unique_prototypes = 1;
1004
1005 // ---- Phytomer Parameters ---- //
1006
1007 PhytomerParameters phytomer_parameters_apple(context_ptr->getRandomGenerator());
1008
1009 phytomer_parameters_apple.internode.pitch = 0;
1010 phytomer_parameters_apple.internode.phyllotactic_angle.uniformDistribution(130, 145);
1011 phytomer_parameters_apple.internode.radius_initial = 0.004;
1012 phytomer_parameters_apple.internode.length_segments = 1;
1013 phytomer_parameters_apple.internode.image_texture = "AppleBark.jpg";
1014 phytomer_parameters_apple.internode.max_floral_buds_per_petiole = 1;
1015
1016 phytomer_parameters_apple.petiole.petioles_per_internode = 1;
1017 phytomer_parameters_apple.petiole.pitch.uniformDistribution(-40, -25);
1018 phytomer_parameters_apple.petiole.taper = 0.1;
1019 phytomer_parameters_apple.petiole.curvature = 0;
1020 phytomer_parameters_apple.petiole.length = 0.04;
1021 phytomer_parameters_apple.petiole.radius = 0.00075;
1022 phytomer_parameters_apple.petiole.length_segments = 1;
1023 phytomer_parameters_apple.petiole.radial_subdivisions = 3;
1024 phytomer_parameters_apple.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
1025
1026 phytomer_parameters_apple.leaf.leaves_per_petiole = 1;
1027 phytomer_parameters_apple.leaf.prototype_scale = 0.12;
1028 phytomer_parameters_apple.leaf.prototype = leaf_prototype;
1029
1030 phytomer_parameters_apple.peduncle.length = 0.04;
1031 phytomer_parameters_apple.peduncle.radius = 0.001;
1032 phytomer_parameters_apple.peduncle.pitch = 90;
1033 phytomer_parameters_apple.peduncle.roll = 90;
1034 phytomer_parameters_apple.peduncle.length_segments = 1;
1035
1036 phytomer_parameters_apple.inflorescence.flowers_per_peduncle = 1;
1037 phytomer_parameters_apple.inflorescence.pitch = 0;
1038 phytomer_parameters_apple.inflorescence.roll = 0;
1039 phytomer_parameters_apple.inflorescence.flower_prototype_scale = 0.03;
1040 phytomer_parameters_apple.inflorescence.flower_prototype_function = AppleFlowerPrototype;
1041 phytomer_parameters_apple.inflorescence.fruit_prototype_scale = 0.1;
1042 phytomer_parameters_apple.inflorescence.fruit_prototype_function = AppleFruitPrototype;
1043 phytomer_parameters_apple.inflorescence.fruit_gravity_factor_fraction = 0.5;
1044
1045 // ---- Shoot Parameters ---- //
1046
1047 // Trunk
1048 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
1049 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_apple;
1050 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(175, 185);
1051 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.01;
1052 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
1053 shoot_parameters_trunk.max_nodes = 30;
1054 shoot_parameters_trunk.girth_area_factor = 5.f;
1055 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
1056 shoot_parameters_trunk.vegetative_bud_break_time = 0;
1057 shoot_parameters_trunk.tortuosity = 0.5;
1058 shoot_parameters_trunk.internode_length_max = 0.05;
1059 shoot_parameters_trunk.internode_length_decay_rate = 0;
1060 shoot_parameters_trunk.defineChildShootTypes({"lateral"}, {1});
1061
1062 // Proleptic shoots
1063 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
1064 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_apple;
1065 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
1066 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = ApplePhytomerCreationFunction;
1067 shoot_parameters_proleptic.max_nodes = 20;
1068 shoot_parameters_proleptic.max_nodes_per_season = 20;
1069 shoot_parameters_proleptic.phyllochron_min = 2.0;
1070 shoot_parameters_proleptic.elongation_rate_max = 0.15;
1071 shoot_parameters_proleptic.girth_area_factor = 7.f;
1072 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.1;
1073 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.4;
1074 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
1075 shoot_parameters_proleptic.gravitropic_curvature.uniformDistribution(550, 700);
1076 shoot_parameters_proleptic.tortuosity = 3;
1077 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(70, 110);
1078 shoot_parameters_proleptic.insertion_angle_decay_rate = 20;
1079 shoot_parameters_proleptic.internode_length_max = 0.04;
1080 shoot_parameters_proleptic.internode_length_min = 0.01;
1081 shoot_parameters_proleptic.internode_length_decay_rate = 0.004;
1082 shoot_parameters_proleptic.fruit_set_probability = 0.3;
1083 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
1084 shoot_parameters_proleptic.max_terminal_floral_buds = 1;
1085 shoot_parameters_proleptic.flowers_require_dormancy = true;
1086 shoot_parameters_proleptic.growth_requires_dormancy = true;
1087 shoot_parameters_proleptic.determinate_shoot_growth = false;
1088 shoot_parameters_proleptic.defineChildShootTypes({"proleptic"}, {1.0});
1089
1090 // lateral shoots
1091 ShootParameters shoot_parameters_lateral = shoot_parameters_proleptic;
1092 shoot_parameters_lateral.gravitropic_curvature = 0;
1093 shoot_parameters_lateral.phytomer_parameters.internode.phyllotactic_angle = 360;
1094
1095 defineShootType("trunk", shoot_parameters_trunk);
1096 defineShootType("proleptic", shoot_parameters_proleptic);
1097 defineShootType("lateral", shoot_parameters_lateral);
1098}
1099
1100uint PlantArchitecture::buildAppleFruitingWall(const helios::vec3 &base_position) {
1101
1102 if (shoot_types.empty()) {
1103 // automatically initialize apple tree shoots
1104 initializeAppleFruitingWallShoots();
1105 }
1106
1107 std::vector<std::vector<vec3>> trellis_points;
1108
1109 std::vector<float> wire_heights{0.75, 1.6, 2.25, 2.8};
1110 float tree_spacing = 1.2;
1111
1112 trellis_points.reserve(wire_heights.size());
1113 for (int i = 0; i < wire_heights.size(); i++) {
1114 trellis_points.push_back(linspace(make_vec3(0, -0.5f * tree_spacing, wire_heights.at(i)), make_vec3(0, 0.5f * tree_spacing, wire_heights.at(i)), 8));
1115 // context_ptr->addTube( 4, {base_position+trellis_points.back().at(0), base_position+trellis_points.back().back()}, {0.005, 0.005}, {RGB::silver, RGB::silver});
1116 }
1117
1118 for (int j = 0; j < trellis_points.size(); j++) {
1119 for (int i = 0; i < trellis_points[j].size(); i++) {
1120 trellis_points.at(j).at(i) += base_position;
1121 }
1122 }
1123
1124 uint plantID = addPlantInstance(base_position, 0);
1125
1126 uint uID_leader = addBaseStemShoot(plantID, std::ceil(wire_heights.back() / 0.1), make_AxisRotation(context_ptr->randu(0, 0.05 * M_PI), 0, 0), shoot_types.at("trunk").phytomer_parameters.internode.radius_initial.val(), 0.09, 1, 1, 0.1, "trunk");
1127 plant_instances.at(plantID).shoot_tree.at(uID_leader)->meristem_is_alive = false;
1128 removeShootLeaves(plantID, uID_leader);
1129 removeShootVegetativeBuds(plantID, uID_leader);
1130 removeShootFloralBuds(plantID, uID_leader);
1131
1132 for (int i = 0; i < 8; i++) {
1133 float z;
1134 if (i == 0) {
1135 z = wire_heights.front() - 0.1;
1136 } else {
1137 z = context_ptr->randu(wire_heights.front() - 0.1, wire_heights.at(wire_heights.size() - 1));
1138 }
1139 int node = std::round(z / 0.1) - 1;
1140 addChildShoot(plantID, uID_leader, node, 8, make_AxisRotation(context_ptr->randu(float(0.45f * M_PI), 0.52f * M_PI), context_ptr->randu(0, 1) * M_PI, M_PI), 0.005, 0.05, 1, 1, 0.5, "lateral");
1141 }
1142
1143 // Set plant-specific attraction points for this grapevine's trellis system
1144 setPlantAttractionPoints(plantID, flatten(trellis_points), 45.f, 1.f, 0.8);
1145
1146 setPlantPhenologicalThresholds(plantID, 165, -1, 3, 7, 30, 200, false);
1147 plant_instances.at(plantID).max_age = 730;
1148
1149 return plantID;
1150}
1151
1152void PlantArchitecture::initializeAsparagusShoots() {
1153
1154 // ---- Phytomer Parameters ---- //
1155
1156 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
1157
1158 phytomer_parameters.internode.pitch = 1;
1159 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(127.5, 147.5);
1160 phytomer_parameters.internode.radius_initial = 0.00025;
1161 phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
1162 phytomer_parameters.internode.max_vegetative_buds_per_petiole = 1;
1163 phytomer_parameters.internode.color = RGB::forestgreen;
1164 phytomer_parameters.internode.length_segments = 2;
1165
1166 phytomer_parameters.petiole.petioles_per_internode = 1;
1167 phytomer_parameters.petiole.pitch = 90;
1168 phytomer_parameters.petiole.radius = 0.0001;
1169 phytomer_parameters.petiole.length = 0.0005;
1170 phytomer_parameters.petiole.taper = 0.5;
1171 phytomer_parameters.petiole.curvature = 0;
1172 phytomer_parameters.petiole.color = phytomer_parameters.internode.color;
1173 phytomer_parameters.petiole.length_segments = 1;
1174 phytomer_parameters.petiole.radial_subdivisions = 5;
1175
1176 phytomer_parameters.leaf.leaves_per_petiole = 5;
1177 phytomer_parameters.leaf.pitch.normalDistribution(-5, 30);
1178 phytomer_parameters.leaf.yaw = 30;
1179 phytomer_parameters.leaf.roll = 0;
1180 phytomer_parameters.leaf.leaflet_offset = 0;
1181 phytomer_parameters.leaf.leaflet_scale = 0.9;
1182 phytomer_parameters.leaf.prototype.prototype_function = AsparagusLeafPrototype;
1183 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.018, 0.02);
1184 // phytomer_parameters.leaf.subdivisions = 6;
1185
1186 phytomer_parameters.peduncle.length = 0.17;
1187 phytomer_parameters.peduncle.radius = 0.0015;
1188 phytomer_parameters.peduncle.pitch.uniformDistribution(0, 30);
1189 phytomer_parameters.peduncle.roll = 90;
1190 phytomer_parameters.peduncle.curvature.uniformDistribution(50, 250);
1191 phytomer_parameters.peduncle.length_segments = 6;
1192 phytomer_parameters.peduncle.radial_subdivisions = 6;
1193
1194 phytomer_parameters.inflorescence.flowers_per_peduncle.uniformDistribution(1, 3);
1195 phytomer_parameters.inflorescence.flower_offset = 0.;
1196 phytomer_parameters.inflorescence.pitch.uniformDistribution(50, 70);
1197 phytomer_parameters.inflorescence.roll.uniformDistribution(-20, 20);
1198 phytomer_parameters.inflorescence.flower_prototype_scale = 0.015;
1199 phytomer_parameters.inflorescence.fruit_prototype_scale.uniformDistribution(0.02, 0.025);
1200 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction.uniformDistribution(0., 0.5);
1201
1202 // ---- Shoot Parameters ---- //
1203
1204 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
1205 shoot_parameters.phytomer_parameters = phytomer_parameters;
1206 shoot_parameters.phytomer_parameters.phytomer_creation_function = AsparagusPhytomerCreationFunction;
1207
1208 shoot_parameters.max_nodes = 20;
1209 shoot_parameters.insertion_angle_tip.uniformDistribution(40, 70);
1210 // shoot_parameters.child_insertion_angle_decay_rate = 0; (default)
1211 shoot_parameters.internode_length_max = 0.015;
1212 // shoot_parameters.child_internode_length_min = 0.0; (default)
1213 // shoot_parameters.child_internode_length_decay_rate = 0; (default)
1214 shoot_parameters.base_roll = 90;
1215 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
1216 shoot_parameters.gravitropic_curvature = -200;
1217
1218 shoot_parameters.phyllochron_min = 1;
1219 shoot_parameters.elongation_rate_max = 0.15;
1220 // shoot_parameters.girth_growth_rate = 0.00005;
1221 shoot_parameters.girth_area_factor = 30;
1222 shoot_parameters.vegetative_bud_break_time = 5;
1223 shoot_parameters.vegetative_bud_break_probability_min = 0.25;
1224 // shoot_parameters.max_terminal_floral_buds = 0; (default)
1225 // shoot_parameters.flower_bud_break_probability.uniformDistribution(0.1, 0.2);
1226 shoot_parameters.fruit_set_probability = 0.;
1227 // shoot_parameters.flowers_require_dormancy = false; (default)
1228 // shoot_parameters.growth_requires_dormancy = false; (default)
1229 // shoot_parameters.determinate_shoot_growth = true; (default)
1230
1231 shoot_parameters.defineChildShootTypes({"main"}, {1.0});
1232
1233 defineShootType("main", shoot_parameters);
1234}
1235
1236uint PlantArchitecture::buildAsparagusPlant(const helios::vec3 &base_position) {
1237
1238 if (shoot_types.empty()) {
1239 // automatically initialize asparagus plant shoots
1240 initializeAsparagusShoots();
1241 }
1242
1243 uint plantID = addPlantInstance(base_position, 0);
1244
1245 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(0, 0.f, 0.f), 0.0003, 0.015, 1, 0.1, 0.1, "main");
1246
1247 breakPlantDormancy(plantID);
1248
1249 setPlantPhenologicalThresholds(plantID, 0, -1, -1, -1, 5, 100, false);
1250
1251 plant_instances.at(plantID).max_age = 20;
1252
1253 return plantID;
1254}
1255
1256void PlantArchitecture::initializeBindweedShoots() {
1257
1258 // ---- Phytomer Parameters ---- //
1259
1260 PhytomerParameters phytomer_parameters_bindweed(context_ptr->getRandomGenerator());
1261
1262 phytomer_parameters_bindweed.internode.pitch.uniformDistribution(0, 15);
1263 phytomer_parameters_bindweed.internode.phyllotactic_angle = 180.f;
1264 phytomer_parameters_bindweed.internode.radius_initial = 0.0012;
1265 phytomer_parameters_bindweed.internode.color = make_RGBcolor(0.3, 0.38, 0.21);
1266 phytomer_parameters_bindweed.internode.length_segments = 1;
1267
1268 phytomer_parameters_bindweed.petiole.petioles_per_internode = 1;
1269 phytomer_parameters_bindweed.petiole.pitch.uniformDistribution(80, 100);
1270 phytomer_parameters_bindweed.petiole.radius = 0.001;
1271 phytomer_parameters_bindweed.petiole.length = 0.006;
1272 phytomer_parameters_bindweed.petiole.taper = 0;
1273 phytomer_parameters_bindweed.petiole.curvature = 0;
1274 phytomer_parameters_bindweed.petiole.color = phytomer_parameters_bindweed.internode.color;
1275 phytomer_parameters_bindweed.petiole.length_segments = 1;
1276
1277 phytomer_parameters_bindweed.leaf.leaves_per_petiole = 1;
1278 phytomer_parameters_bindweed.leaf.pitch.uniformDistribution(5, 30);
1279 phytomer_parameters_bindweed.leaf.yaw = 0;
1280 phytomer_parameters_bindweed.leaf.roll = 90;
1281 phytomer_parameters_bindweed.leaf.prototype_scale = 0.05;
1282 phytomer_parameters_bindweed.leaf.prototype.OBJ_model_file = "BindweedLeaf.obj";
1283
1284 phytomer_parameters_bindweed.peduncle.length = 0.01;
1285 phytomer_parameters_bindweed.peduncle.radius = 0.0005;
1286 phytomer_parameters_bindweed.peduncle.color = phytomer_parameters_bindweed.internode.color;
1287
1288 phytomer_parameters_bindweed.inflorescence.flowers_per_peduncle = 1;
1289 phytomer_parameters_bindweed.inflorescence.pitch = -90.f;
1290 phytomer_parameters_bindweed.inflorescence.flower_prototype_function = BindweedFlowerPrototype;
1291 phytomer_parameters_bindweed.inflorescence.flower_prototype_scale = 0.045;
1292
1293 // ---- Shoot Parameters ---- //
1294
1295 ShootParameters shoot_parameters_primary(context_ptr->getRandomGenerator());
1296 shoot_parameters_primary.phytomer_parameters = phytomer_parameters_bindweed;
1297 shoot_parameters_primary.vegetative_bud_break_probability_min = 0.1;
1298 shoot_parameters_primary.vegetative_bud_break_probability_decay_rate = -1.;
1299 shoot_parameters_primary.vegetative_bud_break_time = 10;
1300 shoot_parameters_primary.base_roll = 90;
1301 shoot_parameters_primary.phyllochron_min = 1;
1302 shoot_parameters_primary.elongation_rate_max = 0.25;
1303 shoot_parameters_primary.girth_area_factor = 0;
1304 shoot_parameters_primary.internode_length_max = 0.03;
1305 shoot_parameters_primary.internode_length_decay_rate = 0;
1306 shoot_parameters_primary.insertion_angle_tip.uniformDistribution(50, 80);
1307 shoot_parameters_primary.flowers_require_dormancy = false;
1308 shoot_parameters_primary.growth_requires_dormancy = false;
1309 shoot_parameters_primary.flower_bud_break_probability = 0.2;
1310 shoot_parameters_primary.determinate_shoot_growth = false;
1311 shoot_parameters_primary.max_nodes = 15;
1312 shoot_parameters_primary.gravitropic_curvature = 40;
1313 shoot_parameters_primary.tortuosity = 0;
1314 shoot_parameters_primary.defineChildShootTypes({"secondary_bindweed"}, {1.f});
1315
1316 ShootParameters shoot_parameters_base = shoot_parameters_primary;
1317 shoot_parameters_base.phytomer_parameters = phytomer_parameters_bindweed;
1318 shoot_parameters_base.phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(137.5 - 10, 137.5 + 10);
1319 shoot_parameters_base.phytomer_parameters.petiole.petioles_per_internode = 0;
1320 shoot_parameters_base.phytomer_parameters.internode.pitch = 0;
1321 shoot_parameters_base.phytomer_parameters.petiole.pitch = 0;
1322 shoot_parameters_base.vegetative_bud_break_probability_min = 1.0;
1323 shoot_parameters_base.vegetative_bud_break_time = 2;
1324 shoot_parameters_base.phyllochron_min = 2;
1325 shoot_parameters_base.elongation_rate_max = 0.15;
1326 shoot_parameters_base.girth_area_factor = 0.f;
1327 shoot_parameters_base.gravitropic_curvature = 0;
1328 shoot_parameters_base.internode_length_max = 0.01;
1329 shoot_parameters_base.internode_length_decay_rate = 0;
1330 shoot_parameters_base.insertion_angle_tip = 95;
1331 shoot_parameters_base.insertion_angle_decay_rate = 0;
1332 shoot_parameters_base.flowers_require_dormancy = false;
1333 shoot_parameters_base.growth_requires_dormancy = false;
1334 shoot_parameters_base.flower_bud_break_probability = 0.0;
1335 shoot_parameters_base.max_nodes.uniformDistribution(3, 5);
1336 shoot_parameters_base.defineChildShootTypes({"primary_bindweed"}, {1.f});
1337
1338 ShootParameters shoot_parameters_children = shoot_parameters_primary;
1339 shoot_parameters_children.base_roll = 0;
1340
1341 defineShootType("base_bindweed", shoot_parameters_base);
1342 defineShootType("primary_bindweed", shoot_parameters_primary);
1343 defineShootType("secondary_bindweed", shoot_parameters_children);
1344}
1345
1346uint PlantArchitecture::buildBindweedPlant(const helios::vec3 &base_position) {
1347
1348 if (shoot_types.empty()) {
1349 // automatically initialize bindweed plant shoots
1350 initializeBindweedShoots();
1351 }
1352
1353 uint plantID = addPlantInstance(base_position, 0);
1354
1355 uint uID_stem = addBaseStemShoot(plantID, 3, make_AxisRotation(0, 0.f, 0.f), 0.001, 0.001, 1, 1, 0, "base_bindweed");
1356
1357 breakPlantDormancy(plantID);
1358
1359 setPlantPhenologicalThresholds(plantID, 0, -1, 14, -1, -1, 1000, false);
1360
1361 plant_instances.at(plantID).max_age = 50;
1362
1363 return plantID;
1364}
1365
1366void PlantArchitecture::initializeBeanShoots() {
1367
1368 // ---- Leaf Prototype ---- //
1369
1370 LeafPrototype leaf_prototype_trifoliate(context_ptr->getRandomGenerator());
1371 leaf_prototype_trifoliate.leaf_texture_file[0] = "BeanLeaf_tip.png";
1372 leaf_prototype_trifoliate.leaf_texture_file[-1] = "BeanLeaf_left_centered.png";
1373 leaf_prototype_trifoliate.leaf_texture_file[1] = "BeanLeaf_right_centered.png";
1374 leaf_prototype_trifoliate.leaf_aspect_ratio = 1.f;
1375 leaf_prototype_trifoliate.midrib_fold_fraction = 0.2;
1376 leaf_prototype_trifoliate.longitudinal_curvature.uniformDistribution(-0.3f, -0.2f);
1377 leaf_prototype_trifoliate.lateral_curvature = -1.f;
1378 leaf_prototype_trifoliate.subdivisions = 6;
1379 leaf_prototype_trifoliate.unique_prototypes = 5;
1380 leaf_prototype_trifoliate.build_petiolule = true;
1381
1382 LeafPrototype leaf_prototype_unifoliate = leaf_prototype_trifoliate;
1383 leaf_prototype_unifoliate.leaf_texture_file.clear();
1384 leaf_prototype_unifoliate.leaf_texture_file[0] = "BeanLeaf_unifoliate_centered.png";
1385 leaf_prototype_unifoliate.unique_prototypes = 2;
1386
1387 // ---- Phytomer Parameters ---- //
1388
1389 PhytomerParameters phytomer_parameters_trifoliate(context_ptr->getRandomGenerator());
1390
1391 phytomer_parameters_trifoliate.internode.pitch = 20;
1392 phytomer_parameters_trifoliate.internode.phyllotactic_angle.uniformDistribution(145, 215);
1393 phytomer_parameters_trifoliate.internode.radius_initial = 0.001;
1394 phytomer_parameters_trifoliate.internode.max_floral_buds_per_petiole = 1;
1395 phytomer_parameters_trifoliate.internode.max_vegetative_buds_per_petiole = 1;
1396 phytomer_parameters_trifoliate.internode.color = make_RGBcolor(0.2, 0.25, 0.05);
1397 phytomer_parameters_trifoliate.internode.length_segments = 2;
1398
1399 phytomer_parameters_trifoliate.petiole.petioles_per_internode = 1;
1400 phytomer_parameters_trifoliate.petiole.pitch.uniformDistribution(20, 50);
1401 phytomer_parameters_trifoliate.petiole.radius = 0.0015;
1402 phytomer_parameters_trifoliate.petiole.length.uniformDistribution(0.1, 0.14);
1403 phytomer_parameters_trifoliate.petiole.taper = 0.;
1404 phytomer_parameters_trifoliate.petiole.curvature.uniformDistribution(-100, 200);
1405 phytomer_parameters_trifoliate.petiole.color = make_RGBcolor(0.28, 0.35, 0.07);
1406 phytomer_parameters_trifoliate.petiole.length_segments = 5;
1407 phytomer_parameters_trifoliate.petiole.radial_subdivisions = 6;
1408
1409 phytomer_parameters_trifoliate.leaf.leaves_per_petiole = 3;
1410 phytomer_parameters_trifoliate.leaf.pitch.normalDistribution(0, 20);
1411 phytomer_parameters_trifoliate.leaf.yaw = 10;
1412 phytomer_parameters_trifoliate.leaf.roll = -15;
1413 phytomer_parameters_trifoliate.leaf.leaflet_offset = 0.3;
1414 phytomer_parameters_trifoliate.leaf.leaflet_scale = 0.9;
1415 phytomer_parameters_trifoliate.leaf.prototype_scale.uniformDistribution(0.09, 0.11);
1416 phytomer_parameters_trifoliate.leaf.prototype = leaf_prototype_trifoliate;
1417
1418 phytomer_parameters_trifoliate.peduncle.length = 0.04;
1419 phytomer_parameters_trifoliate.peduncle.radius = 0.00075;
1420 phytomer_parameters_trifoliate.peduncle.pitch.uniformDistribution(0, 40);
1421 phytomer_parameters_trifoliate.peduncle.roll = 90;
1422 phytomer_parameters_trifoliate.peduncle.curvature.uniformDistribution(-500, 500);
1423 phytomer_parameters_trifoliate.peduncle.color = phytomer_parameters_trifoliate.petiole.color;
1424 phytomer_parameters_trifoliate.peduncle.length_segments = 1;
1425 phytomer_parameters_trifoliate.peduncle.radial_subdivisions = 6;
1426
1427 phytomer_parameters_trifoliate.inflorescence.flowers_per_peduncle.uniformDistribution(1, 4);
1428 phytomer_parameters_trifoliate.inflorescence.flower_offset = 0.2;
1429 phytomer_parameters_trifoliate.inflorescence.pitch.uniformDistribution(50, 70);
1430 phytomer_parameters_trifoliate.inflorescence.roll.uniformDistribution(-20, 20);
1431 phytomer_parameters_trifoliate.inflorescence.flower_prototype_scale = 0.03;
1432 phytomer_parameters_trifoliate.inflorescence.flower_prototype_function = BeanFlowerPrototype;
1433 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_scale.uniformDistribution(0.15, 0.2);
1434 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_function = BeanFruitPrototype;
1435 phytomer_parameters_trifoliate.inflorescence.fruit_gravity_factor_fraction.uniformDistribution(0.8, 1.0);
1436
1437 PhytomerParameters phytomer_parameters_unifoliate = phytomer_parameters_trifoliate;
1438 phytomer_parameters_unifoliate.internode.pitch = 0;
1439 phytomer_parameters_unifoliate.internode.max_vegetative_buds_per_petiole = 1;
1440 phytomer_parameters_unifoliate.internode.max_floral_buds_per_petiole = 0;
1441 phytomer_parameters_unifoliate.petiole.petioles_per_internode = 2;
1442 phytomer_parameters_unifoliate.petiole.length = 0.0001;
1443 phytomer_parameters_unifoliate.petiole.radius = 0.0004;
1444 phytomer_parameters_unifoliate.petiole.pitch.uniformDistribution(50, 70);
1445 phytomer_parameters_unifoliate.leaf.leaves_per_petiole = 1;
1446 phytomer_parameters_unifoliate.leaf.prototype_scale = 0.04;
1447 phytomer_parameters_unifoliate.leaf.pitch.uniformDistribution(-10, 10);
1448 phytomer_parameters_unifoliate.leaf.prototype = leaf_prototype_unifoliate;
1449
1450 // ---- Shoot Parameters ---- //
1451
1452 ShootParameters shoot_parameters_trifoliate(context_ptr->getRandomGenerator());
1453 shoot_parameters_trifoliate.phytomer_parameters = phytomer_parameters_trifoliate;
1454 shoot_parameters_trifoliate.phytomer_parameters.phytomer_creation_function = BeanPhytomerCreationFunction;
1455
1456 shoot_parameters_trifoliate.max_nodes = 25;
1457 shoot_parameters_trifoliate.insertion_angle_tip.uniformDistribution(40, 60);
1458 // shoot_parameters_trifoliate.child_insertion_angle_decay_rate = 0; (default)
1459 shoot_parameters_trifoliate.internode_length_max = 0.025;
1460 // shoot_parameters_trifoliate.child_internode_length_min = 0.0; (default)
1461 // shoot_parameters_trifoliate.child_internode_length_decay_rate = 0; (default)
1462 shoot_parameters_trifoliate.base_roll = 90;
1463 shoot_parameters_trifoliate.base_yaw.uniformDistribution(-20, 20);
1464 shoot_parameters_trifoliate.gravitropic_curvature = 200;
1465
1466 shoot_parameters_trifoliate.phyllochron_min = 2;
1467 shoot_parameters_trifoliate.elongation_rate_max = 0.1;
1468 shoot_parameters_trifoliate.girth_area_factor = 1.5f;
1469 shoot_parameters_trifoliate.vegetative_bud_break_time = 15;
1470 shoot_parameters_trifoliate.vegetative_bud_break_probability_min = 0.1;
1471 shoot_parameters_trifoliate.vegetative_bud_break_probability_decay_rate = -0.4;
1472 // shoot_parameters_trifoliate.max_terminal_floral_buds = 0; (default)
1473 shoot_parameters_trifoliate.flower_bud_break_probability.uniformDistribution(0.3, 0.4);
1474 shoot_parameters_trifoliate.fruit_set_probability = 0.4;
1475 // shoot_parameters_trifoliate.flowers_require_dormancy = false; (default)
1476 // shoot_parameters_trifoliate.growth_requires_dormancy = false; (default)
1477 // shoot_parameters_trifoliate.determinate_shoot_growth = true; (default)
1478
1479 shoot_parameters_trifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
1480
1481
1482 ShootParameters shoot_parameters_unifoliate = shoot_parameters_trifoliate;
1483 shoot_parameters_unifoliate.phytomer_parameters = phytomer_parameters_unifoliate;
1484 shoot_parameters_unifoliate.phytomer_parameters.phytomer_creation_function = nullptr;
1485 shoot_parameters_unifoliate.max_nodes = 1;
1486 shoot_parameters_unifoliate.girth_area_factor = 1.f;
1487 shoot_parameters_unifoliate.vegetative_bud_break_probability_min = 1.0;
1488 shoot_parameters_unifoliate.flower_bud_break_probability = 0;
1489 shoot_parameters_unifoliate.insertion_angle_tip = 50;
1490 shoot_parameters_unifoliate.insertion_angle_decay_rate = 0;
1491 shoot_parameters_unifoliate.vegetative_bud_break_time = 8;
1492 shoot_parameters_unifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
1493
1494 defineShootType("unifoliate", shoot_parameters_unifoliate);
1495 defineShootType("trifoliate", shoot_parameters_trifoliate);
1496}
1497
1498uint PlantArchitecture::buildBeanPlant(const helios::vec3 &base_position) {
1499
1500 if (shoot_types.empty()) {
1501 // automatically initialize bean plant shoots
1502 initializeBeanShoots();
1503 }
1504
1505 uint plantID = addPlantInstance(base_position, 0);
1506
1507 AxisRotation base_rotation = make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
1508 uint uID_unifoliate = addBaseStemShoot(plantID, 1, base_rotation, 0.0005, 0.03, 0.01, 0.01, 0, "unifoliate");
1509
1510 appendShoot(plantID, uID_unifoliate, 1, make_AxisRotation(0, 0, 0.5f * M_PI), shoot_types.at("trifoliate").phytomer_parameters.internode.radius_initial.val(), shoot_types.at("trifoliate").internode_length_max.val(), 0.1, 0.1, 0, "trifoliate");
1511
1512 breakPlantDormancy(plantID);
1513
1514 setPlantPhenologicalThresholds(plantID, 0, 40, 5, 5, 30, 1000, false);
1515
1516 plant_instances.at(plantID).max_age = 365;
1517
1518 return plantID;
1519}
1520
1521void PlantArchitecture::initializeBougainvilleaShoots() {
1522
1523 // ---- Leaf Prototype ---- //
1524
1525 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
1526 leaf_prototype.leaf_texture_file[0] = "CapsicumLeaf.png";
1527 leaf_prototype.leaf_aspect_ratio = 0.8f;
1528 leaf_prototype.midrib_fold_fraction = 0.2f;
1529 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.15, -0.05f);
1530 leaf_prototype.lateral_curvature = -0.25f;
1531 leaf_prototype.wave_period = 0.35f;
1532 leaf_prototype.wave_amplitude = 0.1f;
1533 leaf_prototype.subdivisions = 7;
1534 leaf_prototype.unique_prototypes = 5;
1535
1536 // ---- Phytomer Parameters ---- //
1537
1538 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
1539
1540 phytomer_parameters.internode.pitch = 10;
1541 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(160, 190);
1542 phytomer_parameters.internode.radius_initial = 0.001;
1543 phytomer_parameters.internode.color = make_RGBcolor(0.213, 0.270, 0.056);
1544 phytomer_parameters.internode.length_segments = 1;
1545
1546 phytomer_parameters.petiole.petioles_per_internode = 1;
1547 phytomer_parameters.petiole.pitch.uniformDistribution(-60, -40);
1548 phytomer_parameters.petiole.radius = 0.0001;
1549 phytomer_parameters.petiole.length = 0.0001;
1550 phytomer_parameters.petiole.taper = 1;
1551 phytomer_parameters.petiole.curvature = 0;
1552 phytomer_parameters.petiole.color = phytomer_parameters.internode.color;
1553 phytomer_parameters.petiole.length_segments = 1;
1554
1555 phytomer_parameters.leaf.leaves_per_petiole = 1;
1556 phytomer_parameters.leaf.pitch = 0;
1557 phytomer_parameters.leaf.yaw = 10;
1558 phytomer_parameters.leaf.roll = 0;
1559 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.08, 0.12);
1560 phytomer_parameters.leaf.prototype = leaf_prototype;
1561
1562 phytomer_parameters.peduncle.length = 0.15;
1563 phytomer_parameters.peduncle.radius = 0.0015;
1564 phytomer_parameters.peduncle.pitch.uniformDistribution(70, 90);
1565 phytomer_parameters.peduncle.roll.uniformDistribution(0, 180);
1566 phytomer_parameters.peduncle.curvature.uniformDistribution(-200, 200);
1567 phytomer_parameters.peduncle.color = phytomer_parameters.internode.color;
1568 phytomer_parameters.peduncle.length_segments = 3;
1569 phytomer_parameters.peduncle.radial_subdivisions = 6;
1570
1571 phytomer_parameters.inflorescence.flowers_per_peduncle.uniformDistribution(4, 6);
1572 phytomer_parameters.inflorescence.pitch = 80;
1573 phytomer_parameters.inflorescence.roll.uniformDistribution(-180, 180);
1574 phytomer_parameters.inflorescence.flower_prototype_scale = 0.05;
1575 phytomer_parameters.inflorescence.flower_prototype_function = BougainvilleaFlowerPrototype;
1576 phytomer_parameters.inflorescence.unique_prototypes = 1;
1577 phytomer_parameters.inflorescence.flower_offset = 0.12;
1578
1579 // ---- Shoot Parameters ---- //
1580
1581 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
1582 shoot_parameters.phytomer_parameters = phytomer_parameters;
1583
1584 shoot_parameters.max_nodes = 60;
1585 shoot_parameters.insertion_angle_tip = 25;
1586 shoot_parameters.insertion_angle_decay_rate = 0;
1587 shoot_parameters.internode_length_max = 0.04;
1588 shoot_parameters.internode_length_min = 0.0;
1589 shoot_parameters.internode_length_decay_rate = 0;
1590 shoot_parameters.base_roll = 90;
1591 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
1592 shoot_parameters.gravitropic_curvature = 500;
1593 shoot_parameters.tortuosity = 3;
1594
1595 shoot_parameters.phyllochron_min = 2.75;
1596 shoot_parameters.elongation_rate_max = 0.1;
1597 shoot_parameters.girth_area_factor = 1.f;
1598 shoot_parameters.vegetative_bud_break_time = 30;
1599 shoot_parameters.vegetative_bud_break_probability_min = 0.075;
1600 shoot_parameters.vegetative_bud_break_probability_decay_rate = 0.8;
1601 shoot_parameters.flower_bud_break_probability = 0.5;
1602 shoot_parameters.flowers_require_dormancy = false;
1603 shoot_parameters.growth_requires_dormancy = false;
1604 shoot_parameters.determinate_shoot_growth = false;
1605
1606 shoot_parameters.defineChildShootTypes({"secondary"}, {1.0});
1607
1608 defineShootType("mainstem", shoot_parameters);
1609
1610 ShootParameters shoot_parameters_secondary = shoot_parameters;
1611 shoot_parameters_secondary.max_nodes = 15;
1612
1613 defineShootType("secondary", shoot_parameters_secondary);
1614}
1615
1616uint PlantArchitecture::buildBougainvilleaPlant(const helios::vec3 &base_position) {
1617
1618 if (shoot_types.empty()) {
1619 // automatically initialize bougainvillea plant shoots
1620 initializeBougainvilleaShoots();
1621 }
1622
1623 uint plantID = addPlantInstance(base_position, 0);
1624
1625 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
1626 uint uID_stem = addBaseStemShoot(plantID, 3, base_rotation, 0.002, shoot_types.at("mainstem").internode_length_max.val(), 0.01, 0.01, 0, "mainstem");
1627
1628 removeShootVegetativeBuds(plantID, uID_stem);
1629 removeShootFloralBuds(plantID, uID_stem);
1630 removeShootLeaves(plantID, uID_stem);
1631
1632 breakPlantDormancy(plantID);
1633
1634 setPlantPhenologicalThresholds(plantID, 0, -1, 75, 1000, 1000, 1000, false);
1635
1636 plant_instances.at(plantID).max_age = 365;
1637
1638 return plantID;
1639}
1640
1641void PlantArchitecture::initializeCapsicumShoots() {
1642
1643 // ---- Leaf Prototype ---- //
1644
1645 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
1646 leaf_prototype.leaf_texture_file[0] = "CapsicumLeaf.png";
1647 leaf_prototype.leaf_aspect_ratio = 0.45f;
1648 leaf_prototype.midrib_fold_fraction = 0.1f;
1649 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.15, -0.05f);
1650 leaf_prototype.lateral_curvature = -0.15f;
1651 leaf_prototype.wave_period = 0.35f;
1652 leaf_prototype.wave_amplitude = 0.0f;
1653 leaf_prototype.subdivisions = 5;
1654 leaf_prototype.unique_prototypes = 5;
1655
1656 // ---- Phytomer Parameters ---- //
1657
1658 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
1659
1660 phytomer_parameters.internode.pitch = 10;
1661 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(137.5 - 10, 137.5 + 10);
1662 phytomer_parameters.internode.radius_initial = 0.001;
1663 phytomer_parameters.internode.color = make_RGBcolor(0.213, 0.270, 0.056);
1664 phytomer_parameters.internode.length_segments = 1;
1665
1666 phytomer_parameters.petiole.petioles_per_internode = 1;
1667 phytomer_parameters.petiole.pitch.uniformDistribution(-60, -40);
1668 phytomer_parameters.petiole.radius = 0.0001;
1669 phytomer_parameters.petiole.length = 0.0001;
1670 phytomer_parameters.petiole.taper = 1;
1671 phytomer_parameters.petiole.curvature = 0;
1672 phytomer_parameters.petiole.color = phytomer_parameters.internode.color;
1673 phytomer_parameters.petiole.length_segments = 1;
1674
1675 phytomer_parameters.leaf.leaves_per_petiole = 1;
1676 phytomer_parameters.leaf.pitch = 0;
1677 phytomer_parameters.leaf.yaw = 10;
1678 phytomer_parameters.leaf.roll = 0;
1679 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.12, 0.15);
1680 phytomer_parameters.leaf.prototype = leaf_prototype;
1681
1682 phytomer_parameters.peduncle.length = 0.01;
1683 phytomer_parameters.peduncle.radius = 0.001;
1684 phytomer_parameters.peduncle.pitch.uniformDistribution(10, 30);
1685 phytomer_parameters.peduncle.roll = 0;
1686 phytomer_parameters.peduncle.curvature = -700;
1687 phytomer_parameters.peduncle.color = phytomer_parameters.internode.color;
1688 phytomer_parameters.peduncle.length_segments = 3;
1689 phytomer_parameters.peduncle.radial_subdivisions = 6;
1690
1691 phytomer_parameters.inflorescence.flowers_per_peduncle = 1;
1692 phytomer_parameters.inflorescence.pitch = 20;
1693 phytomer_parameters.inflorescence.roll.uniformDistribution(-30, 30);
1694 phytomer_parameters.inflorescence.flower_prototype_scale = 0.025;
1695 phytomer_parameters.inflorescence.flower_prototype_function = AlmondFlowerPrototype;
1696 phytomer_parameters.inflorescence.fruit_prototype_scale.uniformDistribution(0.12, 0.16);
1697 phytomer_parameters.inflorescence.fruit_prototype_function = CapsicumFruitPrototype;
1698 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction = 0.9;
1699 phytomer_parameters.inflorescence.unique_prototypes = 10;
1700
1701 PhytomerParameters phytomer_parameters_secondary = phytomer_parameters;
1702
1703 // ---- Shoot Parameters ---- //
1704
1705 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
1706 shoot_parameters.phytomer_parameters = phytomer_parameters;
1707 shoot_parameters.phytomer_parameters.phytomer_creation_function = CapsicumPhytomerCreationFunction;
1708
1709 shoot_parameters.max_nodes = 30;
1710 shoot_parameters.insertion_angle_tip = 35;
1711 shoot_parameters.insertion_angle_decay_rate = 0;
1712 shoot_parameters.internode_length_max = 0.04;
1713 shoot_parameters.internode_length_min = 0.0;
1714 shoot_parameters.internode_length_decay_rate = 0;
1715 shoot_parameters.base_roll = 90;
1716 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
1717 shoot_parameters.gravitropic_curvature = 300;
1718 shoot_parameters.tortuosity = 3;
1719
1720 shoot_parameters.phyllochron_min = 3;
1721 shoot_parameters.elongation_rate_max = 0.1;
1722 shoot_parameters.girth_area_factor = 2.f;
1723 shoot_parameters.vegetative_bud_break_time = 30;
1724 shoot_parameters.vegetative_bud_break_probability_min = 0.4;
1725 shoot_parameters.vegetative_bud_break_probability_decay_rate = 0;
1726 shoot_parameters.flower_bud_break_probability = 0.5;
1727 shoot_parameters.fruit_set_probability = 0.05;
1728 shoot_parameters.flowers_require_dormancy = false;
1729 shoot_parameters.growth_requires_dormancy = false;
1730 shoot_parameters.determinate_shoot_growth = true;
1731
1732 shoot_parameters.defineChildShootTypes({"secondary"}, {1.0});
1733
1734 defineShootType("mainstem", shoot_parameters);
1735
1736 ShootParameters shoot_parameters_secondary = shoot_parameters;
1737 shoot_parameters_secondary.phytomer_parameters = phytomer_parameters_secondary;
1738 shoot_parameters_secondary.max_nodes = 7;
1739 shoot_parameters_secondary.phyllochron_min = 6;
1740 shoot_parameters_secondary.vegetative_bud_break_probability_min = 0.2;
1741
1742 defineShootType("secondary", shoot_parameters_secondary);
1743}
1744
1745uint PlantArchitecture::buildCapsicumPlant(const helios::vec3 &base_position) {
1746
1747 if (shoot_types.empty()) {
1748 // automatically initialize capsicum plant shoots
1749 initializeCapsicumShoots();
1750 }
1751
1752 uint plantID = addPlantInstance(base_position, 0);
1753
1754 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
1755 uint uID_stem = addBaseStemShoot(plantID, 1, base_rotation, 0.002, shoot_types.at("mainstem").internode_length_max.val(), 0.01, 0.01, 0, "mainstem");
1756
1757 breakPlantDormancy(plantID);
1758
1759 setPlantPhenologicalThresholds(plantID, 0, -1, -1, 75, 14, 1000, false);
1760
1761 plant_instances.at(plantID).max_age = 365;
1762
1763 return plantID;
1764}
1765
1766void PlantArchitecture::initializeCheeseweedShoots() {
1767
1768 // ---- Leaf Prototype ---- //
1769
1770 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
1771 leaf_prototype.OBJ_model_file = "CheeseweedLeaf.obj";
1772
1773 // ---- Phytomer Parameters ---- //
1774
1775 PhytomerParameters phytomer_parameters_cheeseweed(context_ptr->getRandomGenerator());
1776
1777 phytomer_parameters_cheeseweed.internode.pitch = 0;
1778 phytomer_parameters_cheeseweed.internode.phyllotactic_angle.uniformDistribution(127.5f, 147.5);
1779 phytomer_parameters_cheeseweed.internode.radius_initial = 0.0005;
1780 phytomer_parameters_cheeseweed.internode.color = make_RGBcolor(0.60, 0.65, 0.40);
1781 phytomer_parameters_cheeseweed.internode.length_segments = 1;
1782
1783 phytomer_parameters_cheeseweed.petiole.petioles_per_internode = 1;
1784 phytomer_parameters_cheeseweed.petiole.pitch.uniformDistribution(45, 75);
1785 phytomer_parameters_cheeseweed.petiole.radius = 0.0005;
1786 phytomer_parameters_cheeseweed.petiole.length.uniformDistribution(0.02, 0.06);
1787 phytomer_parameters_cheeseweed.petiole.taper = 0;
1788 phytomer_parameters_cheeseweed.petiole.curvature = -300;
1789 phytomer_parameters_cheeseweed.petiole.length_segments = 5;
1790 phytomer_parameters_cheeseweed.petiole.color = phytomer_parameters_cheeseweed.internode.color;
1791
1792 phytomer_parameters_cheeseweed.leaf.leaves_per_petiole = 1;
1793 phytomer_parameters_cheeseweed.leaf.pitch.uniformDistribution(-30, 0);
1794 phytomer_parameters_cheeseweed.leaf.yaw = 0;
1795 phytomer_parameters_cheeseweed.leaf.roll = 0;
1796 phytomer_parameters_cheeseweed.leaf.prototype_scale = 0.035;
1797 phytomer_parameters_cheeseweed.leaf.prototype = leaf_prototype;
1798
1799 // ---- Shoot Parameters ---- //
1800
1801 ShootParameters shoot_parameters_base(context_ptr->getRandomGenerator());
1802 shoot_parameters_base.phytomer_parameters = phytomer_parameters_cheeseweed;
1803 shoot_parameters_base.vegetative_bud_break_probability_min = 0.2;
1804 shoot_parameters_base.vegetative_bud_break_time = 6;
1805 shoot_parameters_base.phyllochron_min = 2;
1806 shoot_parameters_base.elongation_rate_max = 0.1;
1807 shoot_parameters_base.girth_area_factor = 10.f;
1808 shoot_parameters_base.gravitropic_curvature = 0;
1809 shoot_parameters_base.internode_length_max = 0.0015;
1810 shoot_parameters_base.internode_length_decay_rate = 0;
1811 shoot_parameters_base.flowers_require_dormancy = false;
1812 shoot_parameters_base.growth_requires_dormancy = false;
1813 shoot_parameters_base.flower_bud_break_probability = 0.;
1814 shoot_parameters_base.max_nodes = 8;
1815
1816 defineShootType("base", shoot_parameters_base);
1817}
1818
1819uint PlantArchitecture::buildCheeseweedPlant(const helios::vec3 &base_position) {
1820
1821 if (shoot_types.empty()) {
1822 // automatically initialize cheeseweed plant shoots
1823 initializeCheeseweedShoots();
1824 }
1825
1826 uint plantID = addPlantInstance(base_position, 0);
1827
1828 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(0, 0.f, 0.f), 0.0001, 0.0025, 0.1, 0.1, 0, "base");
1829
1830 breakPlantDormancy(plantID);
1831
1832 setPlantPhenologicalThresholds(plantID, 0, -1, -1, -1, -1, 1000, false);
1833
1834 plant_instances.at(plantID).max_age = 40;
1835
1836 return plantID;
1837}
1838
1839void PlantArchitecture::initializeCowpeaShoots() {
1840
1841 // ---- Leaf Prototype ---- //
1842
1843 LeafPrototype leaf_prototype_trifoliate(context_ptr->getRandomGenerator());
1844 leaf_prototype_trifoliate.leaf_texture_file[0] = "CowpeaLeaf_tip_centered.png";
1845 leaf_prototype_trifoliate.leaf_texture_file[-1] = "CowpeaLeaf_left_centered.png";
1846 leaf_prototype_trifoliate.leaf_texture_file[1] = "CowpeaLeaf_right_centered.png";
1847 leaf_prototype_trifoliate.leaf_aspect_ratio = 0.7f;
1848 leaf_prototype_trifoliate.midrib_fold_fraction = 0.2;
1849 leaf_prototype_trifoliate.longitudinal_curvature.uniformDistribution(-0.3f, -0.1f);
1850 leaf_prototype_trifoliate.lateral_curvature = -0.4f;
1851 leaf_prototype_trifoliate.subdivisions = 6;
1852 leaf_prototype_trifoliate.unique_prototypes = 5;
1853 leaf_prototype_trifoliate.build_petiolule = true;
1854
1855 LeafPrototype leaf_prototype_unifoliate = leaf_prototype_trifoliate;
1856 leaf_prototype_unifoliate.leaf_texture_file.clear();
1857 leaf_prototype_unifoliate.leaf_texture_file[0] = "CowpeaLeaf_unifoliate_centered.png";
1858
1859 // ---- Phytomer Parameters ---- //
1860
1861 PhytomerParameters phytomer_parameters_trifoliate(context_ptr->getRandomGenerator());
1862
1863 phytomer_parameters_trifoliate.internode.pitch = 20;
1864 phytomer_parameters_trifoliate.internode.phyllotactic_angle.uniformDistribution(145, 215);
1865 phytomer_parameters_trifoliate.internode.radius_initial = 0.0015;
1866 phytomer_parameters_trifoliate.internode.max_floral_buds_per_petiole = 1;
1867 phytomer_parameters_trifoliate.internode.max_vegetative_buds_per_petiole = 1;
1868 phytomer_parameters_trifoliate.internode.color = make_RGBcolor(0.15, 0.2, 0.1);
1869 phytomer_parameters_trifoliate.internode.length_segments = 2;
1870
1871 phytomer_parameters_trifoliate.petiole.petioles_per_internode = 1;
1872 phytomer_parameters_trifoliate.petiole.pitch.uniformDistribution(45, 60);
1873 phytomer_parameters_trifoliate.petiole.radius = 0.0018;
1874 phytomer_parameters_trifoliate.petiole.length.uniformDistribution(0.06, 0.08);
1875 phytomer_parameters_trifoliate.petiole.taper = 0.25;
1876 phytomer_parameters_trifoliate.petiole.curvature.uniformDistribution(-200, -50);
1877 phytomer_parameters_trifoliate.petiole.color = make_RGBcolor(0.2, 0.25, 0.06);
1878 phytomer_parameters_trifoliate.petiole.length_segments = 5;
1879 phytomer_parameters_trifoliate.petiole.radial_subdivisions = 6;
1880
1881 phytomer_parameters_trifoliate.leaf.leaves_per_petiole = 3;
1882 phytomer_parameters_trifoliate.leaf.pitch.normalDistribution(45, 20);
1883 phytomer_parameters_trifoliate.leaf.yaw = 10;
1884 phytomer_parameters_trifoliate.leaf.roll = -15;
1885 phytomer_parameters_trifoliate.leaf.leaflet_offset = 0.4;
1886 phytomer_parameters_trifoliate.leaf.leaflet_scale = 0.9;
1887 phytomer_parameters_trifoliate.leaf.prototype_scale.uniformDistribution(0.09, 0.12);
1888 phytomer_parameters_trifoliate.leaf.prototype = leaf_prototype_trifoliate;
1889
1890 phytomer_parameters_trifoliate.peduncle.length.uniformDistribution(0.3, 0.4);
1891 phytomer_parameters_trifoliate.peduncle.radius = 0.00225;
1892 phytomer_parameters_trifoliate.peduncle.pitch.uniformDistribution(0, 30);
1893 phytomer_parameters_trifoliate.peduncle.roll = 90;
1894 phytomer_parameters_trifoliate.peduncle.curvature.uniformDistribution(125, 200);
1895 phytomer_parameters_trifoliate.peduncle.color = make_RGBcolor(0.17, 0.213, 0.051);
1896 phytomer_parameters_trifoliate.peduncle.length_segments = 6;
1897 phytomer_parameters_trifoliate.peduncle.radial_subdivisions = 6;
1898
1899 phytomer_parameters_trifoliate.inflorescence.flowers_per_peduncle.uniformDistribution(1, 3);
1900 phytomer_parameters_trifoliate.inflorescence.flower_offset = 0.05;
1901 phytomer_parameters_trifoliate.inflorescence.pitch.uniformDistribution(40, 60);
1902 phytomer_parameters_trifoliate.inflorescence.roll.uniformDistribution(-20, 20);
1903 phytomer_parameters_trifoliate.inflorescence.flower_prototype_scale = 0.03;
1904 phytomer_parameters_trifoliate.inflorescence.flower_prototype_function = CowpeaFlowerPrototype;
1905 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_scale.uniformDistribution(0.09, 0.1);
1906 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_function = CowpeaFruitPrototype;
1907 phytomer_parameters_trifoliate.inflorescence.fruit_gravity_factor_fraction.uniformDistribution(0.5, 0.7);
1908
1909 PhytomerParameters phytomer_parameters_unifoliate = phytomer_parameters_trifoliate;
1910 phytomer_parameters_unifoliate.internode.pitch = 0;
1911 phytomer_parameters_unifoliate.internode.max_vegetative_buds_per_petiole = 1;
1912 phytomer_parameters_unifoliate.internode.max_floral_buds_per_petiole = 0;
1913 phytomer_parameters_unifoliate.petiole.petioles_per_internode = 2;
1914 phytomer_parameters_unifoliate.petiole.length = 0.0001;
1915 phytomer_parameters_unifoliate.petiole.radius = 0.0004;
1916 phytomer_parameters_unifoliate.petiole.pitch.uniformDistribution(60, 80);
1917 phytomer_parameters_unifoliate.leaf.leaves_per_petiole = 1;
1918 phytomer_parameters_unifoliate.leaf.prototype_scale = 0.02;
1919 phytomer_parameters_unifoliate.leaf.pitch.uniformDistribution(-10, 10);
1920 phytomer_parameters_unifoliate.leaf.prototype = leaf_prototype_unifoliate;
1921
1922 // ---- Shoot Parameters ---- //
1923
1924 ShootParameters shoot_parameters_trifoliate(context_ptr->getRandomGenerator());
1925 shoot_parameters_trifoliate.phytomer_parameters = phytomer_parameters_trifoliate;
1926 shoot_parameters_trifoliate.phytomer_parameters.phytomer_creation_function = CowpeaPhytomerCreationFunction;
1927
1928 shoot_parameters_trifoliate.max_nodes = 20;
1929 shoot_parameters_trifoliate.insertion_angle_tip.uniformDistribution(40, 60);
1930 // shoot_parameters_trifoliate.child_insertion_angle_decay_rate = 0; (default)
1931 shoot_parameters_trifoliate.internode_length_max = 0.025;
1932 // shoot_parameters_trifoliate.child_internode_length_min = 0.0; (default)
1933 // shoot_parameters_trifoliate.child_internode_length_decay_rate = 0; (default)
1934 shoot_parameters_trifoliate.base_roll = 90;
1935 shoot_parameters_trifoliate.base_yaw.uniformDistribution(-20, 20);
1936 shoot_parameters_trifoliate.gravitropic_curvature = 200;
1937
1938 shoot_parameters_trifoliate.phyllochron_min = 2;
1939 shoot_parameters_trifoliate.elongation_rate_max = 0.1;
1940 shoot_parameters_trifoliate.girth_area_factor = 1.5f;
1941 shoot_parameters_trifoliate.vegetative_bud_break_time = 10;
1942 shoot_parameters_trifoliate.vegetative_bud_break_probability_min = 0.2;
1943 shoot_parameters_trifoliate.vegetative_bud_break_probability_decay_rate = -0.4;
1944 // shoot_parameters_trifoliate.max_terminal_floral_buds = 0; (default)
1945 shoot_parameters_trifoliate.flower_bud_break_probability.uniformDistribution(0.1, 0.15);
1946 shoot_parameters_trifoliate.fruit_set_probability = 0.4;
1947 // shoot_parameters_trifoliate.flowers_require_dormancy = false; (default)
1948 // shoot_parameters_trifoliate.growth_requires_dormancy = false; (default)
1949 // shoot_parameters_trifoliate.determinate_shoot_growth = true; (default)
1950
1951 shoot_parameters_trifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
1952
1953
1954 ShootParameters shoot_parameters_unifoliate = shoot_parameters_trifoliate;
1955 shoot_parameters_unifoliate.phytomer_parameters = phytomer_parameters_unifoliate;
1956 shoot_parameters_unifoliate.max_nodes = 1;
1957 shoot_parameters_unifoliate.girth_area_factor = 1.f;
1958 shoot_parameters_unifoliate.vegetative_bud_break_probability_min = 1;
1959 shoot_parameters_unifoliate.flower_bud_break_probability = 0;
1960 shoot_parameters_unifoliate.insertion_angle_tip = 40;
1961 shoot_parameters_unifoliate.insertion_angle_decay_rate = 0;
1962 shoot_parameters_unifoliate.vegetative_bud_break_time = 8;
1963 shoot_parameters_unifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
1964
1965 defineShootType("unifoliate", shoot_parameters_unifoliate);
1966 defineShootType("trifoliate", shoot_parameters_trifoliate);
1967}
1968
1969uint PlantArchitecture::buildCowpeaPlant(const helios::vec3 &base_position) {
1970
1971 if (shoot_types.empty()) {
1972 // automatically initialize cowpea plant shoots
1973 initializeCowpeaShoots();
1974 }
1975
1976 uint plantID = addPlantInstance(base_position, 0);
1977
1978 AxisRotation base_rotation = make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
1979 uint uID_unifoliate = addBaseStemShoot(plantID, 1, base_rotation, 0.0005, 0.03, 0.01, 0.01, 0, "unifoliate");
1980
1981 appendShoot(plantID, uID_unifoliate, 1, make_AxisRotation(0, 0, 0.5f * M_PI), shoot_types.at("trifoliate").phytomer_parameters.internode.radius_initial.val(), shoot_types.at("trifoliate").internode_length_max.val(), 0.1, 0.1, 0, "trifoliate");
1982
1983 breakPlantDormancy(plantID);
1984
1985 setPlantPhenologicalThresholds(plantID, 0, 40, 5, 5, 30, 1000, false);
1986
1987 plant_instances.at(plantID).max_age = 365;
1988
1989 return plantID;
1990}
1991
1992void PlantArchitecture::initializeGrapevineVSPShoots() {
1993
1994 // ---- Leaf Prototype ---- //
1995
1996 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
1997 leaf_prototype.leaf_texture_file[0] = "GrapeLeaf.png";
1998 leaf_prototype.leaf_aspect_ratio = 1.f;
1999 leaf_prototype.midrib_fold_fraction = 0.3f;
2000 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.4, 0.4);
2001 leaf_prototype.lateral_curvature = 0;
2002 leaf_prototype.wave_period = 0.3f;
2003 leaf_prototype.wave_amplitude = 0.1f;
2004 leaf_prototype.subdivisions = 5;
2005 leaf_prototype.unique_prototypes = 10;
2006 leaf_prototype.leaf_offset = make_vec3(-0.3, 0, 0);
2007
2008 // ---- Phytomer Parameters ---- //
2009
2010 PhytomerParameters phytomer_parameters_grapevine(context_ptr->getRandomGenerator());
2011
2012 phytomer_parameters_grapevine.internode.pitch = 20;
2013 phytomer_parameters_grapevine.internode.phyllotactic_angle.uniformDistribution(160, 200);
2014 phytomer_parameters_grapevine.internode.radius_initial = 0.003;
2015 phytomer_parameters_grapevine.internode.color = make_RGBcolor(0.23, 0.13, 0.062);
2016 phytomer_parameters_grapevine.internode.length_segments = 1;
2017 phytomer_parameters_grapevine.internode.max_floral_buds_per_petiole = 1;
2018 phytomer_parameters_grapevine.internode.max_vegetative_buds_per_petiole = 1;
2019
2020 phytomer_parameters_grapevine.petiole.petioles_per_internode = 1;
2021 phytomer_parameters_grapevine.petiole.color = make_RGBcolor(0.13, 0.066, 0.03);
2022 phytomer_parameters_grapevine.petiole.pitch.uniformDistribution(45, 70);
2023 phytomer_parameters_grapevine.petiole.radius = 0.0025;
2024 phytomer_parameters_grapevine.petiole.length = 0.1;
2025 phytomer_parameters_grapevine.petiole.taper = 0;
2026 phytomer_parameters_grapevine.petiole.curvature = 0;
2027 phytomer_parameters_grapevine.petiole.length_segments = 1;
2028
2029 phytomer_parameters_grapevine.leaf.leaves_per_petiole = 1;
2030 phytomer_parameters_grapevine.leaf.pitch.uniformDistribution(-110, -80);
2031 phytomer_parameters_grapevine.leaf.yaw.uniformDistribution(-20, 20);
2032 phytomer_parameters_grapevine.leaf.roll.uniformDistribution(-5, 5);
2033 phytomer_parameters_grapevine.leaf.prototype_scale = 0.2;
2034 phytomer_parameters_grapevine.leaf.prototype = leaf_prototype;
2035
2036 phytomer_parameters_grapevine.peduncle.length = 0.08;
2037 phytomer_parameters_grapevine.peduncle.pitch.uniformDistribution(50, 90);
2038 phytomer_parameters_grapevine.peduncle.color = make_RGBcolor(0.32, 0.05, 0.13);
2039
2040 phytomer_parameters_grapevine.inflorescence.flowers_per_peduncle = 1;
2041 phytomer_parameters_grapevine.inflorescence.pitch = 0;
2042 // phytomer_parameters_grapevine.inflorescence.flower_prototype_function = GrapevineFlowerPrototype;
2043 phytomer_parameters_grapevine.inflorescence.flower_prototype_scale = 0.04;
2044 phytomer_parameters_grapevine.inflorescence.fruit_prototype_function = GrapevineFruitPrototype;
2045 phytomer_parameters_grapevine.inflorescence.fruit_prototype_scale = 0.04;
2046 phytomer_parameters_grapevine.inflorescence.fruit_gravity_factor_fraction = 0.7;
2047
2048 phytomer_parameters_grapevine.phytomer_creation_function = GrapevinePhytomerCreationFunction;
2049 // phytomer_parameters_grapevine.phytomer_callback_function = GrapevinePhytomerCallbackFunction;
2050
2051 // ---- Shoot Parameters ---- //
2052
2053 ShootParameters shoot_parameters_main(context_ptr->getRandomGenerator());
2054 shoot_parameters_main.phytomer_parameters = phytomer_parameters_grapevine;
2055 shoot_parameters_main.vegetative_bud_break_probability_min = 0.075;
2056 shoot_parameters_main.vegetative_bud_break_probability_decay_rate = 1.;
2057 shoot_parameters_main.vegetative_bud_break_time = 30;
2058 shoot_parameters_main.phyllochron_min.uniformDistribution(1.75, 2.25);
2059 shoot_parameters_main.elongation_rate_max = 0.15;
2060 shoot_parameters_main.girth_area_factor = 1.f;
2061 shoot_parameters_main.gravitropic_curvature = 400;
2062 shoot_parameters_main.tortuosity = 15;
2063 shoot_parameters_main.internode_length_max.uniformDistribution(0.06, 0.08);
2064 shoot_parameters_main.internode_length_decay_rate = 0;
2065 shoot_parameters_main.insertion_angle_tip = 45;
2066 shoot_parameters_main.insertion_angle_decay_rate = 0;
2067 shoot_parameters_main.flowers_require_dormancy = false;
2068 shoot_parameters_main.growth_requires_dormancy = false;
2069 shoot_parameters_main.determinate_shoot_growth = false;
2070 shoot_parameters_main.max_terminal_floral_buds = 0;
2071 shoot_parameters_main.flower_bud_break_probability = 0.5;
2072 shoot_parameters_main.fruit_set_probability = 0.2;
2073 shoot_parameters_main.max_nodes = 20;
2074 shoot_parameters_main.base_roll.uniformDistribution(90 - 25, 90 + 25);
2075 shoot_parameters_main.base_yaw = 0;
2076
2077 ShootParameters shoot_parameters_cane = shoot_parameters_main;
2078 // shoot_parameters_cane.phytomer_parameters.internode.image_texture = "GrapeBark.jpg";
2079 shoot_parameters_cane.phytomer_parameters.internode.pitch = 0;
2080 shoot_parameters_cane.phytomer_parameters.internode.radial_subdivisions = 15;
2081 shoot_parameters_cane.phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
2082 shoot_parameters_cane.phytomer_parameters.internode.phyllotactic_angle = 0;
2083 shoot_parameters_cane.insertion_angle_tip.uniformDistribution(60, 120);
2084 shoot_parameters_cane.girth_area_factor = 0.7f;
2085 shoot_parameters_cane.max_nodes = 9;
2086 shoot_parameters_cane.gravitropic_curvature.uniformDistribution(-20, 20);
2087 shoot_parameters_cane.tortuosity = 1;
2088 shoot_parameters_cane.gravitropic_curvature = 10;
2089 shoot_parameters_cane.vegetative_bud_break_probability_min = 0.9;
2090 shoot_parameters_cane.defineChildShootTypes({"grapevine_shoot"}, {1.f});
2091
2092 ShootParameters shoot_parameters_trunk = shoot_parameters_main;
2093 shoot_parameters_trunk.phytomer_parameters.internode.image_texture = "GrapeBark.jpg";
2094 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
2095 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
2096 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.05;
2097 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 25;
2098 shoot_parameters_trunk.phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
2099 shoot_parameters_trunk.phyllochron_min = 2.5;
2100 shoot_parameters_trunk.insertion_angle_tip = 90;
2101 shoot_parameters_trunk.girth_area_factor = 0;
2102 shoot_parameters_trunk.max_nodes = 18;
2103 shoot_parameters_trunk.tortuosity = 0;
2104 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
2105 shoot_parameters_trunk.defineChildShootTypes({"grapevine_shoot"}, {1.f});
2106
2107 defineShootType("grapevine_trunk", shoot_parameters_trunk);
2108 defineShootType("grapevine_cane", shoot_parameters_cane);
2109 defineShootType("grapevine_shoot", shoot_parameters_main);
2110}
2111
2112uint PlantArchitecture::buildGrapevineVSP(const helios::vec3 &base_position) {
2113
2114 if (shoot_types.empty()) {
2115 // automatically initialize grapevine plant shoots
2116 initializeGrapevineVSPShoots();
2117 }
2118
2119 // Get training system parameters
2120 auto vine_spacing = getParameterValue(current_build_parameters, "vine_spacing", 2.4f, 0.5f, 5.f, "plant-to-plant spacing in meters");
2121 auto trunk_height = getParameterValue(current_build_parameters, "trunk_height", 0.1f, 0.05f, 1.f, "total trunk height in meters");
2122
2123 // Calculate trunk nodes based on desired height
2124 float trunk_internode_length = 0.1f;
2125 uint trunk_nodes = uint(trunk_height / trunk_internode_length);
2126 if (trunk_nodes < 1)
2127 trunk_nodes = 1;
2128
2129 // Calculate cane nodes to span to neighboring plant
2130 float cane_internode_length = 0.15f;
2131 float cane_total_length = vine_spacing / 2.f; // Cane extends from center to next plant
2132 uint cane_nodes = uint(cane_total_length / cane_internode_length);
2133 if (cane_nodes < 1)
2134 cane_nodes = 1;
2135
2136 // Fixed training parameters (not user-customizable)
2137 float cane_radius = 0.005f;
2138 float cane_pitch_min = float(0.45f * M_PI);
2139 float cane_pitch_max = float(0.52f * M_PI);
2140
2141 uint plantID = addPlantInstance(base_position, 0);
2142
2143 uint uID_stem = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(context_ptr->randu(0, 0.05 * M_PI), 0, 0), shoot_types.at("grapevine_trunk").phytomer_parameters.internode.radius_initial.val(), trunk_internode_length, 1, 1, 0.1,
2144 "grapevine_trunk");
2145
2146 uint uID_cane_L = appendShoot(plantID, uID_stem, cane_nodes, make_AxisRotation(context_ptr->randu(cane_pitch_min, cane_pitch_max), 0, M_PI), cane_radius, cane_internode_length, 1, 1, 0.5, "grapevine_cane");
2147 uint uID_cane_R = appendShoot(plantID, uID_stem, cane_nodes, make_AxisRotation(context_ptr->randu(cane_pitch_min, cane_pitch_max), M_PI, M_PI), cane_radius, cane_internode_length, 1, 1, 0.5, "grapevine_cane");
2148
2149 // makePlantDormant(plantID);
2150
2151 removeShootLeaves(plantID, uID_stem);
2152 removeShootLeaves(plantID, uID_cane_L);
2153 removeShootLeaves(plantID, uID_cane_R);
2154
2155 setPlantPhenologicalThresholds(plantID, 165, -1, -1, 45, 45, 200, false);
2156
2157 plant_instances.at(plantID).max_age = 365;
2158
2159 return plantID;
2160}
2161
2162void PlantArchitecture::initializeGrapevineWyeShoots() {
2163
2164 // ---- Leaf Prototype ---- //
2165
2166 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2167 leaf_prototype.leaf_texture_file[0] = "GrapeLeaf.png";
2168 leaf_prototype.leaf_aspect_ratio = 1.f;
2169 leaf_prototype.midrib_fold_fraction = 0.3f;
2170 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.4, 0.4);
2171 leaf_prototype.lateral_curvature = 0;
2172 leaf_prototype.wave_period = 0.3f;
2173 leaf_prototype.wave_amplitude = 0.1f;
2174 leaf_prototype.subdivisions = 5;
2175 leaf_prototype.unique_prototypes = 10;
2176 leaf_prototype.leaf_offset = make_vec3(-0.3, 0, 0);
2177
2178 // ---- Phytomer Parameters ---- //
2179
2180 PhytomerParameters phytomer_parameters_grapevine(context_ptr->getRandomGenerator());
2181
2182 phytomer_parameters_grapevine.internode.pitch = 20;
2183 phytomer_parameters_grapevine.internode.phyllotactic_angle.uniformDistribution(160, 200);
2184 phytomer_parameters_grapevine.internode.radius_initial = 0.003;
2185 phytomer_parameters_grapevine.internode.color = make_RGBcolor(0.23, 0.13, 0.062);
2186 phytomer_parameters_grapevine.internode.length_segments = 1;
2187 phytomer_parameters_grapevine.internode.max_floral_buds_per_petiole = 1;
2188 phytomer_parameters_grapevine.internode.max_vegetative_buds_per_petiole = 1;
2189
2190 phytomer_parameters_grapevine.petiole.petioles_per_internode = 1;
2191 phytomer_parameters_grapevine.petiole.color = make_RGBcolor(0.13, 0.125, 0.03);
2192 phytomer_parameters_grapevine.petiole.pitch.uniformDistribution(45, 70);
2193 phytomer_parameters_grapevine.petiole.radius = 0.0025;
2194 phytomer_parameters_grapevine.petiole.length = 0.1;
2195 phytomer_parameters_grapevine.petiole.taper = 0;
2196 phytomer_parameters_grapevine.petiole.curvature = 0;
2197 phytomer_parameters_grapevine.petiole.length_segments = 1;
2198
2199 phytomer_parameters_grapevine.leaf.leaves_per_petiole = 1;
2200 phytomer_parameters_grapevine.leaf.pitch.uniformDistribution(-110, -80);
2201 phytomer_parameters_grapevine.leaf.yaw.uniformDistribution(-20, 20);
2202 phytomer_parameters_grapevine.leaf.roll.uniformDistribution(-5, 5);
2203 phytomer_parameters_grapevine.leaf.prototype_scale = 0.2;
2204 phytomer_parameters_grapevine.leaf.prototype = leaf_prototype;
2205
2206 phytomer_parameters_grapevine.peduncle.length = 0.04;
2207 phytomer_parameters_grapevine.peduncle.radius = 0.005;
2208 phytomer_parameters_grapevine.peduncle.color = make_RGBcolor(0.13, 0.125, 0.03);
2209 phytomer_parameters_grapevine.peduncle.pitch.uniformDistribution(80, 100);
2210
2211 phytomer_parameters_grapevine.inflorescence.flowers_per_peduncle = 1;
2212 phytomer_parameters_grapevine.inflorescence.pitch = 0;
2213 // phytomer_parameters_grapevine.inflorescence.flower_prototype_function = GrapevineFlowerPrototype;
2214 phytomer_parameters_grapevine.inflorescence.flower_prototype_scale = 0.04;
2215 phytomer_parameters_grapevine.inflorescence.fruit_prototype_function = GrapevineFruitPrototype;
2216 phytomer_parameters_grapevine.inflorescence.fruit_prototype_scale = 0.04;
2217 phytomer_parameters_grapevine.inflorescence.fruit_gravity_factor_fraction = 0.9;
2218
2219 phytomer_parameters_grapevine.phytomer_creation_function = GrapevinePhytomerCreationFunction;
2220 // phytomer_parameters_grapevine.phytomer_callback_function = GrapevinePhytomerCallbackFunction;
2221
2222 // ---- Shoot Parameters ---- //
2223
2224 ShootParameters shoot_parameters_main(context_ptr->getRandomGenerator());
2225 shoot_parameters_main.phytomer_parameters = phytomer_parameters_grapevine;
2226 shoot_parameters_main.vegetative_bud_break_probability_min = 0.025;
2227 shoot_parameters_main.vegetative_bud_break_probability_decay_rate = -1.;
2228 shoot_parameters_main.vegetative_bud_break_time = 30;
2229 shoot_parameters_main.phyllochron_min.uniformDistribution(2.5, 3.5);
2230 shoot_parameters_main.elongation_rate_max = 0.15;
2231 shoot_parameters_main.girth_area_factor = 0.8f;
2232 shoot_parameters_main.gravitropic_curvature.uniformDistribution(0, 100);
2233 shoot_parameters_main.tortuosity = 10;
2234 shoot_parameters_main.internode_length_max.uniformDistribution(0.06, 0.08);
2235 shoot_parameters_main.internode_length_decay_rate = 0;
2236 shoot_parameters_main.insertion_angle_tip = 45;
2237 shoot_parameters_main.insertion_angle_decay_rate = 0;
2238 shoot_parameters_main.flowers_require_dormancy = false;
2239 shoot_parameters_main.growth_requires_dormancy = false;
2240 shoot_parameters_main.determinate_shoot_growth = false;
2241 shoot_parameters_main.max_terminal_floral_buds = 0;
2242 shoot_parameters_main.flower_bud_break_probability = 0.5;
2243 shoot_parameters_main.fruit_set_probability = 0.2;
2244 shoot_parameters_main.max_nodes.uniformDistribution(14, 18);
2245 shoot_parameters_main.base_roll.uniformDistribution(90 - 25, 90 + 25);
2246 shoot_parameters_main.base_yaw.uniformDistribution(-50, 50);
2247
2248 ShootParameters shoot_parameters_cordon = shoot_parameters_main;
2249 shoot_parameters_cordon.phytomer_parameters.internode.image_texture = "GrapeBark.jpg";
2250 shoot_parameters_cordon.phytomer_parameters.internode.pitch = 0;
2251 shoot_parameters_cordon.phytomer_parameters.internode.radial_subdivisions = 15;
2252 shoot_parameters_cordon.phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
2253 shoot_parameters_cordon.phytomer_parameters.internode.phyllotactic_angle = 0;
2254 shoot_parameters_cordon.insertion_angle_tip.uniformDistribution(60, 120);
2255 shoot_parameters_cordon.girth_area_factor = 3.5f;
2256 shoot_parameters_cordon.max_nodes = 8;
2257 shoot_parameters_cordon.tortuosity = 1;
2258 shoot_parameters_cordon.gravitropic_curvature = 0;
2259 shoot_parameters_cordon.vegetative_bud_break_probability_min = 0.9;
2260 shoot_parameters_cordon.base_yaw = 0;
2261 shoot_parameters_cordon.defineChildShootTypes({"grapevine_shoot"}, {1.f});
2262
2263 ShootParameters shoot_parameters_trunk = shoot_parameters_main;
2264 shoot_parameters_trunk.phytomer_parameters.internode.image_texture = "GrapeBark.jpg";
2265 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
2266 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
2267 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.05;
2268 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 25;
2269 shoot_parameters_trunk.phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
2270 shoot_parameters_trunk.phyllochron_min = 2.5;
2271 shoot_parameters_trunk.insertion_angle_tip = 90;
2272 shoot_parameters_trunk.girth_area_factor = 0;
2273 shoot_parameters_trunk.max_nodes = 18;
2274 shoot_parameters_trunk.tortuosity = 2;
2275 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
2276 shoot_parameters_trunk.defineChildShootTypes({"grapevine_shoot"}, {1.f});
2277
2278 defineShootType("grapevine_trunk", shoot_parameters_trunk);
2279 defineShootType("grapevine_cordon", shoot_parameters_cordon);
2280 defineShootType("grapevine_shoot", shoot_parameters_main);
2281}
2282
2283uint PlantArchitecture::buildGrapevineWye(const helios::vec3 &base_position) {
2284
2285 if (shoot_types.empty()) {
2286 // automatically initialize grapevine plant shoots
2287 initializeGrapevineWyeShoots();
2288 }
2289
2290 // Get training system parameters
2291 auto trunk_height_total = getParameterValue(current_build_parameters, "trunk_height", 0.165f, 0.05f, 1.f, "total trunk height in meters");
2292 auto cordon_spacing = getParameterValue(current_build_parameters, "cordon_spacing", 0.6f, 0.2f, 2.f, "spacing between cordon rows in meters");
2293 auto vine_spacing = getParameterValue(current_build_parameters, "vine_spacing", 1.8f, 0.5f, 5.f, "plant-to-plant spacing in meters");
2294 auto catch_wire_height = getParameterValue(current_build_parameters, "catch_wire_height", 2.1f, 0.5f, 4.f, "absolute height of catch wires in meters");
2295
2296 // Calculate trunk nodes based on desired height
2297 float trunk_internode_length = 0.165f / 8.f; // Original was 8 nodes * 0.165m total
2298 uint trunk_nodes = uint(trunk_height_total / trunk_internode_length);
2299 if (trunk_nodes < 1)
2300 trunk_nodes = 1;
2301
2302 // Calculate trellis head height from catch wire height (catch wires above fruiting wires)
2303 float head_height = catch_wire_height - 0.35f; // Offset to match original geometry
2304
2305 // Fixed training parameters (not user-customizable)
2306 uint upright_nodes = 3;
2307 float upright_pitch_min = 42.f;
2308 float upright_pitch_max = 48.f;
2309 float upright_radius = 0.03f;
2310 float upright_length = 0.14f;
2311 uint cordon_nodes = 8;
2312 float cordon_radius = 0.02f;
2313 float cordon_length = 0.11f;
2314 float catch_wire_offset_1 = 0.15f;
2315 float catch_wire_offset_2 = 0.35f;
2316
2317 std::vector<std::vector<vec3>> trellis_points;
2318
2319 // fruiting wires
2320 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, -0.5f * cordon_spacing, head_height), make_vec3(0.5f * vine_spacing, -0.5f * cordon_spacing, head_height), 8));
2321 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, 0.5f * cordon_spacing, head_height), make_vec3(0.5f * vine_spacing, 0.5f * cordon_spacing, head_height), 8));
2322
2323 // first catch wires (these don't exist in a real Wye trellis, but are needed to keep the vines from falling through the wires)
2324 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, -0.5f * cordon_spacing - catch_wire_offset_1, catch_wire_height - catch_wire_offset_2),
2325 make_vec3(0.5f * vine_spacing, -0.5f * cordon_spacing - catch_wire_offset_1, catch_wire_height - catch_wire_offset_2), 8));
2326 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, 0.5f * cordon_spacing + catch_wire_offset_1, catch_wire_height - catch_wire_offset_2),
2327 make_vec3(0.5f * vine_spacing, 0.5f * cordon_spacing + catch_wire_offset_1, catch_wire_height - catch_wire_offset_2), 8));
2328
2329 // second catch wires (at specified height)
2330 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, -0.5f * cordon_spacing - catch_wire_offset_2, catch_wire_height), make_vec3(0.5f * vine_spacing, -0.5f * cordon_spacing - catch_wire_offset_2, catch_wire_height), 8));
2331 trellis_points.push_back(linspace(make_vec3(-0.5f * vine_spacing, 0.5f * cordon_spacing + catch_wire_offset_2, catch_wire_height), make_vec3(0.5f * vine_spacing, 0.5f * cordon_spacing + catch_wire_offset_2, catch_wire_height), 8));
2332
2333 for (int j = 0; j < trellis_points.size(); j++) {
2334 for (int i = 0; i < trellis_points[j].size(); i++) {
2335 trellis_points.at(j).at(i) += base_position;
2336 }
2337 }
2338
2339 uint plantID = addPlantInstance(base_position, 0);
2340
2341 // Set plant-specific attraction points for this grapevine's trellis system
2342 setPlantAttractionPoints(plantID, flatten(trellis_points), 45.f, 0.5f, 0.5);
2343
2344 uint uID_stem = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(0., 0, 0), shoot_types.at("grapevine_trunk").phytomer_parameters.internode.radius_initial.val(), trunk_internode_length, 1, 1, 0.1, "grapevine_trunk");
2345
2346 uint uID_upright_L = appendShoot(plantID, uID_stem, upright_nodes, make_AxisRotation(deg2rad(context_ptr->randu(upright_pitch_min, upright_pitch_max)), 0, M_PI), upright_radius, upright_length, 1, 1, 0.2, "grapevine_trunk");
2347 uint uID_upright_R = appendShoot(plantID, uID_stem, upright_nodes, make_AxisRotation(deg2rad(context_ptr->randu(upright_pitch_min, upright_pitch_max)), M_PI, M_PI), upright_radius, upright_length, 1, 1, 0.2, "grapevine_trunk");
2348
2349 uint uID_cordon_L1 = appendShoot(plantID, uID_upright_L, cordon_nodes, make_AxisRotation(deg2rad(-90), 0.5 * M_PI, -0.2), cordon_radius, cordon_length, 1, 1, 0.5, "grapevine_cordon");
2350 uint uID_cordon_L2 = appendShoot(plantID, uID_upright_L, cordon_nodes, make_AxisRotation(deg2rad(-90), -0.5 * M_PI, 0.2), cordon_radius, cordon_length, 1, 1, 0.5, "grapevine_cordon");
2351
2352 uint uID_cordon_R1 = appendShoot(plantID, uID_upright_R, cordon_nodes, make_AxisRotation(deg2rad(-90), 0.5 * M_PI, 0.2), cordon_radius, cordon_length, 1, 1, 0.5, "grapevine_cordon");
2353 uint uID_cordon_R2 = appendShoot(plantID, uID_upright_R, cordon_nodes, make_AxisRotation(deg2rad(-90), -0.5 * M_PI, -0.2), cordon_radius, cordon_length, 1, 1, 0.5, "grapevine_cordon");
2354
2355 removeShootLeaves(plantID, uID_stem);
2356 removeShootLeaves(plantID, uID_upright_L);
2357 removeShootLeaves(plantID, uID_upright_R);
2358 removeShootLeaves(plantID, uID_cordon_L1);
2359 removeShootLeaves(plantID, uID_cordon_L2);
2360 removeShootLeaves(plantID, uID_cordon_R1);
2361 removeShootLeaves(plantID, uID_cordon_R2);
2362
2363 removeShootVegetativeBuds(plantID, uID_upright_L);
2364 removeShootVegetativeBuds(plantID, uID_upright_R);
2365
2366 setPlantPhenologicalThresholds(plantID, 165, -1, -1, 45, 45, 200, false);
2367
2368 plant_instances.at(plantID).max_age = 365;
2369
2370 return plantID;
2371}
2372
2373void PlantArchitecture::initializeGroundCherryWeedShoots() {
2374
2375 // ---- Leaf Prototype ---- //
2376
2377 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2378 leaf_prototype.leaf_texture_file[0] = "GroundCherryLeaf.png";
2379 leaf_prototype.leaf_aspect_ratio.uniformDistribution(0.3, 0.5);
2380 leaf_prototype.midrib_fold_fraction = 0.2f;
2381 leaf_prototype.longitudinal_curvature = 0.1f;
2382 ;
2383 leaf_prototype.lateral_curvature = -0.3f;
2384 leaf_prototype.wave_period = 0.35f;
2385 leaf_prototype.wave_amplitude = 0.08f;
2386 leaf_prototype.subdivisions = 6;
2387 leaf_prototype.unique_prototypes = 5;
2388
2389 // ---- Phytomer Parameters ---- //
2390
2391 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
2392
2393 phytomer_parameters.internode.pitch = 5;
2394 phytomer_parameters.internode.phyllotactic_angle = 137.5;
2395 phytomer_parameters.internode.radius_initial = 0.0005;
2396 phytomer_parameters.internode.color = make_RGBcolor(0.266, 0.3375, 0.0700);
2397 phytomer_parameters.internode.length_segments = 1;
2398
2399 phytomer_parameters.petiole.petioles_per_internode = 1;
2400 phytomer_parameters.petiole.pitch.uniformDistribution(45, 60);
2401 phytomer_parameters.petiole.radius = 0.0005;
2402 phytomer_parameters.petiole.length = 0.025;
2403 phytomer_parameters.petiole.taper = 0.15;
2404 phytomer_parameters.petiole.curvature.uniformDistribution(-150, -50);
2405 phytomer_parameters.petiole.color = phytomer_parameters.internode.color;
2406 phytomer_parameters.petiole.length_segments = 2;
2407
2408 phytomer_parameters.leaf.leaves_per_petiole = 1;
2409 phytomer_parameters.leaf.pitch.uniformDistribution(-30, 5);
2410 phytomer_parameters.leaf.yaw = 10;
2411 phytomer_parameters.leaf.roll = 0;
2412 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.06, 0.08);
2413 phytomer_parameters.leaf.prototype = leaf_prototype;
2414
2415 phytomer_parameters.peduncle.length = 0.01;
2416 phytomer_parameters.peduncle.radius = 0.001;
2417 phytomer_parameters.peduncle.pitch = 20;
2418 phytomer_parameters.peduncle.roll = 0;
2419 phytomer_parameters.peduncle.curvature = -700;
2420 phytomer_parameters.peduncle.color = phytomer_parameters.internode.color;
2421 phytomer_parameters.peduncle.length_segments = 2;
2422 phytomer_parameters.peduncle.radial_subdivisions = 6;
2423
2424 phytomer_parameters.inflorescence.flowers_per_peduncle = 1;
2425 phytomer_parameters.inflorescence.pitch = 0;
2426 phytomer_parameters.inflorescence.roll.uniformDistribution(-30, 30);
2427 phytomer_parameters.inflorescence.flower_prototype_scale = 0.01;
2428 phytomer_parameters.inflorescence.flower_prototype_function = BindweedFlowerPrototype;
2429 phytomer_parameters.inflorescence.fruit_prototype_scale = 0.06;
2430 // phytomer_parameters.inflorescence.fruit_prototype_function = GroundCherryFruitPrototype;
2431 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction = 0.75;
2432
2433 // ---- Shoot Parameters ---- //
2434
2435 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
2436 shoot_parameters.phytomer_parameters = phytomer_parameters;
2437 // shoot_parameters.phytomer_parameters.phytomer_creation_function = TomatoPhytomerCreationFunction;
2438
2439 shoot_parameters.max_nodes = 26;
2440 shoot_parameters.insertion_angle_tip = 50;
2441 shoot_parameters.insertion_angle_decay_rate = 0;
2442 shoot_parameters.internode_length_max = 0.015;
2443 shoot_parameters.internode_length_min = 0.0;
2444 shoot_parameters.internode_length_decay_rate = 0;
2445 shoot_parameters.base_roll = 90;
2446 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
2447 shoot_parameters.gravitropic_curvature = 700;
2448 shoot_parameters.tortuosity = 3;
2449
2450 shoot_parameters.phyllochron_min = 1;
2451 shoot_parameters.elongation_rate_max = 0.1;
2452 shoot_parameters.girth_area_factor = 2.f;
2453 shoot_parameters.vegetative_bud_break_time = 10;
2454 shoot_parameters.vegetative_bud_break_probability_min = 0.1;
2455 shoot_parameters.vegetative_bud_break_probability_decay_rate = -0.5;
2456 shoot_parameters.flower_bud_break_probability = 0.25;
2457 shoot_parameters.fruit_set_probability = 0.5;
2458 shoot_parameters.flowers_require_dormancy = false;
2459 shoot_parameters.growth_requires_dormancy = false;
2460 shoot_parameters.determinate_shoot_growth = false;
2461
2462 shoot_parameters.defineChildShootTypes({"mainstem"}, {1.0});
2463
2464 defineShootType("mainstem", shoot_parameters);
2465}
2466
2467uint PlantArchitecture::buildGroundCherryWeedPlant(const helios::vec3 &base_position) {
2468
2469 if (shoot_types.empty()) {
2470 // automatically initialize ground cherry plant shoots
2471 initializeGroundCherryWeedShoots();
2472 }
2473
2474 uint plantID = addPlantInstance(base_position, 0);
2475
2476 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
2477 uint uID_stem = addBaseStemShoot(plantID, 1, base_rotation, 0.0025, 0.018, 0.01, 0.01, 0, "mainstem");
2478
2479 breakPlantDormancy(plantID);
2480
2481 setPlantPhenologicalThresholds(plantID, 0, 20, -1, 20, 30, 1000, false);
2482
2483 plant_instances.at(plantID).max_age = 80;
2484
2485 return plantID;
2486}
2487
2488void PlantArchitecture::initializeMaizeShoots() {
2489
2490 // ---- Leaf Prototype ---- //
2491
2492 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2493 leaf_prototype.leaf_texture_file[0] = "SorghumLeaf.png";
2494 leaf_prototype.leaf_aspect_ratio = 0.25f;
2495 leaf_prototype.midrib_fold_fraction = 0.3f;
2496 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.45, -0.3);
2497 leaf_prototype.lateral_curvature = -0.3f;
2498 ;
2499 leaf_prototype.petiole_roll = 0.04f;
2500 leaf_prototype.wave_period = 0.1f;
2501 leaf_prototype.wave_amplitude = 0.1f;
2502 leaf_prototype.leaf_buckle_length.uniformDistribution(0.4, 0.6);
2503 leaf_prototype.leaf_buckle_angle.uniformDistribution(40, 50);
2504 leaf_prototype.subdivisions = 50;
2505 leaf_prototype.unique_prototypes = 10;
2506
2507 // ---- Phytomer Parameters ---- //
2508
2509 PhytomerParameters phytomer_parameters_maize(context_ptr->getRandomGenerator());
2510
2511 phytomer_parameters_maize.internode.pitch = 0;
2512 phytomer_parameters_maize.internode.phyllotactic_angle.uniformDistribution(170, 190);
2513 phytomer_parameters_maize.internode.radius_initial = 0.0075;
2514 phytomer_parameters_maize.internode.color = make_RGBcolor(0.126, 0.182, 0.084);
2515 phytomer_parameters_maize.internode.length_segments = 2;
2516 phytomer_parameters_maize.internode.radial_subdivisions = 10;
2517 phytomer_parameters_maize.internode.max_floral_buds_per_petiole = 1;
2518 phytomer_parameters_maize.internode.max_vegetative_buds_per_petiole = 0;
2519
2520 phytomer_parameters_maize.petiole.petioles_per_internode = 1;
2521 phytomer_parameters_maize.petiole.pitch.uniformDistribution(-40, -20);
2522 phytomer_parameters_maize.petiole.radius = 0.0;
2523 phytomer_parameters_maize.petiole.length = 0.05;
2524 phytomer_parameters_maize.petiole.taper = 0;
2525 phytomer_parameters_maize.petiole.curvature = 0;
2526 phytomer_parameters_maize.petiole.length_segments = 1;
2527
2528 phytomer_parameters_maize.leaf.leaves_per_petiole = 1;
2529 phytomer_parameters_maize.leaf.pitch = 0;
2530 phytomer_parameters_maize.leaf.yaw = 0;
2531 phytomer_parameters_maize.leaf.roll = 0;
2532 phytomer_parameters_maize.leaf.prototype_scale = 0.6;
2533 phytomer_parameters_maize.leaf.prototype = leaf_prototype;
2534
2535 phytomer_parameters_maize.peduncle.length = 0.14f;
2536 phytomer_parameters_maize.peduncle.radius = 0.004;
2537 phytomer_parameters_maize.peduncle.curvature = 0;
2538 phytomer_parameters_maize.peduncle.color = phytomer_parameters_maize.internode.color;
2539 phytomer_parameters_maize.peduncle.radial_subdivisions = 6;
2540 phytomer_parameters_maize.peduncle.length_segments = 2;
2541
2542 phytomer_parameters_maize.inflorescence.flowers_per_peduncle = 7;
2543 phytomer_parameters_maize.inflorescence.pitch.uniformDistribution(0, 30);
2544 phytomer_parameters_maize.inflorescence.roll = 0;
2545 phytomer_parameters_maize.inflorescence.flower_offset = 0.1;
2546 phytomer_parameters_maize.inflorescence.fruit_prototype_scale = 0.15;
2547 phytomer_parameters_maize.inflorescence.fruit_prototype_function = MaizeTasselPrototype;
2548
2549 phytomer_parameters_maize.phytomer_creation_function = MaizePhytomerCreationFunction;
2550
2551 // ---- Shoot Parameters ---- //
2552
2553 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
2554 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters_maize;
2555 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0.5;
2556 shoot_parameters_mainstem.flower_bud_break_probability = 1;
2557 shoot_parameters_mainstem.phyllochron_min = 2;
2558 shoot_parameters_mainstem.elongation_rate_max = 0.1;
2559 shoot_parameters_mainstem.girth_area_factor = 6.f;
2560 shoot_parameters_mainstem.gravitropic_curvature.uniformDistribution(-500, 0);
2561 shoot_parameters_mainstem.internode_length_max = 0.22;
2562 shoot_parameters_mainstem.tortuosity = 1.f;
2563 shoot_parameters_mainstem.internode_length_decay_rate = 0;
2564 shoot_parameters_mainstem.flowers_require_dormancy = false;
2565 shoot_parameters_mainstem.growth_requires_dormancy = false;
2566 shoot_parameters_mainstem.determinate_shoot_growth = false;
2567 shoot_parameters_mainstem.flower_bud_break_probability = 1.0;
2568 shoot_parameters_mainstem.fruit_set_probability = 1.0;
2569 shoot_parameters_mainstem.defineChildShootTypes({"mainstem"}, {1.0});
2570 shoot_parameters_mainstem.max_nodes = 17;
2571 shoot_parameters_mainstem.max_terminal_floral_buds = 1;
2572
2573 defineShootType("mainstem", shoot_parameters_mainstem);
2574}
2575
2576uint PlantArchitecture::buildMaizePlant(const helios::vec3 &base_position) {
2577
2578 if (shoot_types.empty()) {
2579 // automatically initialize maize plant shoots
2580 initializeMaizeShoots();
2581 }
2582
2583 uint plantID = addPlantInstance(base_position, 0);
2584
2585 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(context_ptr->randu(0.f, 0.035f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)), 0.003, 0.08, 0.01, 0.01, 0.2, "mainstem");
2586
2587 breakPlantDormancy(plantID);
2588
2589 setPlantPhenologicalThresholds(plantID, 0, -1, -1, 4, 10, 1000, false);
2590
2591 plant_instances.at(plantID).max_age = 365;
2592
2593 return plantID;
2594}
2595
2596void PlantArchitecture::initializeOliveTreeShoots() {
2597
2598 // ---- Leaf Prototype ---- //
2599
2600 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2601 leaf_prototype.prototype_function = OliveLeafPrototype;
2602 leaf_prototype.unique_prototypes = 1;
2603
2604 // ---- Phytomer Parameters ---- //
2605
2606 PhytomerParameters phytomer_parameters_olive(context_ptr->getRandomGenerator());
2607
2608 phytomer_parameters_olive.internode.pitch = 0;
2609 phytomer_parameters_olive.internode.phyllotactic_angle.uniformDistribution(80, 100);
2610 phytomer_parameters_olive.internode.radius_initial = 0.002;
2611 phytomer_parameters_olive.internode.length_segments = 1;
2612 phytomer_parameters_olive.internode.image_texture = "OliveBark.jpg";
2613 phytomer_parameters_olive.internode.max_floral_buds_per_petiole = 3;
2614
2615 phytomer_parameters_olive.petiole.petioles_per_internode = 2;
2616 phytomer_parameters_olive.petiole.pitch.uniformDistribution(-40, -20);
2617 phytomer_parameters_olive.petiole.taper = 0.1;
2618 phytomer_parameters_olive.petiole.curvature = 0;
2619 phytomer_parameters_olive.petiole.length = 0.01;
2620 phytomer_parameters_olive.petiole.radius = 0.0005;
2621 phytomer_parameters_olive.petiole.length_segments = 1;
2622 phytomer_parameters_olive.petiole.radial_subdivisions = 3;
2623 phytomer_parameters_olive.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
2624
2625 phytomer_parameters_olive.leaf.leaves_per_petiole = 1;
2626 phytomer_parameters_olive.leaf.prototype_scale = 0.06;
2627 phytomer_parameters_olive.leaf.prototype = leaf_prototype;
2628
2629 phytomer_parameters_olive.peduncle.length = 0.065;
2630 phytomer_parameters_olive.peduncle.radius = 0.001;
2631 phytomer_parameters_olive.peduncle.pitch = 60;
2632 phytomer_parameters_olive.peduncle.roll = 0;
2633 phytomer_parameters_olive.peduncle.length_segments = 1;
2634 phytomer_parameters_olive.peduncle.color = make_RGBcolor(0.7, 0.72, 0.7);
2635
2636 phytomer_parameters_olive.inflorescence.flowers_per_peduncle = 10;
2637 phytomer_parameters_olive.inflorescence.flower_offset = 0.13;
2638 phytomer_parameters_olive.inflorescence.pitch.uniformDistribution(80, 100);
2639 phytomer_parameters_olive.inflorescence.roll.uniformDistribution(0, 360);
2640 phytomer_parameters_olive.inflorescence.flower_prototype_scale = 0.01;
2641 // phytomer_parameters_olive.inflorescence.flower_prototype_function = OliveFlowerPrototype;
2642 phytomer_parameters_olive.inflorescence.fruit_prototype_scale = 0.025;
2643 phytomer_parameters_olive.inflorescence.fruit_prototype_function = OliveFruitPrototype;
2644
2645 // ---- Shoot Parameters ---- //
2646
2647 // Trunk
2648 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
2649 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_olive;
2650 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
2651 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.015;
2652 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 20;
2653 shoot_parameters_trunk.max_nodes = 20;
2654 shoot_parameters_trunk.girth_area_factor = 3.f;
2655 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
2656 shoot_parameters_trunk.vegetative_bud_break_time = 0;
2657 shoot_parameters_trunk.tortuosity = 1;
2658 shoot_parameters_trunk.internode_length_max = 0.05;
2659 shoot_parameters_trunk.internode_length_decay_rate = 0;
2660 shoot_parameters_trunk.defineChildShootTypes({"scaffold"}, {1});
2661
2662 // Proleptic shoots
2663 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
2664 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_olive;
2665 // shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = OlivePhytomerCreationFunction;
2666 shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = OlivePhytomerCallbackFunction;
2667 shoot_parameters_proleptic.max_nodes.uniformDistribution(16, 24);
2668 shoot_parameters_proleptic.max_nodes_per_season.uniformDistribution(8, 12);
2669 shoot_parameters_proleptic.phyllochron_min = 2.0;
2670 shoot_parameters_proleptic.elongation_rate_max = 0.25;
2671 shoot_parameters_proleptic.girth_area_factor = 5.f;
2672 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.025;
2673 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 1.0;
2674 shoot_parameters_proleptic.vegetative_bud_break_time = 30;
2675 shoot_parameters_proleptic.gravitropic_curvature.uniformDistribution(550, 650);
2676 shoot_parameters_proleptic.tortuosity = 5;
2677 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(35, 40);
2678 shoot_parameters_proleptic.insertion_angle_decay_rate = 2;
2679 shoot_parameters_proleptic.internode_length_max = 0.05;
2680 shoot_parameters_proleptic.internode_length_min = 0.03;
2681 shoot_parameters_proleptic.internode_length_decay_rate = 0.004;
2682 shoot_parameters_proleptic.fruit_set_probability = 0.25;
2683 shoot_parameters_proleptic.flower_bud_break_probability = 0.25;
2684 shoot_parameters_proleptic.max_terminal_floral_buds = 4;
2685 shoot_parameters_proleptic.flowers_require_dormancy = true;
2686 shoot_parameters_proleptic.growth_requires_dormancy = true;
2687 shoot_parameters_proleptic.determinate_shoot_growth = false;
2688 shoot_parameters_proleptic.defineChildShootTypes({"proleptic"}, {1.0});
2689
2690 // Main scaffolds
2691 ShootParameters shoot_parameters_scaffold = shoot_parameters_proleptic;
2692 shoot_parameters_scaffold.phytomer_parameters.internode.radial_subdivisions = 10;
2693 shoot_parameters_scaffold.max_nodes = 30;
2694 shoot_parameters_scaffold.max_nodes_per_season = 10;
2695 shoot_parameters_scaffold.gravitropic_curvature = 700;
2696 shoot_parameters_scaffold.internode_length_max = 0.04;
2697 shoot_parameters_scaffold.tortuosity = 3;
2698 shoot_parameters_scaffold.defineChildShootTypes({"proleptic"}, {1.0});
2699
2700 defineShootType("trunk", shoot_parameters_trunk);
2701 defineShootType("scaffold", shoot_parameters_scaffold);
2702 defineShootType("proleptic", shoot_parameters_proleptic);
2703}
2704
2705uint PlantArchitecture::buildOliveTree(const helios::vec3 &base_position) {
2706
2707 if (shoot_types.empty()) {
2708 // automatically initialize olive tree shoots
2709 initializeOliveTreeShoots();
2710 }
2711
2712 uint plantID = addPlantInstance(base_position, 0);
2713
2714 uint uID_trunk = addBaseStemShoot(plantID, 19, make_AxisRotation(context_ptr->randu(0.f, 0.025f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)),
2715 shoot_types.at("trunk").phytomer_parameters.internode.radius_initial.val(), 0.01, 1.f, 1.f, 0, "trunk");
2716 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0, 0.01, 1, 1);
2717
2718 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
2719
2720 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
2721 for (const auto &phytomer: phytomers) {
2722 phytomer->removeLeaf();
2723 phytomer->setVegetativeBudState(BUD_DEAD);
2724 phytomer->setFloralBudState(BUD_DEAD);
2725 }
2726
2727 uint Nscaffolds = 4; // context_ptr->randu(4,5);
2728
2729 for (int i = 0; i < Nscaffolds; i++) {
2730 float pitch = context_ptr->randu(deg2rad(30), deg2rad(35));
2731 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, context_ptr->randu(5, 7), make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(Nscaffolds) * 2 * M_PI, 0), 0.007,
2732 shoot_types.at("scaffold").internode_length_max.val(), 1.f, 1.f, 0.5, "scaffold", 0);
2733 }
2734
2735 makePlantDormant(plantID);
2736
2737 setPlantPhenologicalThresholds(plantID, 165, -1, 3, 7, 20, 200, 600, true);
2738 plant_instances.at(plantID).max_age = 1825;
2739
2740 return plantID;
2741}
2742
2743void PlantArchitecture::initializePistachioTreeShoots() {
2744
2745 // ---- Leaf Prototype ---- //
2746
2747 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2748 leaf_prototype.leaf_texture_file[0] = "PistachioLeaf.png";
2749 leaf_prototype.leaf_aspect_ratio = 0.6f;
2750 leaf_prototype.midrib_fold_fraction = 0.;
2751 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.4, 0.4);
2752 leaf_prototype.lateral_curvature = 0.;
2753 leaf_prototype.wave_period = 0.3f;
2754 leaf_prototype.wave_amplitude = 0.1f;
2755 leaf_prototype.subdivisions = 3;
2756 leaf_prototype.unique_prototypes = 5;
2757
2758 // ---- Phytomer Parameters ---- //
2759
2760 PhytomerParameters phytomer_parameters_pistachio(context_ptr->getRandomGenerator());
2761
2762 phytomer_parameters_pistachio.internode.pitch = 0;
2763 phytomer_parameters_pistachio.internode.phyllotactic_angle.uniformDistribution(160, 200);
2764 phytomer_parameters_pistachio.internode.radius_initial = 0.002;
2765 phytomer_parameters_pistachio.internode.length_segments = 1;
2766 phytomer_parameters_pistachio.internode.image_texture = "OliveBark.jpg";
2767 phytomer_parameters_pistachio.internode.max_floral_buds_per_petiole = 3;
2768
2769 phytomer_parameters_pistachio.petiole.petioles_per_internode = 1;
2770 phytomer_parameters_pistachio.petiole.pitch.uniformDistribution(-60, -45);
2771 phytomer_parameters_pistachio.petiole.taper = 0.1;
2772 phytomer_parameters_pistachio.petiole.curvature.uniformDistribution(-800, 800);
2773 phytomer_parameters_pistachio.petiole.length = 0.075;
2774 phytomer_parameters_pistachio.petiole.radius = 0.001;
2775 phytomer_parameters_pistachio.petiole.length_segments = 1;
2776 phytomer_parameters_pistachio.petiole.radial_subdivisions = 3;
2777 phytomer_parameters_pistachio.petiole.color = make_RGBcolor(0.6, 0.6, 0.4);
2778
2779 phytomer_parameters_pistachio.leaf.leaves_per_petiole = 3;
2780 phytomer_parameters_pistachio.leaf.prototype_scale = 0.08;
2781 phytomer_parameters_pistachio.leaf.leaflet_offset = 0.3;
2782 phytomer_parameters_pistachio.leaf.leaflet_scale = 0.75;
2783 phytomer_parameters_pistachio.leaf.pitch.uniformDistribution(-20, 20);
2784 phytomer_parameters_pistachio.leaf.roll.uniformDistribution(-20, 20);
2785 phytomer_parameters_pistachio.leaf.prototype = leaf_prototype;
2786
2787 phytomer_parameters_pistachio.peduncle.length = 0.1;
2788 phytomer_parameters_pistachio.peduncle.radius = 0.001;
2789 phytomer_parameters_pistachio.peduncle.pitch = 60;
2790 phytomer_parameters_pistachio.peduncle.roll = 0;
2791 phytomer_parameters_pistachio.peduncle.length_segments = 1;
2792 phytomer_parameters_pistachio.peduncle.curvature.uniformDistribution(500, 900);
2793 phytomer_parameters_pistachio.peduncle.color = make_RGBcolor(0.7, 0.72, 0.7);
2794
2795 phytomer_parameters_pistachio.inflorescence.flowers_per_peduncle = 16;
2796 phytomer_parameters_pistachio.inflorescence.flower_offset = 0.08;
2797 phytomer_parameters_pistachio.inflorescence.pitch.uniformDistribution(50, 70);
2798 phytomer_parameters_pistachio.inflorescence.roll.uniformDistribution(0, 360);
2799 phytomer_parameters_pistachio.inflorescence.flower_prototype_scale = 0.025;
2800 // phytomer_parameters_pistachio.inflorescence.flower_prototype_function = PistachioFlowerPrototype;
2801 phytomer_parameters_pistachio.inflorescence.fruit_prototype_scale = 0.025;
2802 phytomer_parameters_pistachio.inflorescence.fruit_prototype_function = PistachioFruitPrototype;
2803
2804 // ---- Shoot Parameters ---- //
2805
2806 // Trunk
2807 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
2808 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_pistachio;
2809 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 180;
2810 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.015;
2811 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 20;
2812 shoot_parameters_trunk.max_nodes = 20;
2813 shoot_parameters_trunk.girth_area_factor = 5.f;
2814 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
2815 shoot_parameters_trunk.vegetative_bud_break_time = 0;
2816 shoot_parameters_trunk.tortuosity = 0.75;
2817 shoot_parameters_trunk.internode_length_max = 0.05;
2818 shoot_parameters_trunk.internode_length_decay_rate = 0;
2819 shoot_parameters_trunk.defineChildShootTypes({"proleptic"}, {1});
2820
2821 // Proleptic shoots
2822 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
2823 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_pistachio;
2824 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = PistachioPhytomerCreationFunction;
2825 shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = PistachioPhytomerCallbackFunction;
2826 shoot_parameters_proleptic.phytomer_parameters.internode.pitch.uniformDistribution(-15, 15);
2827 shoot_parameters_proleptic.max_nodes.uniformDistribution(16, 24);
2828 shoot_parameters_proleptic.max_nodes_per_season.uniformDistribution(8, 10);
2829 shoot_parameters_proleptic.phyllochron_min = 2.0;
2830 shoot_parameters_proleptic.elongation_rate_max = 0.25;
2831 shoot_parameters_proleptic.girth_area_factor = 7.f;
2832 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.1;
2833 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.7;
2834 shoot_parameters_proleptic.vegetative_bud_break_time = 0;
2835 shoot_parameters_proleptic.gravitropic_curvature = 350;
2836 shoot_parameters_proleptic.tortuosity = 2;
2837 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(45, 55);
2838 shoot_parameters_proleptic.insertion_angle_decay_rate = 10;
2839 shoot_parameters_proleptic.internode_length_max = 0.06;
2840 shoot_parameters_proleptic.internode_length_min = 0.02;
2841 shoot_parameters_proleptic.internode_length_decay_rate = 0.06;
2842 shoot_parameters_proleptic.fruit_set_probability = 0.2;
2843 shoot_parameters_proleptic.flower_bud_break_probability = 0.35;
2844 shoot_parameters_proleptic.max_terminal_floral_buds = 2;
2845 shoot_parameters_proleptic.flowers_require_dormancy = true;
2846 shoot_parameters_proleptic.growth_requires_dormancy = true;
2847 shoot_parameters_proleptic.determinate_shoot_growth = false;
2848
2849 defineShootType("trunk", shoot_parameters_trunk);
2850 defineShootType("proleptic", shoot_parameters_proleptic);
2851}
2852
2853uint PlantArchitecture::buildPistachioTree(const helios::vec3 &base_position) {
2854
2855 if (shoot_types.empty()) {
2856 // automatically initialize pistachio tree shoots
2857 initializePistachioTreeShoots();
2858 }
2859
2860 // Get training system parameters (with defaults matching original hard-coded values)
2861 auto trunk_height = getParameterValue(current_build_parameters, "trunk_height", 1.0f, 0.1f, 3.f, "total trunk height in meters");
2862 auto num_scaffolds = uint(getParameterValue(current_build_parameters, "num_scaffolds", 4.f, 2.f, 8.f, "number of scaffold branches"));
2863 auto scaffold_angle = getParameterValue(current_build_parameters, "scaffold_angle", 50.f, 20.f, 70.f, "scaffold branch angle in degrees");
2864
2865 // Calculate trunk nodes based on desired height and internode length
2866 float trunk_internode_length = 0.05f; // Default internode length for pistachio
2867 uint trunk_nodes = uint(trunk_height / trunk_internode_length);
2868 if (trunk_nodes < 1)
2869 trunk_nodes = 1;
2870
2871 // Fixed training parameters (not user-customizable)
2872 float scaffold_radius = 0.007f;
2873 float scaffold_length = 0.03f;
2874 uint scaffold_nodes = 5;
2875
2876 uint plantID = addPlantInstance(base_position, 0);
2877
2878 uint uID_trunk = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)),
2879 shoot_types.at("trunk").phytomer_parameters.internode.radius_initial.val(), trunk_internode_length, 1.f, 1.f, 0, "trunk");
2880 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0, 0.01, 1, 1);
2881
2882 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
2883
2884 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
2885 for (const auto &phytomer: phytomers) {
2886 phytomer->removeLeaf();
2887 phytomer->setVegetativeBudState(BUD_DEAD);
2888 phytomer->setFloralBudState(BUD_DEAD);
2889 }
2890
2891 float pitch = deg2rad(scaffold_angle);
2892 // Four-scaffold training system in cardinal directions
2893 if (num_scaffolds >= 1) {
2894 addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - 1, scaffold_nodes, make_AxisRotation(pitch + context_ptr->randu(-0.15f, 0.15f), 0, 0.5 * M_PI + context_ptr->randu(-0.2f, 0.2f)), scaffold_radius, scaffold_length, 1.f,
2895 1.f, 0.5, "proleptic", 0);
2896 }
2897 if (num_scaffolds >= 2) {
2898 addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - 1, scaffold_nodes, make_AxisRotation(pitch + context_ptr->randu(-0.15f, 0.15f), M_PI, 0.5 * M_PI + context_ptr->randu(-0.2f, 0.2f)), scaffold_radius, scaffold_length,
2899 1.f, 1.f, 0.5, "proleptic", 0);
2900 }
2901 if (num_scaffolds >= 3) {
2902 addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - 2, scaffold_nodes, make_AxisRotation(pitch + context_ptr->randu(-0.15f, 0.15f), 0.5 * M_PI, 0.5 * M_PI + context_ptr->randu(-0.2f, 0.2f)), scaffold_radius,
2903 scaffold_length, 1.f, 1.f, 0.5, "proleptic", 0);
2904 }
2905 if (num_scaffolds >= 4) {
2906 addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - 2, scaffold_nodes, make_AxisRotation(pitch + context_ptr->randu(-0.15f, 0.15f), 1.5 * M_PI, 0.5 * M_PI + context_ptr->randu(-0.2f, 0.2f)), scaffold_radius,
2907 scaffold_length, 1.f, 1.f, 0.5, "proleptic", 0);
2908 }
2909
2910
2911 makePlantDormant(plantID);
2912
2913 setPlantPhenologicalThresholds(plantID, 165, -1, -1, 7, 20, 200, false);
2914 plant_instances.at(plantID).max_age = 1460;
2915
2916 return plantID;
2917}
2918
2919void PlantArchitecture::initializePuncturevineShoots() {
2920
2921 // ---- Leaf Prototype ---- //
2922
2923 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
2924 leaf_prototype.leaf_texture_file[0] = "PuncturevineLeaf.png";
2925 leaf_prototype.leaf_aspect_ratio = 0.4f;
2926 leaf_prototype.midrib_fold_fraction = 0.2f;
2927 leaf_prototype.longitudinal_curvature = -0.1f;
2928 leaf_prototype.lateral_curvature = 0.4f;
2929 leaf_prototype.subdivisions = 1;
2930 leaf_prototype.unique_prototypes = 1;
2931
2932 // ---- Phytomer Parameters ---- //
2933
2934 PhytomerParameters phytomer_parameters_puncturevine(context_ptr->getRandomGenerator());
2935
2936 phytomer_parameters_puncturevine.internode.pitch.uniformDistribution(0, 15);
2937 phytomer_parameters_puncturevine.internode.phyllotactic_angle = 180.f;
2938 phytomer_parameters_puncturevine.internode.radius_initial = 0.001;
2939 phytomer_parameters_puncturevine.internode.color = make_RGBcolor(0.28, 0.18, 0.13);
2940 phytomer_parameters_puncturevine.internode.length_segments = 1;
2941
2942 phytomer_parameters_puncturevine.petiole.petioles_per_internode = 1;
2943 phytomer_parameters_puncturevine.petiole.pitch.uniformDistribution(60, 80);
2944 phytomer_parameters_puncturevine.petiole.radius = 0.0005;
2945 phytomer_parameters_puncturevine.petiole.length = 0.03;
2946 phytomer_parameters_puncturevine.petiole.taper = 0;
2947 phytomer_parameters_puncturevine.petiole.curvature = 0;
2948 phytomer_parameters_puncturevine.petiole.color = phytomer_parameters_puncturevine.internode.color;
2949 phytomer_parameters_puncturevine.petiole.length_segments = 1;
2950
2951 phytomer_parameters_puncturevine.leaf.leaves_per_petiole = 11;
2952 phytomer_parameters_puncturevine.leaf.pitch.uniformDistribution(0, 40);
2953 phytomer_parameters_puncturevine.leaf.yaw = 30;
2954 phytomer_parameters_puncturevine.leaf.roll.uniformDistribution(-5, 5);
2955 phytomer_parameters_puncturevine.leaf.prototype_scale = 0.012;
2956 phytomer_parameters_puncturevine.leaf.leaflet_offset = 0.18;
2957 phytomer_parameters_puncturevine.leaf.leaflet_scale = 1;
2958 phytomer_parameters_puncturevine.leaf.prototype = leaf_prototype;
2959
2960 phytomer_parameters_puncturevine.peduncle.length = 0.001;
2961 phytomer_parameters_puncturevine.peduncle.color = phytomer_parameters_puncturevine.internode.color;
2962
2963 phytomer_parameters_puncturevine.inflorescence.flowers_per_peduncle = 1;
2964 phytomer_parameters_puncturevine.inflorescence.pitch = -90.f;
2965 phytomer_parameters_puncturevine.inflorescence.flower_prototype_function = PuncturevineFlowerPrototype;
2966 phytomer_parameters_puncturevine.inflorescence.flower_prototype_scale = 0.01;
2967
2968 // ---- Shoot Parameters ---- //
2969
2970 ShootParameters shoot_parameters_primary(context_ptr->getRandomGenerator());
2971 shoot_parameters_primary.phytomer_parameters = phytomer_parameters_puncturevine;
2972 shoot_parameters_primary.vegetative_bud_break_probability_min = 0.1;
2973 shoot_parameters_primary.vegetative_bud_break_probability_decay_rate = 1.f;
2974 shoot_parameters_primary.vegetative_bud_break_time = 10;
2975 shoot_parameters_primary.base_roll = 90;
2976 shoot_parameters_primary.phyllochron_min = 1;
2977 shoot_parameters_primary.elongation_rate_max = 0.2;
2978 shoot_parameters_primary.girth_area_factor = 0.f;
2979 shoot_parameters_primary.internode_length_max = 0.02;
2980 shoot_parameters_primary.internode_length_decay_rate = 0;
2981 shoot_parameters_primary.insertion_angle_tip.uniformDistribution(75, 85);
2982 shoot_parameters_primary.insertion_angle_decay_rate = 0;
2983 shoot_parameters_primary.flowers_require_dormancy = false;
2984 shoot_parameters_primary.growth_requires_dormancy = false;
2985 shoot_parameters_primary.flower_bud_break_probability = 0.2;
2986 shoot_parameters_primary.determinate_shoot_growth = false;
2987 shoot_parameters_primary.max_nodes = 15;
2988 shoot_parameters_primary.gravitropic_curvature = 25;
2989 shoot_parameters_primary.tortuosity = 0;
2990 shoot_parameters_primary.defineChildShootTypes({"secondary_puncturevine"}, {1.f});
2991
2992 ShootParameters shoot_parameters_base = shoot_parameters_primary;
2993 shoot_parameters_base.phytomer_parameters = phytomer_parameters_puncturevine;
2994 shoot_parameters_base.phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(137.5 - 10, 137.5 + 10);
2995 shoot_parameters_base.phytomer_parameters.petiole.petioles_per_internode = 0;
2996 shoot_parameters_base.phytomer_parameters.internode.pitch = 0;
2997 shoot_parameters_base.phytomer_parameters.petiole.pitch = 0;
2998 shoot_parameters_base.vegetative_bud_break_probability_min = 1;
2999 shoot_parameters_base.vegetative_bud_break_time = 2;
3000 shoot_parameters_base.phyllochron_min = 2;
3001 shoot_parameters_base.elongation_rate_max = 0.15;
3002 shoot_parameters_base.gravitropic_curvature = 0;
3003 shoot_parameters_base.internode_length_max = 0.01;
3004 shoot_parameters_base.internode_length_decay_rate = 0;
3005 shoot_parameters_base.insertion_angle_tip = 90;
3006 shoot_parameters_base.insertion_angle_decay_rate = 0;
3007 shoot_parameters_base.flowers_require_dormancy = false;
3008 shoot_parameters_base.growth_requires_dormancy = false;
3009 shoot_parameters_base.flower_bud_break_probability = 0.0;
3010 shoot_parameters_base.max_nodes.uniformDistribution(3, 5);
3011 shoot_parameters_base.defineChildShootTypes({"primary_puncturevine"}, {1.f});
3012
3013 ShootParameters shoot_parameters_children = shoot_parameters_primary;
3014 shoot_parameters_children.base_roll = 0;
3015
3016 defineShootType("base_puncturevine", shoot_parameters_base);
3017 defineShootType("primary_puncturevine", shoot_parameters_primary);
3018 defineShootType("secondary_puncturevine", shoot_parameters_children);
3019}
3020
3021uint PlantArchitecture::buildPuncturevinePlant(const helios::vec3 &base_position) {
3022
3023 if (shoot_types.empty()) {
3024 // automatically initialize puncturevine plant shoots
3025 initializePuncturevineShoots();
3026 }
3027
3028 uint plantID = addPlantInstance(base_position, 0);
3029
3030 uint uID_stem = addBaseStemShoot(plantID, 3, make_AxisRotation(0, 0.f, 0.f), 0.001, 0.001, 1, 1, 0, "base_puncturevine");
3031
3032 breakPlantDormancy(plantID);
3033
3034 plant_instances.at(plantID).max_age = 45;
3035
3036 setPlantPhenologicalThresholds(plantID, 0, -1, 14, -1, -1, 1000, false);
3037
3038 return plantID;
3039}
3040
3041void PlantArchitecture::initializeEasternRedbudShoots() {
3042
3043 // ---- Leaf Prototype ---- //
3044
3045 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3046 leaf_prototype.leaf_texture_file[0] = "RedbudLeaf.png";
3047 leaf_prototype.leaf_aspect_ratio = 1.f;
3048 leaf_prototype.midrib_fold_fraction = 0.2f;
3049 leaf_prototype.longitudinal_curvature = -0.15f;
3050 leaf_prototype.lateral_curvature = -0.1f;
3051 leaf_prototype.wave_period = 0.3f;
3052 leaf_prototype.wave_amplitude = 0.025f;
3053 leaf_prototype.subdivisions = 5;
3054 leaf_prototype.unique_prototypes = 5;
3055 leaf_prototype.leaf_offset = make_vec3(-0.3, 0, 0);
3056
3057 // ---- Phytomer Parameters ---- //
3058
3059 PhytomerParameters phytomer_parameters_redbud(context_ptr->getRandomGenerator());
3060
3061 phytomer_parameters_redbud.internode.pitch = 15;
3062 phytomer_parameters_redbud.internode.phyllotactic_angle.uniformDistribution(170, 190);
3063 phytomer_parameters_redbud.internode.radius_initial = 0.0015;
3064 phytomer_parameters_redbud.internode.image_texture = "WesternRedbudBark.jpg";
3065 phytomer_parameters_redbud.internode.color.scale(0.3);
3066 phytomer_parameters_redbud.internode.length_segments = 1;
3067 phytomer_parameters_redbud.internode.max_floral_buds_per_petiole = 5;
3068
3069 phytomer_parameters_redbud.petiole.petioles_per_internode = 1;
3070 phytomer_parameters_redbud.petiole.color = make_RGBcolor(0.65, 0.52, 0.39);
3071 phytomer_parameters_redbud.petiole.pitch.uniformDistribution(20, 40);
3072 phytomer_parameters_redbud.petiole.radius = 0.002;
3073 phytomer_parameters_redbud.petiole.length = 0.075;
3074 phytomer_parameters_redbud.petiole.taper = 0;
3075 phytomer_parameters_redbud.petiole.curvature = 0;
3076 phytomer_parameters_redbud.petiole.length_segments = 1;
3077
3078 phytomer_parameters_redbud.leaf.leaves_per_petiole = 1;
3079 phytomer_parameters_redbud.leaf.pitch.uniformDistribution(-110, -80);
3080 phytomer_parameters_redbud.leaf.yaw = 0;
3081 phytomer_parameters_redbud.leaf.roll.uniformDistribution(-5, 5);
3082 phytomer_parameters_redbud.leaf.prototype_scale = 0.1;
3083 phytomer_parameters_redbud.leaf.prototype = leaf_prototype;
3084
3085 phytomer_parameters_redbud.peduncle.length = 0.02;
3086 phytomer_parameters_redbud.peduncle.pitch.uniformDistribution(50, 90);
3087 phytomer_parameters_redbud.peduncle.color = make_RGBcolor(0.32, 0.05, 0.13);
3088
3089 phytomer_parameters_redbud.inflorescence.flowers_per_peduncle = 1;
3090 phytomer_parameters_redbud.inflorescence.pitch = 0;
3091 phytomer_parameters_redbud.inflorescence.flower_prototype_function = RedbudFlowerPrototype;
3092 phytomer_parameters_redbud.inflorescence.flower_prototype_scale = 0.04;
3093 phytomer_parameters_redbud.inflorescence.fruit_prototype_function = RedbudFruitPrototype;
3094 phytomer_parameters_redbud.inflorescence.fruit_prototype_scale = 0.1;
3095 phytomer_parameters_redbud.inflorescence.fruit_gravity_factor_fraction = 0.7;
3096
3097 phytomer_parameters_redbud.phytomer_creation_function = RedbudPhytomerCreationFunction;
3098 phytomer_parameters_redbud.phytomer_callback_function = RedbudPhytomerCallbackFunction;
3099
3100 // ---- Shoot Parameters ---- //
3101
3102 ShootParameters shoot_parameters_main(context_ptr->getRandomGenerator());
3103 shoot_parameters_main.phytomer_parameters = phytomer_parameters_redbud;
3104 shoot_parameters_main.vegetative_bud_break_probability_min = 1.0;
3105 shoot_parameters_main.vegetative_bud_break_time = 2;
3106 shoot_parameters_main.phyllochron_min = 2;
3107 shoot_parameters_main.elongation_rate_max = 0.1;
3108 shoot_parameters_main.girth_area_factor = 4.f;
3109 shoot_parameters_main.gravitropic_curvature = 300;
3110 shoot_parameters_main.tortuosity = 5;
3111 shoot_parameters_main.internode_length_max = 0.04;
3112 shoot_parameters_main.internode_length_decay_rate = 0.005;
3113 shoot_parameters_main.internode_length_min = 0.01;
3114 shoot_parameters_main.insertion_angle_tip = 75;
3115 shoot_parameters_main.insertion_angle_decay_rate = 10;
3116 shoot_parameters_main.flowers_require_dormancy = true;
3117 shoot_parameters_main.growth_requires_dormancy = true;
3118 shoot_parameters_main.determinate_shoot_growth = false;
3119 shoot_parameters_main.max_terminal_floral_buds = 0;
3120 shoot_parameters_main.flower_bud_break_probability = 0.8;
3121 shoot_parameters_main.fruit_set_probability = 0.3;
3122 shoot_parameters_main.max_nodes = 25;
3123 shoot_parameters_main.max_nodes_per_season = 10;
3124 shoot_parameters_main.base_roll = 90;
3125
3126 ShootParameters shoot_parameters_trunk = shoot_parameters_main;
3127 shoot_parameters_trunk.phytomer_parameters.internode.pitch = 0;
3128 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 15;
3129 shoot_parameters_trunk.phytomer_parameters.internode.max_floral_buds_per_petiole = 0;
3130 shoot_parameters_trunk.insertion_angle_tip = 60;
3131 shoot_parameters_trunk.max_nodes = 75;
3132 shoot_parameters_trunk.max_nodes_per_season = 10;
3133 shoot_parameters_trunk.tortuosity = 1.5;
3134 shoot_parameters_trunk.defineChildShootTypes({"eastern_redbud_shoot"}, {1.f});
3135
3136 defineShootType("eastern_redbud_trunk", shoot_parameters_trunk);
3137 defineShootType("eastern_redbud_shoot", shoot_parameters_main);
3138}
3139
3140uint PlantArchitecture::buildEasternRedbudPlant(const helios::vec3 &base_position) {
3141
3142 if (shoot_types.empty()) {
3143 // automatically initialize redbud plant shoots
3144 initializeEasternRedbudShoots();
3145 }
3146
3147 uint plantID = addPlantInstance(base_position, 0);
3148
3149 uint uID_stem = addBaseStemShoot(plantID, 16, make_AxisRotation(context_ptr->randu(0, 0.1 * M_PI), context_ptr->randu(0, 2 * M_PI), context_ptr->randu(0, 2 * M_PI)), 0.0075, 0.05, 1, 1, 0.4, "eastern_redbud_trunk");
3150
3151 makePlantDormant(plantID);
3152 breakPlantDormancy(plantID);
3153
3154 // leave four vegetative buds on the trunk and remove the rest
3155 for (auto &phytomer: this->plant_instances.at(plantID).shoot_tree.at(uID_stem)->phytomers) {
3156 if (phytomer->shoot_index.x < 12) {
3157 for (auto &petiole: phytomer->axillary_vegetative_buds) {
3158 for (auto &vbud: petiole) {
3159 phytomer->setVegetativeBudState(BUD_DEAD, vbud);
3160 }
3161 }
3162 }
3163 }
3164
3165 setPlantPhenologicalThresholds(plantID, 165, -1, 3, 7, 30, 200, false);
3166
3167 plant_instances.at(plantID).max_age = 1460;
3168
3169 return plantID;
3170}
3171
3172void PlantArchitecture::initializeRiceShoots() {
3173
3174 // ---- Leaf Prototype ---- //
3175
3176 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3177 leaf_prototype.leaf_texture_file[0] = "SorghumLeaf.png";
3178 leaf_prototype.leaf_aspect_ratio = 0.06f;
3179 leaf_prototype.midrib_fold_fraction = 0.3f;
3180 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.2, 0);
3181 leaf_prototype.lateral_curvature = -0.3;
3182 leaf_prototype.wave_period = 0.1f;
3183 leaf_prototype.wave_amplitude = 0.1f;
3184 leaf_prototype.subdivisions = 20;
3185 leaf_prototype.unique_prototypes = 10;
3186
3187 // ---- Phytomer Parameters ---- //
3188
3189 PhytomerParameters phytomer_parameters_rice(context_ptr->getRandomGenerator());
3190
3191 phytomer_parameters_rice.internode.pitch = 0;
3192 phytomer_parameters_rice.internode.phyllotactic_angle.uniformDistribution(67, 77);
3193 phytomer_parameters_rice.internode.radius_initial = 0.001;
3194 phytomer_parameters_rice.internode.color = make_RGBcolor(0.27, 0.31, 0.16);
3195 phytomer_parameters_rice.internode.length_segments = 1;
3196 phytomer_parameters_rice.internode.radial_subdivisions = 6;
3197 phytomer_parameters_rice.internode.max_floral_buds_per_petiole = 0;
3198 phytomer_parameters_rice.internode.max_vegetative_buds_per_petiole = 0;
3199
3200 phytomer_parameters_rice.petiole.petioles_per_internode = 1;
3201 phytomer_parameters_rice.petiole.pitch.uniformDistribution(-40, 0);
3202 phytomer_parameters_rice.petiole.radius = 0.0;
3203 phytomer_parameters_rice.petiole.length = 0.01;
3204 phytomer_parameters_rice.petiole.taper = 0;
3205 phytomer_parameters_rice.petiole.curvature = 0;
3206 phytomer_parameters_rice.petiole.length_segments = 1;
3207
3208 phytomer_parameters_rice.leaf.leaves_per_petiole = 1;
3209 phytomer_parameters_rice.leaf.pitch = 0;
3210 phytomer_parameters_rice.leaf.yaw = 0;
3211 phytomer_parameters_rice.leaf.roll = 0;
3212 phytomer_parameters_rice.leaf.prototype_scale = 0.15;
3213 phytomer_parameters_rice.leaf.prototype = leaf_prototype;
3214
3215 phytomer_parameters_rice.peduncle.pitch = 0;
3216 phytomer_parameters_rice.peduncle.length.uniformDistribution(0.14, 0.18);
3217 phytomer_parameters_rice.peduncle.radius = 0.0005;
3218 phytomer_parameters_rice.peduncle.color = phytomer_parameters_rice.internode.color;
3219 phytomer_parameters_rice.peduncle.curvature.uniformDistribution(-800, -50);
3220 phytomer_parameters_rice.peduncle.radial_subdivisions = 6;
3221 phytomer_parameters_rice.peduncle.length_segments = 8;
3222
3223 phytomer_parameters_rice.inflorescence.flowers_per_peduncle = 60;
3224 phytomer_parameters_rice.inflorescence.pitch.uniformDistribution(20, 25);
3225 phytomer_parameters_rice.inflorescence.roll = 0;
3226 phytomer_parameters_rice.inflorescence.fruit_prototype_scale = 0.008;
3227 phytomer_parameters_rice.inflorescence.flower_offset = 0.012;
3228 phytomer_parameters_rice.inflorescence.fruit_prototype_function = RiceSpikePrototype;
3229
3230 // phytomer_parameters_rice.phytomer_creation_function = RicePhytomerCreationFunction;
3231
3232 // ---- Shoot Parameters ---- //
3233
3234 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
3235 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters_rice;
3236 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0;
3237 shoot_parameters_mainstem.flower_bud_break_probability = 1;
3238 shoot_parameters_mainstem.phyllochron_min = 2;
3239 shoot_parameters_mainstem.elongation_rate_max = 0.1;
3240 shoot_parameters_mainstem.girth_area_factor = 5.f;
3241 shoot_parameters_mainstem.gravitropic_curvature.uniformDistribution(-1000, -400);
3242 shoot_parameters_mainstem.internode_length_max = 0.0075;
3243 shoot_parameters_mainstem.internode_length_decay_rate = 0;
3244 shoot_parameters_mainstem.flowers_require_dormancy = false;
3245 shoot_parameters_mainstem.growth_requires_dormancy = false;
3246 shoot_parameters_mainstem.determinate_shoot_growth = false;
3247 shoot_parameters_mainstem.fruit_set_probability = 1.0;
3248 shoot_parameters_mainstem.defineChildShootTypes({"mainstem"}, {1.0});
3249 shoot_parameters_mainstem.max_nodes = 30;
3250 shoot_parameters_mainstem.max_terminal_floral_buds = 5;
3251
3252 defineShootType("mainstem", shoot_parameters_mainstem);
3253}
3254
3255uint PlantArchitecture::buildRicePlant(const helios::vec3 &base_position) {
3256
3257 if (shoot_types.empty()) {
3258 // automatically initialize rice plant shoots
3259 initializeRiceShoots();
3260 }
3261
3262 uint plantID = addPlantInstance(base_position, 0);
3263
3264 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(context_ptr->randu(0.f, 0.1f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)), 0.001, 0.0075, 0.01, 0.01, 0, "mainstem");
3265
3266 breakPlantDormancy(plantID);
3267
3268 setPlantPhenologicalThresholds(plantID, 0, -1, -1, 4, 10, 1000, false);
3269
3270 plant_instances.at(plantID).max_age = 365;
3271
3272 return plantID;
3273}
3274
3275void PlantArchitecture::initializeButterLettuceShoots() {
3276
3277 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3278 leaf_prototype.leaf_texture_file[0] = "RomaineLettuceLeaf.png";
3279 leaf_prototype.leaf_aspect_ratio = 0.85f;
3280 leaf_prototype.midrib_fold_fraction = 0.2f;
3281 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.2, 0.05);
3282 leaf_prototype.lateral_curvature = -0.4f;
3283 leaf_prototype.wave_period.uniformDistribution(0.15, 0.25);
3284 leaf_prototype.wave_amplitude.uniformDistribution(0.05, 0.1);
3285 leaf_prototype.subdivisions = 30;
3286 leaf_prototype.unique_prototypes = 10;
3287
3288 // ---- Phytomer Parameters ---- //
3289
3290 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
3291
3292 phytomer_parameters.internode.pitch = 0;
3293 phytomer_parameters.internode.phyllotactic_angle = 137.5;
3294 phytomer_parameters.internode.radius_initial = 0.02;
3295 phytomer_parameters.internode.color = make_RGBcolor(0.402, 0.423, 0.413);
3296 phytomer_parameters.internode.length_segments = 1;
3297 phytomer_parameters.internode.radial_subdivisions = 10;
3298
3299 phytomer_parameters.petiole.petioles_per_internode = 1;
3300 phytomer_parameters.petiole.pitch.uniformDistribution(0, 30);
3301 phytomer_parameters.petiole.radius = 0.001;
3302 phytomer_parameters.petiole.length = 0.001;
3303 phytomer_parameters.petiole.length_segments = 1;
3304 phytomer_parameters.petiole.radial_subdivisions = 3;
3305 phytomer_parameters.petiole.color = RGB::red;
3306
3307 phytomer_parameters.leaf.leaves_per_petiole = 1;
3308 phytomer_parameters.leaf.pitch = 10;
3309 phytomer_parameters.leaf.yaw = 0;
3310 phytomer_parameters.leaf.roll = 0;
3311 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.15, 0.25);
3312 phytomer_parameters.leaf.prototype = leaf_prototype;
3313
3314 phytomer_parameters.phytomer_creation_function = ButterLettucePhytomerCreationFunction;
3315
3316 // ---- Shoot Parameters ---- //
3317
3318 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
3319 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters;
3320 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0;
3321 shoot_parameters_mainstem.phyllochron_min = 2;
3322 shoot_parameters_mainstem.elongation_rate_max = 0.15;
3323 shoot_parameters_mainstem.girth_area_factor = 0.f;
3324 shoot_parameters_mainstem.gravitropic_curvature = 10;
3325 shoot_parameters_mainstem.internode_length_max = 0.001;
3326 shoot_parameters_mainstem.internode_length_decay_rate = 0;
3327 shoot_parameters_mainstem.flowers_require_dormancy = false;
3328 shoot_parameters_mainstem.growth_requires_dormancy = false;
3329 shoot_parameters_mainstem.flower_bud_break_probability = 0.0;
3330 shoot_parameters_mainstem.max_nodes = 25;
3331
3332 defineShootType("mainstem", shoot_parameters_mainstem);
3333}
3334
3335uint PlantArchitecture::buildButterLettucePlant(const helios::vec3 &base_position) {
3336
3337 if (shoot_types.empty()) {
3338 // automatically initialize lettuce plant shoots
3339 initializeButterLettuceShoots();
3340 }
3341
3342 uint plantID = addPlantInstance(base_position, 0);
3343
3344 uint uID_stem = addBaseStemShoot(plantID, 3, make_AxisRotation(context_ptr->randu(0.f, 0.03f * M_PI), 0.f, context_ptr->randu(0.f, 2.f * M_PI)), 0.005, 0.001, 1, 1, 0, "mainstem");
3345
3346 breakPlantDormancy(plantID);
3347
3348 setPlantPhenologicalThresholds(plantID, 0, -1, -1, -1, -1, 1000, false);
3349
3350 plant_instances.at(plantID).max_age = 365;
3351
3352 return plantID;
3353}
3354
3355void PlantArchitecture::initializeSorghumShoots() {
3356
3357 // ---- Leaf Prototype ---- //
3358
3359 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3360 leaf_prototype.leaf_texture_file[0] = "SorghumLeaf.png";
3361 leaf_prototype.leaf_aspect_ratio = 0.2f;
3362 leaf_prototype.midrib_fold_fraction = 0.3f;
3363 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.4, -0.2);
3364 leaf_prototype.lateral_curvature = -0.3f;
3365 leaf_prototype.petiole_roll = 0.04f;
3366 leaf_prototype.wave_period = 0.1f;
3367 leaf_prototype.wave_amplitude = 0.1f;
3368 leaf_prototype.leaf_buckle_length.uniformDistribution(0.4, 0.6);
3369 leaf_prototype.leaf_buckle_angle.uniformDistribution(45, 55);
3370 leaf_prototype.subdivisions = 50;
3371 leaf_prototype.unique_prototypes = 10;
3372
3373 // ---- Phytomer Parameters ---- //
3374
3375 PhytomerParameters phytomer_parameters_sorghum(context_ptr->getRandomGenerator());
3376
3377 phytomer_parameters_sorghum.internode.pitch = 0;
3378 phytomer_parameters_sorghum.internode.phyllotactic_angle.uniformDistribution(170, 190);
3379 phytomer_parameters_sorghum.internode.radius_initial = 0.003;
3380 phytomer_parameters_sorghum.internode.color = make_RGBcolor(0.09, 0.13, 0.06);
3381 phytomer_parameters_sorghum.internode.length_segments = 2;
3382 phytomer_parameters_sorghum.internode.radial_subdivisions = 10;
3383 phytomer_parameters_sorghum.internode.max_floral_buds_per_petiole = 0;
3384 phytomer_parameters_sorghum.internode.max_vegetative_buds_per_petiole = 0;
3385
3386 phytomer_parameters_sorghum.petiole.petioles_per_internode = 1;
3387 phytomer_parameters_sorghum.petiole.pitch.uniformDistribution(-40, -20);
3388 phytomer_parameters_sorghum.petiole.radius = 0.0;
3389 phytomer_parameters_sorghum.petiole.length = 0.05;
3390 phytomer_parameters_sorghum.petiole.taper = 0;
3391 phytomer_parameters_sorghum.petiole.curvature = 0;
3392 phytomer_parameters_sorghum.petiole.length_segments = 1;
3393
3394 phytomer_parameters_sorghum.leaf.leaves_per_petiole = 1;
3395 phytomer_parameters_sorghum.leaf.pitch = 0;
3396 phytomer_parameters_sorghum.leaf.yaw = 0;
3397 phytomer_parameters_sorghum.leaf.roll = 0;
3398 phytomer_parameters_sorghum.leaf.prototype_scale = 0.6;
3399 phytomer_parameters_sorghum.leaf.prototype = leaf_prototype;
3400
3401 phytomer_parameters_sorghum.peduncle.length = 0.3;
3402 phytomer_parameters_sorghum.peduncle.radius = 0.008;
3403 phytomer_parameters_sorghum.peduncle.color = phytomer_parameters_sorghum.internode.color;
3404 phytomer_parameters_sorghum.peduncle.radial_subdivisions = 10;
3405
3406 phytomer_parameters_sorghum.inflorescence.flowers_per_peduncle = 1;
3407 phytomer_parameters_sorghum.inflorescence.pitch = 0;
3408 phytomer_parameters_sorghum.inflorescence.roll = 0;
3409 phytomer_parameters_sorghum.inflorescence.fruit_prototype_scale = 0.18;
3410 phytomer_parameters_sorghum.inflorescence.fruit_prototype_function = SorghumPaniclePrototype;
3411
3412 phytomer_parameters_sorghum.phytomer_creation_function = SorghumPhytomerCreationFunction;
3413
3414 // ---- Shoot Parameters ---- //
3415
3416 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
3417 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters_sorghum;
3418 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0;
3419 shoot_parameters_mainstem.flower_bud_break_probability = 1;
3420 shoot_parameters_mainstem.phyllochron_min = 2;
3421 shoot_parameters_mainstem.elongation_rate_max = 0.1;
3422 shoot_parameters_mainstem.girth_area_factor = 5.f;
3423 shoot_parameters_mainstem.gravitropic_curvature.uniformDistribution(-1000, -400);
3424 shoot_parameters_mainstem.internode_length_max = 0.26;
3425 shoot_parameters_mainstem.internode_length_decay_rate = 0;
3426 shoot_parameters_mainstem.flowers_require_dormancy = false;
3427 shoot_parameters_mainstem.growth_requires_dormancy = false;
3428 shoot_parameters_mainstem.determinate_shoot_growth = false;
3429 shoot_parameters_mainstem.flower_bud_break_probability = 1.0;
3430 shoot_parameters_mainstem.fruit_set_probability = 1.0;
3431 shoot_parameters_mainstem.defineChildShootTypes({"mainstem"}, {1.0});
3432 shoot_parameters_mainstem.max_nodes = 16;
3433 shoot_parameters_mainstem.max_terminal_floral_buds = 1;
3434
3435 defineShootType("mainstem", shoot_parameters_mainstem);
3436}
3437
3438uint PlantArchitecture::buildSorghumPlant(const helios::vec3 &base_position) {
3439
3440 if (shoot_types.empty()) {
3441 // automatically initialize sorghum plant shoots
3442 initializeSorghumShoots();
3443 }
3444
3445 uint plantID = addPlantInstance(base_position - make_vec3(0, 0, 0.025), 0);
3446
3447 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(context_ptr->randu(0.f, 0.075f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)), 0.003, 0.06, 0.01, 0.01, 0, "mainstem");
3448
3449 breakPlantDormancy(plantID);
3450
3451 setPlantPhenologicalThresholds(plantID, 0, -1, -1, 4, 15, 1000, false);
3452
3453 plant_instances.at(plantID).max_age = 365;
3454
3455 return plantID;
3456}
3457
3458void PlantArchitecture::initializeSoybeanShoots() {
3459
3460 // ---- Leaf Prototype ---- //
3461
3462 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3463 leaf_prototype.leaf_texture_file[0] = "SoybeanLeaf.png";
3464 leaf_prototype.leaf_aspect_ratio = 1.f;
3465 leaf_prototype.midrib_fold_fraction = 0.1f;
3466 leaf_prototype.longitudinal_curvature.uniformDistribution(0.1, 0.2);
3467 leaf_prototype.lateral_curvature = 0.45;
3468 leaf_prototype.subdivisions = 8;
3469 leaf_prototype.unique_prototypes = 5;
3470 leaf_prototype.build_petiolule = true;
3471
3472 PhytomerParameters phytomer_parameters_trifoliate(context_ptr->getRandomGenerator());
3473
3474 phytomer_parameters_trifoliate.internode.pitch = 20;
3475 phytomer_parameters_trifoliate.internode.phyllotactic_angle.uniformDistribution(145, 215);
3476 phytomer_parameters_trifoliate.internode.radius_initial = 0.002;
3477 phytomer_parameters_trifoliate.internode.max_floral_buds_per_petiole = 1;
3478 phytomer_parameters_trifoliate.internode.max_vegetative_buds_per_petiole = 1;
3479 phytomer_parameters_trifoliate.internode.color = make_RGBcolor(0.2, 0.25, 0.05);
3480 phytomer_parameters_trifoliate.internode.length_segments = 5;
3481
3482 phytomer_parameters_trifoliate.petiole.petioles_per_internode = 1;
3483 phytomer_parameters_trifoliate.petiole.pitch.uniformDistribution(15, 40);
3484 phytomer_parameters_trifoliate.petiole.radius = 0.002;
3485 phytomer_parameters_trifoliate.petiole.length.uniformDistribution(0.12, 0.16);
3486 phytomer_parameters_trifoliate.petiole.taper = 0.25;
3487 phytomer_parameters_trifoliate.petiole.curvature.uniformDistribution(-250, 50);
3488 phytomer_parameters_trifoliate.petiole.color = phytomer_parameters_trifoliate.internode.color;
3489 phytomer_parameters_trifoliate.petiole.length_segments = 5;
3490 phytomer_parameters_trifoliate.petiole.radial_subdivisions = 6;
3491
3492 phytomer_parameters_trifoliate.leaf.leaves_per_petiole = 3;
3493 phytomer_parameters_trifoliate.leaf.pitch.uniformDistribution(-30, 10);
3494 phytomer_parameters_trifoliate.leaf.yaw = 10;
3495 phytomer_parameters_trifoliate.leaf.roll.uniformDistribution(-25, 5);
3496 phytomer_parameters_trifoliate.leaf.leaflet_offset = 0.5;
3497 phytomer_parameters_trifoliate.leaf.leaflet_scale = 0.9;
3498 phytomer_parameters_trifoliate.leaf.prototype_scale.uniformDistribution(0.1, 0.14);
3499 phytomer_parameters_trifoliate.leaf.prototype = leaf_prototype;
3500
3501 phytomer_parameters_trifoliate.peduncle.length = 0.01;
3502 phytomer_parameters_trifoliate.peduncle.radius = 0.0005;
3503 phytomer_parameters_trifoliate.peduncle.pitch.uniformDistribution(0, 40);
3504 phytomer_parameters_trifoliate.peduncle.roll = 90;
3505 phytomer_parameters_trifoliate.peduncle.curvature.uniformDistribution(-500, 500);
3506 phytomer_parameters_trifoliate.peduncle.color = phytomer_parameters_trifoliate.internode.color;
3507 phytomer_parameters_trifoliate.peduncle.length_segments = 1;
3508 phytomer_parameters_trifoliate.peduncle.radial_subdivisions = 6;
3509
3510 phytomer_parameters_trifoliate.inflorescence.flowers_per_peduncle.uniformDistribution(1, 4);
3511 phytomer_parameters_trifoliate.inflorescence.flower_offset = 0.2;
3512 phytomer_parameters_trifoliate.inflorescence.pitch.uniformDistribution(50, 70);
3513 phytomer_parameters_trifoliate.inflorescence.roll.uniformDistribution(-20, 20);
3514 phytomer_parameters_trifoliate.inflorescence.flower_prototype_scale = 0.015;
3515 phytomer_parameters_trifoliate.inflorescence.flower_prototype_function = SoybeanFlowerPrototype;
3516 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_scale.uniformDistribution(0.1, 0.12);
3517 phytomer_parameters_trifoliate.inflorescence.fruit_prototype_function = SoybeanFruitPrototype;
3518 phytomer_parameters_trifoliate.inflorescence.fruit_gravity_factor_fraction.uniformDistribution(0.8, 1.0);
3519
3520 PhytomerParameters phytomer_parameters_unifoliate = phytomer_parameters_trifoliate;
3521 phytomer_parameters_unifoliate.internode.pitch = 0;
3522 phytomer_parameters_unifoliate.internode.max_vegetative_buds_per_petiole = 0;
3523 phytomer_parameters_unifoliate.internode.max_floral_buds_per_petiole = 0;
3524 phytomer_parameters_unifoliate.petiole.petioles_per_internode = 2;
3525 phytomer_parameters_unifoliate.petiole.length = 0.01;
3526 phytomer_parameters_unifoliate.petiole.radius = 0.001;
3527 phytomer_parameters_unifoliate.petiole.pitch.uniformDistribution(60, 80);
3528 phytomer_parameters_unifoliate.leaf.leaves_per_petiole = 1;
3529 phytomer_parameters_unifoliate.leaf.prototype_scale = 0.02;
3530 phytomer_parameters_unifoliate.leaf.pitch.uniformDistribution(-10, 10);
3531 phytomer_parameters_unifoliate.leaf.prototype = leaf_prototype;
3532
3533 // ---- Shoot Parameters ---- //
3534
3535 ShootParameters shoot_parameters_trifoliate(context_ptr->getRandomGenerator());
3536 shoot_parameters_trifoliate.phytomer_parameters = phytomer_parameters_trifoliate;
3537 shoot_parameters_trifoliate.phytomer_parameters.phytomer_creation_function = BeanPhytomerCreationFunction;
3538
3539 shoot_parameters_trifoliate.max_nodes = 25;
3540 shoot_parameters_trifoliate.insertion_angle_tip.uniformDistribution(20, 30);
3541 // shoot_parameters_trifoliate.child_insertion_angle_decay_rate = 0; (default)
3542 shoot_parameters_trifoliate.internode_length_max = 0.035;
3543 // shoot_parameters_trifoliate.child_internode_length_min = 0.0; (default)
3544 // shoot_parameters_trifoliate.child_internode_length_decay_rate = 0; (default)
3545 shoot_parameters_trifoliate.base_roll = 90;
3546 shoot_parameters_trifoliate.base_yaw.uniformDistribution(-20, 20);
3547 shoot_parameters_trifoliate.gravitropic_curvature = 400;
3548
3549 shoot_parameters_trifoliate.phyllochron_min = 2;
3550 shoot_parameters_trifoliate.elongation_rate_max = 0.1;
3551 shoot_parameters_trifoliate.girth_area_factor = 2.f;
3552 shoot_parameters_trifoliate.vegetative_bud_break_time = 15;
3553 shoot_parameters_trifoliate.vegetative_bud_break_probability_min = 0.05;
3554 shoot_parameters_trifoliate.vegetative_bud_break_probability_decay_rate = 0.6;
3555 // shoot_parameters_trifoliate.max_terminal_floral_buds = 0; (default)
3556 shoot_parameters_trifoliate.flower_bud_break_probability.uniformDistribution(0.8, 1.0);
3557 shoot_parameters_trifoliate.fruit_set_probability = 0.4;
3558 // shoot_parameters_trifoliate.flowers_require_dormancy = false; (default)
3559 // shoot_parameters_trifoliate.growth_requires_dormancy = false; (default)
3560 // shoot_parameters_trifoliate.determinate_shoot_growth = true; (default)
3561
3562 shoot_parameters_trifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
3563
3564
3565 ShootParameters shoot_parameters_unifoliate = shoot_parameters_trifoliate;
3566 shoot_parameters_unifoliate.phytomer_parameters = phytomer_parameters_unifoliate;
3567 shoot_parameters_unifoliate.max_nodes = 1;
3568 shoot_parameters_unifoliate.flower_bud_break_probability = 0;
3569 shoot_parameters_unifoliate.insertion_angle_tip = 0;
3570 shoot_parameters_unifoliate.insertion_angle_decay_rate = 0;
3571 shoot_parameters_unifoliate.vegetative_bud_break_time = 8;
3572 shoot_parameters_unifoliate.defineChildShootTypes({"trifoliate"}, {1.0});
3573
3574 defineShootType("unifoliate", shoot_parameters_unifoliate);
3575 defineShootType("trifoliate", shoot_parameters_trifoliate);
3576}
3577
3578uint PlantArchitecture::buildSoybeanPlant(const helios::vec3 &base_position) {
3579
3580 if (shoot_types.empty()) {
3581 // automatically initialize bean plant shoots
3582 initializeSoybeanShoots();
3583 }
3584
3585 uint plantID = addPlantInstance(base_position, 0);
3586
3587 AxisRotation base_rotation = make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
3588 uint uID_unifoliate = addBaseStemShoot(plantID, 1, base_rotation, 0.0005, 0.04, 0.01, 0.01, 0, "unifoliate");
3589
3590 appendShoot(plantID, uID_unifoliate, 1, make_AxisRotation(0, 0, 0.5f * M_PI), shoot_types.at("trifoliate").phytomer_parameters.internode.radius_initial.val(), shoot_types.at("trifoliate").internode_length_max.val(), 0.1, 0.1, 0, "trifoliate");
3591
3592 breakPlantDormancy(plantID);
3593
3594 setPlantPhenologicalThresholds(plantID, 0, 40, 5, 5, 30, 1000, false);
3595
3596 plant_instances.at(plantID).max_age = 365;
3597
3598 return plantID;
3599}
3600
3601void PlantArchitecture::initializeStrawberryShoots() {
3602
3603 // ---- Leaf Prototype ---- //
3604
3605 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3606 leaf_prototype.leaf_texture_file[0] = "StrawberryLeaf.png";
3607 leaf_prototype.leaf_aspect_ratio = 1.f;
3608 leaf_prototype.midrib_fold_fraction = 0.2f;
3609 leaf_prototype.longitudinal_curvature = 0.15f;
3610 leaf_prototype.lateral_curvature = -0.35f;
3611 leaf_prototype.wave_period = 0.4f;
3612 leaf_prototype.wave_amplitude = 0.03f;
3613 leaf_prototype.subdivisions = 6;
3614 leaf_prototype.unique_prototypes = 10;
3615
3616 // ---- Phytomer Parameters ---- //
3617
3618 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
3619
3620 phytomer_parameters.internode.pitch = 10;
3621 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(80, 100);
3622 phytomer_parameters.internode.radius_initial = 0.001;
3623 phytomer_parameters.internode.color = make_RGBcolor(0.15, 0.2, 0.1);
3624 phytomer_parameters.internode.length_segments = 1;
3625
3626 phytomer_parameters.petiole.petioles_per_internode = 1;
3627 phytomer_parameters.petiole.pitch.uniformDistribution(10, 45);
3628 phytomer_parameters.petiole.radius = 0.0025;
3629 phytomer_parameters.petiole.length.uniformDistribution(0.15, 0.35);
3630 phytomer_parameters.petiole.taper = 0.5;
3631 phytomer_parameters.petiole.curvature.uniformDistribution(-150, -50);
3632 phytomer_parameters.petiole.color = make_RGBcolor(0.18, 0.23, 0.1);
3633 phytomer_parameters.petiole.length_segments = 5;
3634
3635 phytomer_parameters.leaf.leaves_per_petiole = 3;
3636 phytomer_parameters.leaf.pitch.uniformDistribution(-35, 0);
3637 phytomer_parameters.leaf.yaw = -30;
3638 phytomer_parameters.leaf.roll = -30;
3639 phytomer_parameters.leaf.leaflet_offset = 0.01;
3640 phytomer_parameters.leaf.leaflet_scale = 1.0;
3641 phytomer_parameters.leaf.prototype_scale = 0.12;
3642 phytomer_parameters.leaf.prototype = leaf_prototype;
3643
3644 phytomer_parameters.peduncle.length.uniformDistribution(0.16, 0.2);
3645 phytomer_parameters.peduncle.radius = 0.0018;
3646 phytomer_parameters.peduncle.pitch.uniformDistribution(35, 55);
3647 phytomer_parameters.peduncle.roll = 90;
3648 phytomer_parameters.peduncle.curvature = -150;
3649 phytomer_parameters.peduncle.length_segments = 5;
3650 phytomer_parameters.peduncle.radial_subdivisions = 6;
3651 phytomer_parameters.peduncle.color = phytomer_parameters.petiole.color;
3652
3653 phytomer_parameters.inflorescence.flowers_per_peduncle = 1; //.uniformDistribution(1, 2);
3654 phytomer_parameters.inflorescence.flower_offset = 0.2;
3655 phytomer_parameters.inflorescence.pitch = 70;
3656 phytomer_parameters.inflorescence.roll = 90;
3657 phytomer_parameters.inflorescence.flower_prototype_scale = 0.04;
3658 phytomer_parameters.inflorescence.flower_prototype_function = StrawberryFlowerPrototype;
3659 phytomer_parameters.inflorescence.fruit_prototype_scale = 0.085;
3660 phytomer_parameters.inflorescence.fruit_prototype_function = StrawberryFruitPrototype;
3661 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction = 0.65;
3662
3663 // ---- Shoot Parameters ---- //
3664
3665 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
3666 shoot_parameters.phytomer_parameters = phytomer_parameters;
3667
3668 shoot_parameters.max_nodes = 15;
3669 shoot_parameters.insertion_angle_tip = 40;
3670 shoot_parameters.insertion_angle_decay_rate = 0;
3671 shoot_parameters.internode_length_max = 0.015;
3672 shoot_parameters.internode_length_decay_rate = 0;
3673 shoot_parameters.internode_length_min = 0.0;
3674 shoot_parameters.base_roll = 90;
3675 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
3676 shoot_parameters.gravitropic_curvature.uniformDistribution(-10, 0);
3677 shoot_parameters.tortuosity = 0;
3678
3679 shoot_parameters.phyllochron_min = 2;
3680 shoot_parameters.elongation_rate_max = 0.1;
3681 shoot_parameters.girth_area_factor = 2.f;
3682 shoot_parameters.vegetative_bud_break_time = 15;
3683 shoot_parameters.vegetative_bud_break_probability_min = 0.;
3684 shoot_parameters.vegetative_bud_break_probability_decay_rate = -0.5;
3685 shoot_parameters.flower_bud_break_probability = 1;
3686 shoot_parameters.fruit_set_probability = 0.3;
3687 shoot_parameters.flowers_require_dormancy = false;
3688 shoot_parameters.growth_requires_dormancy = false;
3689 shoot_parameters.determinate_shoot_growth = true;
3690
3691 shoot_parameters.defineChildShootTypes({"mainstem"}, {1.0});
3692
3693 defineShootType("mainstem", shoot_parameters);
3694}
3695
3696uint PlantArchitecture::buildStrawberryPlant(const helios::vec3 &base_position) {
3697
3698 if (shoot_types.empty()) {
3699 // automatically initialize strawberry plant shoots
3700 initializeStrawberryShoots();
3701 }
3702
3703 uint plantID = addPlantInstance(base_position, 0);
3704
3705 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
3706 uint uID_stem = addBaseStemShoot(plantID, 1, base_rotation, 0.001, 0.004, 0.01, 0.01, 0, "mainstem");
3707
3708 breakPlantDormancy(plantID);
3709
3710 setPlantPhenologicalThresholds(plantID, 0, 40, 5, 5, 30, 1000, false);
3711
3712 plant_instances.at(plantID).max_age = 120;
3713
3714 return plantID;
3715}
3716
3717void PlantArchitecture::initializeSugarbeetShoots() {
3718
3719 // ---- Leaf Prototype ---- //
3720
3721 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3722 leaf_prototype.leaf_texture_file[0] = "SugarbeetLeaf.png";
3723 leaf_prototype.leaf_aspect_ratio = 0.4f;
3724 leaf_prototype.midrib_fold_fraction = 0.1f;
3725 leaf_prototype.longitudinal_curvature = -0.2f;
3726 leaf_prototype.lateral_curvature = -0.4f;
3727 leaf_prototype.petiole_roll = 0.75f;
3728 leaf_prototype.wave_period.uniformDistribution(0.08f, 0.15f);
3729 leaf_prototype.wave_amplitude.uniformDistribution(0.02, 0.04);
3730 leaf_prototype.subdivisions = 20;
3731 ;
3732 leaf_prototype.unique_prototypes = 10;
3733
3734 // ---- Phytomer Parameters ---- //
3735
3736 PhytomerParameters phytomer_parameters_sugarbeet(context_ptr->getRandomGenerator());
3737
3738 phytomer_parameters_sugarbeet.internode.pitch = 0;
3739 phytomer_parameters_sugarbeet.internode.phyllotactic_angle = 137.5;
3740 phytomer_parameters_sugarbeet.internode.radius_initial = 0.005;
3741 phytomer_parameters_sugarbeet.internode.color = make_RGBcolor(0.44, 0.58, 0.19);
3742 phytomer_parameters_sugarbeet.internode.length_segments = 1;
3743 phytomer_parameters_sugarbeet.internode.max_vegetative_buds_per_petiole = 0;
3744 phytomer_parameters_sugarbeet.internode.max_floral_buds_per_petiole = 0;
3745
3746 phytomer_parameters_sugarbeet.petiole.petioles_per_internode = 1;
3747 phytomer_parameters_sugarbeet.petiole.pitch.uniformDistribution(0, 40);
3748 phytomer_parameters_sugarbeet.petiole.radius = 0.005;
3749 phytomer_parameters_sugarbeet.petiole.length.uniformDistribution(0.15, 0.2);
3750 phytomer_parameters_sugarbeet.petiole.taper = 0.6;
3751 phytomer_parameters_sugarbeet.petiole.curvature.uniformDistribution(-300, 100);
3752 phytomer_parameters_sugarbeet.petiole.color = phytomer_parameters_sugarbeet.internode.color;
3753 phytomer_parameters_sugarbeet.petiole.length_segments = 8;
3754
3755 phytomer_parameters_sugarbeet.leaf.leaves_per_petiole = 1;
3756 phytomer_parameters_sugarbeet.leaf.pitch.uniformDistribution(-10, 0);
3757 phytomer_parameters_sugarbeet.leaf.yaw.uniformDistribution(-5, 5);
3758 phytomer_parameters_sugarbeet.leaf.roll.uniformDistribution(-15, 15);
3759 phytomer_parameters_sugarbeet.leaf.prototype_scale.uniformDistribution(0.15, 0.25);
3760 phytomer_parameters_sugarbeet.leaf.prototype = leaf_prototype;
3761
3762 // ---- Shoot Parameters ---- //
3763
3764 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
3765 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters_sugarbeet;
3766 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0;
3767 shoot_parameters_mainstem.phyllochron_min = 2;
3768 shoot_parameters_mainstem.elongation_rate_max = 0.1;
3769 shoot_parameters_mainstem.girth_area_factor = 20.f;
3770 shoot_parameters_mainstem.gravitropic_curvature = 10;
3771 shoot_parameters_mainstem.internode_length_max = 0.001;
3772 shoot_parameters_mainstem.internode_length_decay_rate = 0;
3773 shoot_parameters_mainstem.flowers_require_dormancy = false;
3774 shoot_parameters_mainstem.growth_requires_dormancy = false;
3775 shoot_parameters_mainstem.flower_bud_break_probability = 0.0;
3776 shoot_parameters_mainstem.max_nodes = 30;
3777
3778 defineShootType("mainstem", shoot_parameters_mainstem);
3779}
3780
3781uint PlantArchitecture::buildSugarbeetPlant(const helios::vec3 &base_position) {
3782
3783 if (shoot_types.empty()) {
3784 // automatically initialize sugarbeet plant shoots
3785 initializeSugarbeetShoots();
3786 }
3787
3788 uint plantID = addPlantInstance(base_position, 0);
3789
3790 uint uID_stem = addBaseStemShoot(plantID, 3, make_AxisRotation(context_ptr->randu(0.f, 0.01f * M_PI), 0.f * context_ptr->randu(0.f, 2.f * M_PI), 0.25f * M_PI), 0.005, 0.001, 1, 1, 0, "mainstem");
3791
3792 breakPlantDormancy(plantID);
3793
3794 setPlantPhenologicalThresholds(plantID, 0, -1, -1, -1, -1, 1000, false);
3795
3796 plant_instances.at(plantID).max_age = 365;
3797
3798 return plantID;
3799}
3800
3801void PlantArchitecture::initializeTomatoShoots() {
3802
3803 // ---- Leaf Prototype ---- //
3804
3805 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3806 leaf_prototype.leaf_texture_file[0] = "TomatoLeaf_centered.png";
3807 leaf_prototype.leaf_aspect_ratio = 0.5f;
3808 leaf_prototype.midrib_fold_fraction = 0.1f;
3809 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.45, -0.2f);
3810 leaf_prototype.lateral_curvature = -0.3f;
3811 leaf_prototype.wave_period = 0.35f;
3812 leaf_prototype.wave_amplitude = 0.08f;
3813 leaf_prototype.subdivisions = 6;
3814 leaf_prototype.unique_prototypes = 5;
3815
3816 // ---- Phytomer Parameters ---- //
3817
3818 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
3819
3820 phytomer_parameters.internode.pitch = 10;
3821 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(140, 220);
3822 phytomer_parameters.internode.radius_initial = 0.001;
3823 phytomer_parameters.internode.color = make_RGBcolor(0.2495, 0.3162, 0.0657);
3824 phytomer_parameters.internode.length_segments = 1;
3825
3826 phytomer_parameters.petiole.petioles_per_internode = 1;
3827 phytomer_parameters.petiole.pitch.uniformDistribution(45, 60);
3828 phytomer_parameters.petiole.radius = 0.002;
3829 phytomer_parameters.petiole.length = 0.2;
3830 phytomer_parameters.petiole.taper = 0.25;
3831 phytomer_parameters.petiole.curvature.uniformDistribution(-150, -50);
3832 phytomer_parameters.petiole.color = make_RGBcolor(0.3, 0.37, 0.0657);
3833 phytomer_parameters.petiole.length_segments = 5;
3834
3835 phytomer_parameters.leaf.leaves_per_petiole = 7;
3836 phytomer_parameters.leaf.pitch.uniformDistribution(-30, 5);
3837 phytomer_parameters.leaf.yaw = 10;
3838 phytomer_parameters.leaf.roll = 0;
3839 phytomer_parameters.leaf.leaflet_offset = 0.15;
3840 phytomer_parameters.leaf.leaflet_scale = 0.7;
3841 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.12, 0.18);
3842 phytomer_parameters.leaf.prototype = leaf_prototype;
3843
3844 phytomer_parameters.peduncle.length = 0.18;
3845 phytomer_parameters.peduncle.radius = 0.0015;
3846 phytomer_parameters.peduncle.pitch = 20;
3847 phytomer_parameters.peduncle.roll = 0;
3848 phytomer_parameters.peduncle.curvature = -900;
3849 phytomer_parameters.peduncle.color = phytomer_parameters.internode.color;
3850 phytomer_parameters.peduncle.length_segments = 5;
3851 phytomer_parameters.peduncle.radial_subdivisions = 8;
3852
3853 phytomer_parameters.inflorescence.flowers_per_peduncle = 6;
3854 phytomer_parameters.inflorescence.flower_offset = 0.15;
3855 phytomer_parameters.inflorescence.pitch = 80;
3856 phytomer_parameters.inflorescence.roll = 180;
3857 phytomer_parameters.inflorescence.flower_prototype_scale = 0.05;
3858 phytomer_parameters.inflorescence.flower_prototype_function = TomatoFlowerPrototype;
3859 phytomer_parameters.inflorescence.fruit_prototype_scale = 0.15;
3860 phytomer_parameters.inflorescence.fruit_prototype_function = TomatoFruitPrototype;
3861 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction = 0.;
3862
3863 // ---- Shoot Parameters ---- //
3864
3865 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
3866 shoot_parameters.phytomer_parameters = phytomer_parameters;
3867 shoot_parameters.phytomer_parameters.phytomer_creation_function = TomatoPhytomerCreationFunction;
3868
3869 shoot_parameters.max_nodes = 16;
3870 shoot_parameters.insertion_angle_tip = 30;
3871 shoot_parameters.insertion_angle_decay_rate = 0;
3872 shoot_parameters.internode_length_max = 0.04;
3873 shoot_parameters.internode_length_min = 0.0;
3874 shoot_parameters.internode_length_decay_rate = 0;
3875 shoot_parameters.base_roll = 90;
3876 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
3877 shoot_parameters.gravitropic_curvature = 150;
3878 shoot_parameters.tortuosity = 3;
3879
3880 shoot_parameters.phyllochron_min = 2;
3881 shoot_parameters.elongation_rate_max = 0.1;
3882 shoot_parameters.girth_area_factor = 2.f;
3883 shoot_parameters.vegetative_bud_break_time = 30;
3884 shoot_parameters.vegetative_bud_break_probability_min = 0.25;
3885 shoot_parameters.vegetative_bud_break_probability_decay_rate = -0.25;
3886 shoot_parameters.flower_bud_break_probability = 0.2;
3887 shoot_parameters.fruit_set_probability = 0.8;
3888 shoot_parameters.flowers_require_dormancy = false;
3889 shoot_parameters.growth_requires_dormancy = false;
3890 shoot_parameters.determinate_shoot_growth = true;
3891
3892 shoot_parameters.defineChildShootTypes({"mainstem"}, {1.0});
3893
3894 defineShootType("mainstem", shoot_parameters);
3895}
3896
3897uint PlantArchitecture::buildTomatoPlant(const helios::vec3 &base_position) {
3898
3899 if (shoot_types.empty()) {
3900 // automatically initialize tomato plant shoots
3901 initializeTomatoShoots();
3902 }
3903
3904 uint plantID = addPlantInstance(base_position, 0);
3905
3906 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
3907 uint uID_stem = addBaseStemShoot(plantID, 1, base_rotation, 0.002, 0.04, 0.01, 0.01, 0, "mainstem");
3908
3909 breakPlantDormancy(plantID);
3910
3911 setPlantPhenologicalThresholds(plantID, 0, 40, 5, 5, 30, 1000, false);
3912
3913 plant_instances.at(plantID).max_age = 365;
3914
3915 return plantID;
3916}
3917
3918void PlantArchitecture::initializeCherryTomatoShoots() {
3919
3920 // ---- Leaf Prototype ---- //
3921
3922 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
3923 leaf_prototype.leaf_texture_file[0] = "CherryTomatoLeaf.png";
3924 leaf_prototype.leaf_aspect_ratio = 0.6f;
3925 leaf_prototype.midrib_fold_fraction = 0.1f;
3926 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.3, -0.15f);
3927 leaf_prototype.lateral_curvature = -0.8f;
3928 leaf_prototype.wave_period = 0.35f;
3929 leaf_prototype.wave_amplitude = 0.08f;
3930 leaf_prototype.subdivisions = 7;
3931 leaf_prototype.unique_prototypes = 5;
3932
3933 // ---- Phytomer Parameters ---- //
3934
3935 PhytomerParameters phytomer_parameters(context_ptr->getRandomGenerator());
3936
3937 phytomer_parameters.internode.pitch = 5;
3938 phytomer_parameters.internode.phyllotactic_angle.uniformDistribution(14, 220);
3939 phytomer_parameters.internode.radius_initial = 0.001;
3940 phytomer_parameters.internode.color = make_RGBcolor(0.2495, 0.3162, 0.0657);
3941 phytomer_parameters.internode.length_segments = 1;
3942 phytomer_parameters.internode.radial_subdivisions = 14;
3943
3944 phytomer_parameters.petiole.petioles_per_internode = 1;
3945 phytomer_parameters.petiole.pitch.uniformDistribution(45, 60);
3946 phytomer_parameters.petiole.radius = 0.0025;
3947 phytomer_parameters.petiole.length = 0.25;
3948 phytomer_parameters.petiole.taper = 0.25;
3949 phytomer_parameters.petiole.curvature.uniformDistribution(-250, 0);
3950 phytomer_parameters.petiole.color = make_RGBcolor(0.32, 0.37, 0.12);
3951 phytomer_parameters.petiole.length_segments = 5;
3952
3953 phytomer_parameters.leaf.leaves_per_petiole = 9;
3954 phytomer_parameters.leaf.pitch.uniformDistribution(-30, 5);
3955 phytomer_parameters.leaf.yaw = 10;
3956 phytomer_parameters.leaf.roll.uniformDistribution(-20, 20);
3957 phytomer_parameters.leaf.leaflet_offset = 0.22;
3958 phytomer_parameters.leaf.leaflet_scale = 0.9;
3959 phytomer_parameters.leaf.prototype_scale.uniformDistribution(0.12, 0.17);
3960 phytomer_parameters.leaf.prototype = leaf_prototype;
3961
3962 phytomer_parameters.peduncle.length = 0.2;
3963 phytomer_parameters.peduncle.radius = 0.0015;
3964 phytomer_parameters.peduncle.pitch = 20;
3965 phytomer_parameters.peduncle.roll = 0;
3966 phytomer_parameters.peduncle.curvature = -1000;
3967 phytomer_parameters.peduncle.color = phytomer_parameters.internode.color;
3968 phytomer_parameters.peduncle.length_segments = 5;
3969 phytomer_parameters.peduncle.radial_subdivisions = 8;
3970
3971 phytomer_parameters.inflorescence.flowers_per_peduncle = 6;
3972 phytomer_parameters.inflorescence.flower_offset = 0.15;
3973 phytomer_parameters.inflorescence.pitch = 80;
3974 phytomer_parameters.inflorescence.roll = 180;
3975 phytomer_parameters.inflorescence.flower_prototype_scale = 0.05;
3976 phytomer_parameters.inflorescence.flower_prototype_function = TomatoFlowerPrototype;
3977 phytomer_parameters.inflorescence.fruit_prototype_scale = 0.1;
3978 phytomer_parameters.inflorescence.fruit_prototype_function = TomatoFruitPrototype;
3979 phytomer_parameters.inflorescence.fruit_gravity_factor_fraction = 0.2;
3980
3981 // ---- Shoot Parameters ---- //
3982
3983 ShootParameters shoot_parameters(context_ptr->getRandomGenerator());
3984 shoot_parameters.phytomer_parameters = phytomer_parameters;
3985 shoot_parameters.phytomer_parameters.phytomer_creation_function = CherryTomatoPhytomerCreationFunction;
3986 shoot_parameters.phytomer_parameters.phytomer_callback_function = CherryTomatoPhytomerCallbackFunction;
3987
3988 shoot_parameters.max_nodes = 100;
3989 shoot_parameters.insertion_angle_tip = 30;
3990 shoot_parameters.insertion_angle_decay_rate = 0;
3991 shoot_parameters.internode_length_max = 0.04;
3992 shoot_parameters.internode_length_min = 0.0;
3993 shoot_parameters.internode_length_decay_rate = 0;
3994 shoot_parameters.base_roll = 90;
3995 shoot_parameters.base_yaw.uniformDistribution(-20, 20);
3996 shoot_parameters.gravitropic_curvature = 800;
3997 shoot_parameters.tortuosity = 1.5;
3998
3999 shoot_parameters.phyllochron_min = 4;
4000 shoot_parameters.elongation_rate_max = 0.1;
4001 shoot_parameters.girth_area_factor = 2.f;
4002 shoot_parameters.vegetative_bud_break_time = 40;
4003 shoot_parameters.vegetative_bud_break_probability_min = 0.2;
4004 shoot_parameters.vegetative_bud_break_probability_decay_rate = 0.;
4005 shoot_parameters.flower_bud_break_probability = 0.5;
4006 shoot_parameters.fruit_set_probability = 0.9;
4007 shoot_parameters.flowers_require_dormancy = false;
4008 shoot_parameters.growth_requires_dormancy = false;
4009 shoot_parameters.determinate_shoot_growth = false;
4010
4011 shoot_parameters.defineChildShootTypes({"mainstem"}, {1.0});
4012
4013 defineShootType("mainstem", shoot_parameters);
4014}
4015
4016uint PlantArchitecture::buildCherryTomatoPlant(const helios::vec3 &base_position) {
4017
4018 if (shoot_types.empty()) {
4019 // automatically initialize cherry tomato plant shoots
4020 initializeCherryTomatoShoots();
4021 }
4022
4023 uint plantID = addPlantInstance(base_position, 0);
4024
4025 AxisRotation base_rotation = make_AxisRotation(0, context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI));
4026 uint uID_stem = addBaseStemShoot(plantID, 1, base_rotation, 0.002, 0.06, 0.01, 0.01, 0, "mainstem");
4027
4028 breakPlantDormancy(plantID);
4029
4030 setPlantPhenologicalThresholds(plantID, 0, 50, 10, 5, 30, 1000);
4031
4032 plant_instances.at(plantID).max_age = 175;
4033
4034 return plantID;
4035}
4036
4037void PlantArchitecture::initializeWalnutTreeShoots() {
4038
4039 // ---- Leaf Prototype ---- //
4040
4041 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
4042 leaf_prototype.leaf_texture_file[0] = "WalnutLeaf.png";
4043 leaf_prototype.leaf_aspect_ratio = 0.5f;
4044 leaf_prototype.midrib_fold_fraction = 0.15f;
4045 leaf_prototype.longitudinal_curvature = -0.2f;
4046 leaf_prototype.lateral_curvature = 0.1f;
4047 leaf_prototype.wave_period.uniformDistribution(0.08, 0.15);
4048 leaf_prototype.wave_amplitude.uniformDistribution(0.02, 0.04);
4049 leaf_prototype.subdivisions = 3;
4050 leaf_prototype.unique_prototypes = 5;
4051
4052 // ---- Phytomer Parameters ---- //
4053
4054 PhytomerParameters phytomer_parameters_walnut(context_ptr->getRandomGenerator());
4055
4056 phytomer_parameters_walnut.internode.pitch = 0;
4057 phytomer_parameters_walnut.internode.phyllotactic_angle.uniformDistribution(160, 200);
4058 phytomer_parameters_walnut.internode.radius_initial = 0.004;
4059 phytomer_parameters_walnut.internode.length_segments = 1;
4060 phytomer_parameters_walnut.internode.image_texture = "AppleBark.jpg";
4061 phytomer_parameters_walnut.internode.max_floral_buds_per_petiole = 3;
4062
4063 phytomer_parameters_walnut.petiole.petioles_per_internode = 1;
4064 phytomer_parameters_walnut.petiole.pitch.uniformDistribution(-80, -70);
4065 phytomer_parameters_walnut.petiole.taper = 0.2;
4066 phytomer_parameters_walnut.petiole.curvature.uniformDistribution(-1000, 0);
4067 phytomer_parameters_walnut.petiole.length = 0.15;
4068 phytomer_parameters_walnut.petiole.radius = 0.0015;
4069 phytomer_parameters_walnut.petiole.length_segments = 5;
4070 phytomer_parameters_walnut.petiole.radial_subdivisions = 3;
4071 phytomer_parameters_walnut.petiole.color = make_RGBcolor(0.61, 0.5, 0.24);
4072
4073 phytomer_parameters_walnut.leaf.leaves_per_petiole = 5;
4074 phytomer_parameters_walnut.leaf.pitch.uniformDistribution(-40, 0);
4075 phytomer_parameters_walnut.leaf.prototype_scale = 0.14;
4076 phytomer_parameters_walnut.leaf.leaflet_scale = 0.7;
4077 phytomer_parameters_walnut.leaf.leaflet_offset = 0.35;
4078 phytomer_parameters_walnut.leaf.prototype = leaf_prototype;
4079
4080 phytomer_parameters_walnut.peduncle.length = 0.02;
4081 phytomer_parameters_walnut.peduncle.radius = 0.0005;
4082 phytomer_parameters_walnut.peduncle.pitch = 90;
4083 phytomer_parameters_walnut.peduncle.roll = 90;
4084 phytomer_parameters_walnut.peduncle.length_segments = 1;
4085
4086 phytomer_parameters_walnut.inflorescence.flowers_per_peduncle = 1;
4087 phytomer_parameters_walnut.inflorescence.pitch = 0;
4088 phytomer_parameters_walnut.inflorescence.roll = 0;
4089 phytomer_parameters_walnut.inflorescence.flower_prototype_scale = 0.03;
4090 phytomer_parameters_walnut.inflorescence.flower_prototype_function = WalnutFlowerPrototype;
4091 phytomer_parameters_walnut.inflorescence.fruit_prototype_scale = 0.075;
4092 phytomer_parameters_walnut.inflorescence.fruit_prototype_function = WalnutFruitPrototype;
4093
4094 // ---- Shoot Parameters ---- //
4095
4096 // Trunk
4097 ShootParameters shoot_parameters_trunk(context_ptr->getRandomGenerator());
4098 shoot_parameters_trunk.phytomer_parameters = phytomer_parameters_walnut;
4099 shoot_parameters_trunk.phytomer_parameters.internode.phyllotactic_angle = 0;
4100 shoot_parameters_trunk.phytomer_parameters.internode.radius_initial = 0.01;
4101 shoot_parameters_trunk.phytomer_parameters.internode.radial_subdivisions = 24;
4102 shoot_parameters_trunk.max_nodes = 20;
4103 shoot_parameters_trunk.girth_area_factor = 5.f;
4104 shoot_parameters_trunk.vegetative_bud_break_probability_min = 0;
4105 shoot_parameters_trunk.vegetative_bud_break_time = 0;
4106 shoot_parameters_trunk.tortuosity = 1;
4107 shoot_parameters_trunk.internode_length_max = 0.05;
4108 shoot_parameters_trunk.internode_length_decay_rate = 0;
4109 shoot_parameters_trunk.defineChildShootTypes({"scaffold"}, {1});
4110
4111 // Proleptic shoots
4112 ShootParameters shoot_parameters_proleptic(context_ptr->getRandomGenerator());
4113 shoot_parameters_proleptic.phytomer_parameters = phytomer_parameters_walnut;
4114 shoot_parameters_proleptic.phytomer_parameters.internode.color = make_RGBcolor(0.3, 0.2, 0.2);
4115 shoot_parameters_proleptic.phytomer_parameters.phytomer_creation_function = WalnutPhytomerCreationFunction;
4116 // shoot_parameters_proleptic.phytomer_parameters.phytomer_callback_function = WalnutPhytomerCallbackFunction;
4117 shoot_parameters_proleptic.max_nodes = 24;
4118 shoot_parameters_proleptic.max_nodes_per_season = 12;
4119 shoot_parameters_proleptic.phyllochron_min = 2.;
4120 shoot_parameters_proleptic.elongation_rate_max = 0.15;
4121 shoot_parameters_proleptic.girth_area_factor = 9.f;
4122 shoot_parameters_proleptic.vegetative_bud_break_probability_min = 0.05;
4123 shoot_parameters_proleptic.vegetative_bud_break_probability_decay_rate = 0.7;
4124 shoot_parameters_proleptic.vegetative_bud_break_time = 3;
4125 shoot_parameters_proleptic.gravitropic_curvature = 300;
4126 shoot_parameters_proleptic.tortuosity = 4;
4127 shoot_parameters_proleptic.insertion_angle_tip.uniformDistribution(20, 25);
4128 shoot_parameters_proleptic.insertion_angle_decay_rate = 15;
4129 shoot_parameters_proleptic.internode_length_max = 0.08;
4130 shoot_parameters_proleptic.internode_length_min = 0.01;
4131 shoot_parameters_proleptic.internode_length_decay_rate = 0.006;
4132 shoot_parameters_proleptic.fruit_set_probability = 0.3;
4133 shoot_parameters_proleptic.flower_bud_break_probability = 0.3;
4134 shoot_parameters_proleptic.max_terminal_floral_buds = 4;
4135 shoot_parameters_proleptic.flowers_require_dormancy = true;
4136 shoot_parameters_proleptic.growth_requires_dormancy = true;
4137 shoot_parameters_proleptic.determinate_shoot_growth = false;
4138 shoot_parameters_proleptic.defineChildShootTypes({"proleptic"}, {1.0});
4139
4140 // Main scaffolds
4141 ShootParameters shoot_parameters_scaffold = shoot_parameters_proleptic;
4142 shoot_parameters_scaffold.phytomer_parameters.internode.radial_subdivisions = 10;
4143 shoot_parameters_scaffold.max_nodes = 30;
4144 shoot_parameters_scaffold.gravitropic_curvature = 300;
4145 shoot_parameters_scaffold.internode_length_max = 0.06;
4146 shoot_parameters_scaffold.tortuosity = 4;
4147 shoot_parameters_scaffold.defineChildShootTypes({"proleptic"}, {1.0});
4148
4149 defineShootType("trunk", shoot_parameters_trunk);
4150 defineShootType("scaffold", shoot_parameters_scaffold);
4151 defineShootType("proleptic", shoot_parameters_proleptic);
4152}
4153
4154uint PlantArchitecture::buildWalnutTree(const helios::vec3 &base_position) {
4155
4156 if (shoot_types.empty()) {
4157 // automatically initialize walnut tree shoots
4158 initializeWalnutTreeShoots();
4159 }
4160
4161 // Get training system parameters (with defaults matching original hard-coded values)
4162 auto trunk_height = getParameterValue(current_build_parameters, "trunk_height", 0.8f, 0.1f, 3.f, "total trunk height in meters");
4163 auto num_scaffolds = uint(getParameterValue(current_build_parameters, "num_scaffolds", 4.f, 2.f, 8.f, "number of scaffold branches"));
4164 auto scaffold_angle = getParameterValue(current_build_parameters, "scaffold_angle", 40.f, 25.f, 60.f, "scaffold branch angle in degrees");
4165
4166 // Calculate trunk nodes based on desired height and internode length
4167 float trunk_internode_length = 0.04f; // Default internode length for walnut
4168 uint trunk_nodes = uint(trunk_height / trunk_internode_length);
4169 if (trunk_nodes < 1)
4170 trunk_nodes = 1;
4171
4172 // Fixed training parameters (not user-customizable)
4173 float scaffold_radius = 0.007f;
4174 float scaffold_length = 0.06f;
4175
4176 uint plantID = addPlantInstance(base_position, 0);
4177
4178 uint uID_trunk = addBaseStemShoot(plantID, trunk_nodes, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), 0.f * M_PI), shoot_types.at("trunk").phytomer_parameters.internode.radius_initial.val(),
4179 trunk_internode_length, 1.f, 1.f, 0, "trunk");
4180 appendPhytomerToShoot(plantID, uID_trunk, shoot_types.at("trunk").phytomer_parameters, 0, 0.01, 1, 1);
4181
4182 plant_instances.at(plantID).shoot_tree.at(uID_trunk)->meristem_is_alive = false;
4183
4184 auto phytomers = plant_instances.at(plantID).shoot_tree.at(uID_trunk)->phytomers;
4185 for (const auto &phytomer: phytomers) {
4186 phytomer->removeLeaf();
4187 phytomer->setVegetativeBudState(BUD_DEAD);
4188 phytomer->setFloralBudState(BUD_DEAD);
4189 }
4190
4191 // Hard-coded scaffold node range (not user-customizable)
4192 uint scaffold_nodes_min = 5;
4193 uint scaffold_nodes_max = 7;
4194
4195 for (int i = 0; i < num_scaffolds; i++) {
4196 float pitch = deg2rad(scaffold_angle) + context_ptr->randu(-0.1f, 0.1f); // Small randomness around specified angle
4197 uint scaffold_nodes = context_ptr->randu(int(scaffold_nodes_min), int(scaffold_nodes_max));
4198 uint uID_shoot = addChildShoot(plantID, uID_trunk, getShootNodeCount(plantID, uID_trunk) - i - 1, scaffold_nodes, make_AxisRotation(pitch, (float(i) + context_ptr->randu(-0.2f, 0.2f)) / float(num_scaffolds) * 2 * M_PI, 0), scaffold_radius,
4199 scaffold_length, 1.f, 1.f, 0.5, "scaffold", 0);
4200 }
4201
4202 makePlantDormant(plantID);
4203
4204 setPlantPhenologicalThresholds(plantID, 165, -1, 3, 7, 20, 200, false);
4205
4206 plant_instances.at(plantID).max_age = 1460;
4207
4208 return plantID;
4209}
4210
4211void PlantArchitecture::initializeWheatShoots() {
4212
4213 // ---- Leaf Prototype ---- //
4214
4215 LeafPrototype leaf_prototype(context_ptr->getRandomGenerator());
4216 leaf_prototype.leaf_texture_file[0] = "SorghumLeaf.png";
4217 leaf_prototype.leaf_aspect_ratio = 0.1f;
4218 leaf_prototype.midrib_fold_fraction = 0.3f;
4219 leaf_prototype.longitudinal_curvature.uniformDistribution(-0.5, -0.1);
4220 leaf_prototype.lateral_curvature = -0.3;
4221 leaf_prototype.petiole_roll = 0.04f;
4222 leaf_prototype.wave_period = 0.1f;
4223 leaf_prototype.wave_amplitude = 0.1f;
4224 leaf_prototype.leaf_buckle_length.uniformDistribution(0.5, 0.6);
4225 leaf_prototype.leaf_buckle_angle.uniformDistribution(25, 35);
4226 leaf_prototype.subdivisions = 20;
4227 leaf_prototype.unique_prototypes = 10;
4228
4229 // ---- Phytomer Parameters ---- //
4230
4231 PhytomerParameters phytomer_parameters_wheat(context_ptr->getRandomGenerator());
4232
4233 phytomer_parameters_wheat.internode.pitch = 0;
4234 phytomer_parameters_wheat.internode.phyllotactic_angle.uniformDistribution(67, 77);
4235 phytomer_parameters_wheat.internode.radius_initial = 0.001;
4236 phytomer_parameters_wheat.internode.color = make_RGBcolor(0.27, 0.31, 0.16);
4237 phytomer_parameters_wheat.internode.length_segments = 1;
4238 phytomer_parameters_wheat.internode.radial_subdivisions = 6;
4239 phytomer_parameters_wheat.internode.max_floral_buds_per_petiole = 0;
4240 phytomer_parameters_wheat.internode.max_vegetative_buds_per_petiole = 0;
4241
4242 phytomer_parameters_wheat.petiole.petioles_per_internode = 1;
4243 phytomer_parameters_wheat.petiole.pitch.uniformDistribution(-40, -20);
4244 phytomer_parameters_wheat.petiole.radius = 0.0;
4245 phytomer_parameters_wheat.petiole.length = 0.005;
4246 phytomer_parameters_wheat.petiole.taper = 0;
4247 phytomer_parameters_wheat.petiole.curvature = 0;
4248 phytomer_parameters_wheat.petiole.length_segments = 1;
4249
4250 phytomer_parameters_wheat.leaf.leaves_per_petiole = 1;
4251 phytomer_parameters_wheat.leaf.pitch = 0;
4252 phytomer_parameters_wheat.leaf.yaw = 0;
4253 phytomer_parameters_wheat.leaf.roll = 0;
4254 phytomer_parameters_wheat.leaf.prototype_scale = 0.22;
4255 phytomer_parameters_wheat.leaf.prototype = leaf_prototype;
4256
4257 phytomer_parameters_wheat.peduncle.length = 0.1;
4258 phytomer_parameters_wheat.peduncle.radius = 0.002;
4259 phytomer_parameters_wheat.peduncle.color = phytomer_parameters_wheat.internode.color;
4260 phytomer_parameters_wheat.peduncle.curvature = -100;
4261 phytomer_parameters_wheat.peduncle.radial_subdivisions = 6;
4262
4263 phytomer_parameters_wheat.inflorescence.flowers_per_peduncle = 1;
4264 phytomer_parameters_wheat.inflorescence.pitch = 0;
4265 phytomer_parameters_wheat.inflorescence.roll = 0;
4266 phytomer_parameters_wheat.inflorescence.fruit_prototype_scale = 0.1;
4267 phytomer_parameters_wheat.inflorescence.fruit_prototype_function = WheatSpikePrototype;
4268
4269 phytomer_parameters_wheat.phytomer_creation_function = WheatPhytomerCreationFunction;
4270
4271 // ---- Shoot Parameters ---- //
4272
4273 ShootParameters shoot_parameters_mainstem(context_ptr->getRandomGenerator());
4274 shoot_parameters_mainstem.phytomer_parameters = phytomer_parameters_wheat;
4275 shoot_parameters_mainstem.vegetative_bud_break_probability_min = 0;
4276 shoot_parameters_mainstem.flower_bud_break_probability = 1;
4277 shoot_parameters_mainstem.phyllochron_min = 2;
4278 shoot_parameters_mainstem.elongation_rate_max = 0.1;
4279 shoot_parameters_mainstem.girth_area_factor = 6.f;
4280 shoot_parameters_mainstem.gravitropic_curvature.uniformDistribution(-500, -200);
4281 shoot_parameters_mainstem.flowers_require_dormancy = false;
4282 shoot_parameters_mainstem.growth_requires_dormancy = false;
4283 shoot_parameters_mainstem.determinate_shoot_growth = false;
4284 shoot_parameters_mainstem.fruit_set_probability = 1.0;
4285 shoot_parameters_mainstem.defineChildShootTypes({"mainstem"}, {1.0});
4286 shoot_parameters_mainstem.max_nodes = 20;
4287 shoot_parameters_mainstem.max_terminal_floral_buds = 1;
4288
4289 defineShootType("mainstem", shoot_parameters_mainstem);
4290}
4291
4292uint PlantArchitecture::buildWheatPlant(const helios::vec3 &base_position) {
4293
4294 if (shoot_types.empty()) {
4295 // automatically initialize wheat plant shoots
4296 initializeWheatShoots();
4297 }
4298
4299 uint plantID = addPlantInstance(base_position - make_vec3(0, 0, 0.025), 0);
4300
4301 uint uID_stem = addBaseStemShoot(plantID, 1, make_AxisRotation(context_ptr->randu(0.f, 0.05f * M_PI), context_ptr->randu(0.f, 2.f * M_PI), context_ptr->randu(0.f, 2.f * M_PI)), 0.001, 0.025, 0.01, 0.01, 0, "mainstem");
4302
4303 breakPlantDormancy(plantID);
4304
4305 setPlantPhenologicalThresholds(plantID, 0, -1, -1, 4, 10, 1000, false);
4306
4307 plant_instances.at(plantID).max_age = 365;
4308
4309 return plantID;
4310}