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