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