1.3.64
 
Loading...
Searching...
No Matches
PlantArchitecture.h
Go to the documentation of this file.
1
16#ifndef PLANT_ARCHITECTURE
17#define PLANT_ARCHITECTURE
18
19#include <functional>
20#include <utility>
21#include "Context.h"
22#include "Hungarian.h"
23
24// Constants
25constexpr float C_molecular_wt = 12.01; // g C mol^-1
26
27// forward declarations of classes/structs
30struct Shoot;
31struct Phytomer;
32
35public:
37
41 constval = 0.f;
42 distribution = "constant";
43 generator = nullptr;
44 sampled = false;
45 }
46
48
51 explicit RandomParameter_float(float val) {
52 constval = val;
53 distribution = "constant";
54 generator = nullptr;
55 sampled = false;
56 }
57
59
62 explicit RandomParameter_float(std::minstd_rand0 *rand_generator) {
63 constval = 0.f;
64 distribution = "constant";
65 generator = rand_generator;
66 sampled = false;
67 }
68
69 void initialize(float a_val, std::minstd_rand0 *rand_generator) {
70 constval = a_val;
71 distribution = "constant";
72 generator = rand_generator;
73 sampled = false;
74 }
75
76 void initialize(std::minstd_rand0 *rand_generator) {
77 constval = 1.f;
78 distribution = "constant";
79 generator = rand_generator;
80 sampled = false;
81 }
82
83 RandomParameter_float &operator=(float a) {
84 this->distribution = "constant";
85 this->constval = a;
86 this->sampled = false;
87 return *this;
88 }
89
90 void uniformDistribution(float minval, float maxval) {
91 if (minval > maxval) {
92 throw(std::runtime_error("ERROR (PlantArchitecture): RandomParameter_float::uniformDistribution() - minval must be less than or equal to maxval."));
93 }
94 distribution = "uniform";
95 distribution_parameters = {minval, maxval};
96 sampled = false;
97 }
98
99 void normalDistribution(float mean, float std_dev) {
100 distribution = "normal";
101 distribution_parameters = {mean, std_dev};
102 sampled = false;
103 }
104
105 void weibullDistribution(float shape, float scale) {
106 distribution = "weibull";
107 distribution_parameters = {shape, scale};
108 sampled = false;
109 }
110
111 float val() {
112 if (!sampled) {
113 constval = resample();
114 }
115 return constval;
116 }
117
118 float resample() {
119 sampled = true;
120 if (distribution != "constant") {
121 if (generator == nullptr) {
122 throw(std::runtime_error("ERROR (PlantArchitecture): Random parameter was not properly initialized with random number generator."));
123 }
124 if (distribution == "uniform") {
125 std::uniform_real_distribution<float> unif_distribution;
126 constval = distribution_parameters.at(0) + unif_distribution(*generator) * (distribution_parameters.at(1) - distribution_parameters.at(0));
127 } else if (distribution == "normal") {
128 std::normal_distribution<float> norm_distribution(distribution_parameters.at(0), distribution_parameters.at(1));
129 constval = norm_distribution(*generator);
130 } else if (distribution == "weibull") {
131 std::weibull_distribution<float> wbull_distribution(distribution_parameters.at(0), distribution_parameters.at(1));
132 constval = wbull_distribution(*generator);
133 }
134 }
135 return constval;
136 }
137
138 std::string distribution;
139 std::vector<float> distribution_parameters;
140
141private:
142 bool sampled;
143 float constval;
144
145
146 std::minstd_rand0 *generator;
147};
148
151public:
152 explicit RandomParameter_int() {
153 constval = 1;
154 distribution = "constant";
155 generator = nullptr;
156 sampled = false;
157 }
158
159 void initialize(int a_val, std::minstd_rand0 *rand_generator) {
160 constval = a_val;
161 distribution = "constant";
162 generator = rand_generator;
163 sampled = false;
164 }
165
166 void initialize(std::minstd_rand0 *rand_generator) {
167 constval = 1;
168 distribution = "constant";
169 generator = rand_generator;
170 sampled = false;
171 }
172
173 RandomParameter_int &operator=(int a) {
174 this->distribution = "constant";
175 this->constval = a;
176 this->sampled = false;
177 return *this;
178 }
179
180 void uniformDistribution(int minval, int maxval) {
181 if (minval > maxval) {
182 throw(std::runtime_error("ERROR (PlantArchitecture): RandomParameter_int::uniformDistribution() - minval must be less than or equal to maxval."));
183 }
184 distribution = "uniform";
185 distribution_parameters = {minval, maxval};
186 sampled = false;
187 }
188
189 void discreteValues(const std::vector<int> &values) {
190 distribution = "discretevalues";
191 distribution_parameters = values;
192 sampled = false;
193 }
194
195 int val() {
196 if (!sampled) {
197 constval = resample();
198 }
199 return constval;
200 }
201
202 int resample() {
203 sampled = true;
204 if (distribution != "constant") {
205 if (generator == nullptr) {
206 throw(std::runtime_error("ERROR (PlantArchitecture): Random parameter was not properly initialized with random number generator."));
207 }
208 if (distribution == "uniform") {
209 std::uniform_int_distribution<> unif_distribution(distribution_parameters.at(0), distribution_parameters.at(1));
210 constval = unif_distribution(*generator);
211 } else if (distribution == "discretevalues") {
212 std::uniform_int_distribution<> unif_distribution(0, distribution_parameters.size() - 1);
213 constval = distribution_parameters.at(unif_distribution(*generator));
214 }
215 }
216 return constval;
217 }
218
219 std::string distribution;
220 std::vector<int> distribution_parameters;
221
222private:
223 bool sampled;
224 int constval;
225 std::minstd_rand0 *generator;
226};
227
229public:
230 AxisRotation() {
231 pitch = 0;
232 yaw = 0;
233 roll = 0;
234 azimuth = 0;
235 peduncle_axis = helios::make_vec3(0, 0, 1);
236 }
237
238 AxisRotation(float a_pitch, float a_yaw, float a_roll) {
239 pitch = a_pitch;
240 yaw = a_yaw;
241 roll = a_roll;
242 azimuth = 0;
243 peduncle_axis = helios::make_vec3(0, 0, 1);
244 }
245
246 float pitch;
247 float yaw;
248 float roll;
249 float azimuth; // azimuth rotation for inflorescences (aligns to peduncle orientation)
250 helios::vec3 peduncle_axis; // direction vector of peduncle at attachment point (for compound yaw rotation)
251
252 AxisRotation operator+(const AxisRotation &a) const;
253 AxisRotation operator-(const AxisRotation &a) const;
254
255 friend std::ostream &operator<<(std::ostream &os, const AxisRotation &rot) {
256 return os << "AxisRotation<" << rot.pitch << ", " << rot.yaw << ", " << rot.roll << ">";
257 }
258};
259
260inline AxisRotation make_AxisRotation(float a_pitch, float a_yaw, float a_roll) {
261 return {a_pitch, a_yaw, a_roll};
262}
263
264inline AxisRotation AxisRotation::operator+(const AxisRotation &a) const {
265 return {a.pitch + pitch, a.yaw + yaw, a.roll + roll};
266}
267
268inline AxisRotation AxisRotation::operator-(const AxisRotation &a) const {
269 return {a.pitch - pitch, a.yaw - yaw, a.roll - roll};
270}
271
272enum BudState { BUD_DORMANT = 0, BUD_ACTIVE = 1, BUD_FLOWER_CLOSED = 2, BUD_FLOWER_OPEN = 3, BUD_FRUITING = 4, BUD_DEAD = 5 };
273
275
276 // -- Stem Growth Parameters -- //
278 float stem_density = 54000;
288 float maturity_age = 180;
293
294 // -- Leaf Growth Parameters -- //
296 float SLA = 2.5e-2;
299
300 // -- Flower Growth Parameters -- //
302 float total_flower_cost = 8.33e-4;
303
304 // -- Fruit Growth Parameters -- //
306 float fruit_density = 525000;
309
310 // -- Respiration Parameters -- //
321
322 // -- Organ Abortion Thresholds -- //
331
332 // -- Phyllochron Adjustment Parameters -- //
337
340
341 // -- Carbon Transfer Parameters -- //
344 float carbohydrate_transfer_threshold_up = 0.04;
345 float carbon_conductance_down = 0.75; //<= 1.0
346 float carbon_conductance_up = carbon_conductance_down; // Conductance of carbon from parent to child shoots << conductance from child to parent
347};
348
351
352 // -- Leaf Nitrogen Content (Area Basis) -- //
354 float target_leaf_N_area = 1.5f;
357
358 // -- Allocation Parameters -- //
361
362 // -- Rate Limiting Parameters -- //
365
366 // -- Remobilization Parameters -- //
371
372 // -- Fruit Nitrogen Parameters -- //
374 float fruit_N_area = 1.0f;
375};
376
378
386std::vector<uint> makeTubeFromCones(uint radial_subdivisions, const std::vector<helios::vec3> &vertices, const std::vector<float> &radii, const std::vector<helios::RGBcolor> &colors, helios::Context *context_ptr);
387
389
390 // state of the bud
391 BudState state = BUD_DORMANT;
392 // label of the shoot type that will be produced if the bud breaks into a shoot
393 std::string shoot_type_label;
394 // ID of the shoot that the bud will produce if it breaks into a shoot
395 uint shoot_ID = -1;
396};
397
398struct FloralBud {
399
400 // state of the bud
401 BudState state = BUD_DORMANT;
402 // amount of time since the bud flowered (=0 if it has not yet flowered)
403 float time_counter = 0;
404 //=0 for axillary buds, =1 for terminal buds
405 bool isterminal = false;
406 // For axillary buds: index of the petiole within the internode that this floral bud originates from
407 // For terminal buds: index of the phytomer within the shoot that this floral bud originates from
408 uint parent_index = 0;
409 // Index of the bud within the petiole that this floral bud originates from
410 uint bud_index = 0;
411 // Scaling factor fraction of the fruit (if present), ranging from 0 to 1
412 float current_fruit_scale_factor = 1;
413 float previous_fruit_scale_factor = 0;
414
415
416 helios::vec3 base_position;
417 AxisRotation base_rotation;
418 helios::vec3 bending_axis;
419
420 std::vector<helios::vec3> inflorescence_bases;
421 std::vector<AxisRotation> inflorescence_rotation; // pitch, yaw, roll for each flower/fruit
422 std::vector<float> inflorescence_base_scales; // individual base scale for each flower/fruit (before growth scaling)
423 std::vector<uint> peduncle_objIDs;
424 std::vector<uint> inflorescence_objIDs;
425};
426
428public:
430 explicit LeafPrototype(std::minstd_rand0 *generator);
431
433 LeafPrototype() = default;
434
436 uint (*prototype_function)(helios::Context *, LeafPrototype *prototype_parameters, int compound_leaf_index) = nullptr;
437
439
442 std::string OBJ_model_file;
443
445
448 std::map<int, std::string> leaf_texture_file;
449
450 // Ratio of leaf width to leaf length
451 RandomParameter_float leaf_aspect_ratio;
452
455
456 // Parameters for leaf curvature
461
464
465 // Parameters for leaf wave/wrinkles
470
471 // Parameters for leaf buckling
476
479
482
485
487 bool build_petiolule = false;
488
489 uint unique_prototype_identifier = 0;
490
491 void duplicate(const LeafPrototype &a) {
492 this->leaf_texture_file = a.leaf_texture_file;
493 this->OBJ_model_file = a.OBJ_model_file;
494 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
495 this->midrib_fold_fraction = a.midrib_fold_fraction;
496 this->longitudinal_curvature = a.longitudinal_curvature;
497 this->lateral_curvature = a.lateral_curvature;
498 this->petiole_roll = a.petiole_roll;
499 this->wave_period = a.wave_period;
500 this->wave_amplitude = a.wave_amplitude;
501 this->leaf_buckle_length = a.leaf_buckle_length;
502 this->leaf_buckle_angle = a.leaf_buckle_angle;
503 this->leaf_offset = a.leaf_offset;
504 this->subdivisions = a.subdivisions;
505 this->unique_prototypes = a.unique_prototypes;
506 this->unique_prototype_identifier = a.unique_prototype_identifier;
507 this->build_petiolule = a.build_petiolule;
509 this->generator = a.generator;
510 }
511
514 if (this != &a) {
515 this->leaf_texture_file = a.leaf_texture_file;
516 this->OBJ_model_file = a.OBJ_model_file;
517 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
518 if (a.leaf_aspect_ratio.distribution != "constant")
519 this->leaf_aspect_ratio.resample();
520 this->midrib_fold_fraction = a.midrib_fold_fraction;
521 if (a.midrib_fold_fraction.distribution != "constant")
522 this->midrib_fold_fraction.resample();
523 this->longitudinal_curvature = a.longitudinal_curvature;
524 if (a.longitudinal_curvature.distribution != "constant")
525 this->longitudinal_curvature.resample();
526 this->lateral_curvature = a.lateral_curvature;
527 if (a.lateral_curvature.distribution != "constant")
528 this->lateral_curvature.resample();
529 this->petiole_roll = a.petiole_roll;
530 if (a.petiole_roll.distribution != "constant")
531 this->petiole_roll.resample();
532 this->wave_period = a.wave_period;
533 if (a.wave_period.distribution != "constant")
534 this->wave_period.resample();
535 this->wave_amplitude = a.wave_amplitude;
536 if (a.wave_amplitude.distribution != "constant")
537 this->wave_amplitude.resample();
538 this->leaf_buckle_length = a.leaf_buckle_length;
539 if (a.leaf_buckle_length.distribution != "constant")
540 this->leaf_buckle_length.resample();
541 this->leaf_buckle_angle = a.leaf_buckle_angle;
542 if (a.leaf_buckle_angle.distribution != "constant")
543 this->leaf_buckle_angle.resample();
544 this->leaf_offset = a.leaf_offset;
545 this->subdivisions = a.subdivisions;
546 this->unique_prototypes = a.unique_prototypes;
547 this->unique_prototype_identifier = a.unique_prototype_identifier;
548 this->build_petiolule = a.build_petiolule;
550 this->generator = a.generator;
551 if (this->generator != nullptr) {
552 this->sampleIdentifier();
553 }
554 }
555 return *this;
556 }
557
558 void sampleIdentifier() {
559 assert(generator != nullptr);
560 std::uniform_int_distribution<uint> unif_distribution;
561 this->unique_prototype_identifier = unif_distribution(*generator);
562 }
563
564private:
565 std::minstd_rand0 *generator{};
566};
567
569private:
570 struct InternodeParameters {
571
575 RandomParameter_float phyllotactic_angle;
577 RandomParameter_float radius_initial;
579 RandomParameter_int max_vegetative_buds_per_petiole;
581 RandomParameter_int max_floral_buds_per_petiole;
583 helios::RGBcolor color;
585 std::string image_texture;
587 uint length_segments;
589 uint radial_subdivisions;
590
591 InternodeParameters &operator=(const InternodeParameters &a) {
592 if (this != &a) {
593 this->pitch = a.pitch;
594 if (a.pitch.distribution != "constant")
595 this->pitch.resample();
596 this->phyllotactic_angle = a.phyllotactic_angle;
597 if (a.phyllotactic_angle.distribution != "constant")
598 this->phyllotactic_angle.resample();
599 this->radius_initial = a.radius_initial;
600 if (a.radius_initial.distribution != "constant")
601 this->radius_initial.resample();
602 this->max_vegetative_buds_per_petiole = a.max_vegetative_buds_per_petiole;
603 if (a.max_vegetative_buds_per_petiole.distribution != "constant")
604 this->max_vegetative_buds_per_petiole.resample();
605 this->max_floral_buds_per_petiole = a.max_floral_buds_per_petiole;
606 if (a.max_floral_buds_per_petiole.distribution != "constant")
607 this->max_floral_buds_per_petiole.resample();
608 this->color = a.color;
609 this->image_texture = a.image_texture;
610 this->length_segments = a.length_segments;
611 this->radial_subdivisions = a.radial_subdivisions;
612 }
613 return *this;
614 }
615 };
616
617 struct PetioleParameters {
618
620 uint petioles_per_internode;
628 RandomParameter_float curvature;
632 helios::RGBcolor color;
634 uint length_segments;
636 uint radial_subdivisions;
637
638 PetioleParameters &operator=(const PetioleParameters &a) {
639 if (this != &a) {
640 this->petioles_per_internode = a.petioles_per_internode;
641 this->pitch = a.pitch;
642 if (a.pitch.distribution != "constant")
643 this->pitch.resample();
644 this->radius = a.radius;
645 if (a.radius.distribution != "constant")
646 this->radius.resample();
647 this->length = a.length;
648 if (a.length.distribution != "constant")
649 this->length.resample();
650 this->curvature = a.curvature;
651 if (a.curvature.distribution != "constant")
652 this->curvature.resample();
653 this->taper = a.taper;
654 if (a.taper.distribution != "constant")
655 this->taper.resample();
656 this->color = a.color;
657 this->length_segments = a.length_segments;
658 this->radial_subdivisions = a.radial_subdivisions;
659 }
660 return *this;
661 }
662 };
663
664 struct LeafParameters {
666 RandomParameter_int leaves_per_petiole;
674 RandomParameter_float leaflet_offset;
676 RandomParameter_float leaflet_scale;
678 RandomParameter_float prototype_scale;
680 LeafPrototype prototype;
681
682 LeafParameters &operator=(const LeafParameters &a) {
683 if (this != &a) {
684 this->leaves_per_petiole = a.leaves_per_petiole;
685 if (a.leaves_per_petiole.distribution != "constant")
686 this->leaves_per_petiole.resample();
687 this->pitch = a.pitch;
688 if (a.pitch.distribution != "constant")
689 this->pitch.resample();
690 this->yaw = a.yaw;
691 if (a.yaw.distribution != "constant")
692 this->yaw.resample();
693 this->roll = a.roll;
694 if (a.roll.distribution != "constant")
695 this->roll.resample();
696 this->leaflet_offset = a.leaflet_offset;
697 if (a.leaflet_offset.distribution != "constant")
698 this->leaflet_offset.resample();
699 this->leaflet_scale = a.leaflet_scale;
700 if (a.leaflet_scale.distribution != "constant")
701 this->leaflet_scale.resample();
702 this->prototype_scale = a.prototype_scale;
703 if (a.prototype_scale.distribution != "constant")
704 this->prototype_scale.resample();
705 this->prototype.duplicate(a.prototype);
706 }
707 return *this;
708 }
709 };
710
711 struct PeduncleParameters {
721 RandomParameter_float curvature;
723 helios::RGBcolor color;
725 uint length_segments;
727 uint radial_subdivisions;
728
729 PeduncleParameters &operator=(const PeduncleParameters &a) {
730 if (this != &a) {
731 this->length = a.length;
732 if (a.length.distribution != "constant")
733 this->length.resample();
734 this->radius = a.radius;
735 if (a.radius.distribution != "constant")
736 this->radius.resample();
737 this->pitch = a.pitch;
738 if (a.pitch.distribution != "constant")
739 this->pitch.resample();
740 this->roll = a.roll;
741 if (a.roll.distribution != "constant")
742 this->roll.resample();
743 this->curvature = a.curvature;
744 if (a.curvature.distribution != "constant")
745 this->curvature.resample();
746 this->color = a.color;
747 this->length_segments = a.length_segments;
748 this->radial_subdivisions = a.radial_subdivisions;
749 }
750 return *this;
751 }
752 };
753
754 struct InflorescenceParameters {
755
757 RandomParameter_int flowers_per_peduncle;
759 RandomParameter_float flower_offset;
765 RandomParameter_float flower_prototype_scale;
767 uint (*flower_prototype_function)(helios::Context *, uint subdivisions, bool flower_is_open) = nullptr;
769 RandomParameter_float fruit_prototype_scale;
771 uint (*fruit_prototype_function)(helios::Context *, uint subdivisions) = nullptr;
773 RandomParameter_float fruit_gravity_factor_fraction;
775 uint unique_prototypes;
776
777 InflorescenceParameters &operator=(const InflorescenceParameters &a) {
778 if (this != &a) {
779 this->flowers_per_peduncle = a.flowers_per_peduncle;
780 this->flowers_per_peduncle.resample();
781 this->flower_offset = a.flower_offset;
782 this->flower_offset.resample();
783 this->pitch = a.pitch;
784 this->pitch.resample();
785 this->roll = a.roll;
786 this->roll.resample();
787 this->flower_prototype_scale = a.flower_prototype_scale;
788 this->flower_prototype_scale.resample();
789 this->flower_prototype_function = a.flower_prototype_function;
790 this->fruit_prototype_scale = a.fruit_prototype_scale;
791 this->fruit_prototype_scale.resample();
792 this->fruit_prototype_function = a.fruit_prototype_function;
793 this->fruit_gravity_factor_fraction = a.fruit_gravity_factor_fraction;
794 this->fruit_gravity_factor_fraction.resample();
795 this->unique_prototypes = a.unique_prototypes;
796 }
797 return *this;
798 }
799 };
800
801public:
809 InternodeParameters internode;
810
817 PetioleParameters petiole;
818
826 LeafParameters leaf;
827
834 PeduncleParameters peduncle;
835
844 InflorescenceParameters inflorescence;
845
846 // Custom user-defined function that is called when a phytomer is created
854 void (*phytomer_creation_function)(std::shared_ptr<Phytomer> phytomer_ptr, uint shoot_node_index, uint parent_shoot_node_index, uint shoot_max_nodes, float plant_age) = nullptr;
855
856 // Custom user-defined function that is called for each phytomer on every time step
860 void (*phytomer_callback_function)(std::shared_ptr<Phytomer> phytomer_ptr) = nullptr;
861
862
865
867 explicit PhytomerParameters(std::minstd_rand0 *generator);
868
869 friend class PlantArchitecture;
870 friend struct Phytomer;
871 friend struct Shoot;
872};
873
875
878
880 explicit ShootParameters(std::minstd_rand0 *generator);
881
888
889 // ---- Geometric Parameters ---- //
890
916
917 // --- Growth Parameters --- //
918
926
943
944 // ---- Custom Functions ---- //
945
954 void defineChildShootTypes(const std::vector<std::string> &child_shoot_type_labels, const std::vector<float> &child_shoot_type_probabilities);
955
956 ShootParameters &operator=(const ShootParameters &a) {
957 this->phytomer_parameters = a.phytomer_parameters;
958 this->max_nodes = a.max_nodes;
959 this->max_nodes.resample();
960 this->max_nodes_per_season = a.max_nodes_per_season;
961 this->max_nodes_per_season.resample();
962 this->phyllochron_min = a.phyllochron_min;
963 this->phyllochron_min.resample();
964 this->girth_area_factor = a.girth_area_factor;
965 this->girth_area_factor.resample();
966 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
967 this->vegetative_bud_break_probability_min.resample();
968 this->vegetative_bud_break_probability_max = a.vegetative_bud_break_probability_max;
969 this->vegetative_bud_break_probability_max.resample();
970 this->flower_bud_break_probability = a.flower_bud_break_probability;
971 this->flower_bud_break_probability.resample();
972 this->fruit_set_probability = a.fruit_set_probability;
973 this->fruit_set_probability.resample();
974 this->gravitropic_curvature = a.gravitropic_curvature;
975 this->gravitropic_curvature.resample();
976 this->tortuosity = a.tortuosity;
977 this->tortuosity.resample();
978 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
979 this->vegetative_bud_break_probability_min.resample();
980 this->vegetative_bud_break_probability_decay_rate = a.vegetative_bud_break_probability_decay_rate;
981 this->vegetative_bud_break_probability_decay_rate.resample();
982 this->max_terminal_floral_buds = a.max_terminal_floral_buds;
983 this->max_terminal_floral_buds.resample();
984 this->flower_bud_break_probability = a.flower_bud_break_probability;
985 this->flower_bud_break_probability.resample();
986 this->fruit_set_probability = a.fruit_set_probability;
987 this->fruit_set_probability.resample();
988 this->vegetative_bud_break_time = a.vegetative_bud_break_time;
989 this->vegetative_bud_break_time.resample();
990 this->insertion_angle_tip = a.insertion_angle_tip;
991 this->insertion_angle_tip.resample();
992 this->insertion_angle_decay_rate = a.insertion_angle_decay_rate;
993 this->insertion_angle_decay_rate.resample();
994 this->internode_length_max = a.internode_length_max;
995 this->internode_length_max.resample();
996 this->internode_length_min = a.internode_length_min;
997 this->internode_length_min.resample();
998 this->internode_length_decay_rate = a.internode_length_decay_rate;
999 this->internode_length_decay_rate.resample();
1000 this->base_roll = a.base_roll;
1001 this->base_roll.resample();
1002 this->base_yaw = a.base_yaw;
1003 this->base_yaw.resample();
1004 this->flowers_require_dormancy = a.flowers_require_dormancy;
1005 this->growth_requires_dormancy = a.growth_requires_dormancy;
1006 this->child_shoot_type_labels = a.child_shoot_type_labels;
1007 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1008 this->determinate_shoot_growth = a.determinate_shoot_growth;
1009 this->child_shoot_type_labels = a.child_shoot_type_labels;
1010 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1011 return *this;
1012 }
1013
1014 friend class PlantArchitecture;
1015 friend struct Shoot;
1016
1017protected:
1018 std::vector<std::string> child_shoot_type_labels;
1019 std::vector<float> child_shoot_type_probabilities;
1020};
1021
1022struct Phytomer {
1023public:
1025 Phytomer(const PhytomerParameters &params, Shoot *parent_shoot, uint phytomer_index, const helios::vec3 &parent_internode_axis, const helios::vec3 &parent_petiole_axis, helios::vec3 internode_base_origin, const AxisRotation &shoot_base_rotation,
1026 float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, uint rank, PlantArchitecture *plantarchitecture_ptr, helios::Context *context_ptr);
1027
1028 // ---- query info about the phytomer ---- //
1029
1035 [[nodiscard]] std::vector<helios::vec3> getInternodeNodePositions() const;
1036
1042 [[nodiscard]] std::vector<float> getInternodeNodeRadii() const;
1043
1050 [[nodiscard]] float calculatePhytomerVolume(uint node_number) const;
1051
1058 [[nodiscard]] helios::vec3 getInternodeAxisVector(float stem_fraction) const;
1059
1067 [[nodiscard]] helios::vec3 getPetioleAxisVector(float stem_fraction, uint petiole_index) const;
1068
1077 [[nodiscard]] helios::vec3 getPeduncleAxisVector(float stem_fraction, uint petiole_index, uint bud_index) const;
1078
1086 [[nodiscard]] static helios::vec3 getAxisVector(float stem_fraction, const std::vector<helios::vec3> &axis_vertices);
1087
1093 [[nodiscard]] float getInternodeLength() const;
1094
1100 [[nodiscard]] float getInternodeRadius() const;
1101
1107 [[nodiscard]] float getPetioleLength() const;
1108
1115 [[nodiscard]] float getInternodeRadius(float stem_fraction) const;
1116
1122 [[nodiscard]] float getLeafArea() const;
1123
1131 [[nodiscard]] helios::vec3 getLeafBasePosition(uint petiole_index, uint leaf_index) const;
1132
1138 [[nodiscard]] bool hasLeaf() const;
1139
1145 [[nodiscard]] float calculateDownstreamLeafArea() const;
1146
1147 // ---- modify the phytomer ---- //
1148
1150
1154 void setInternodeLengthScaleFraction(float internode_scale_factor_fraction, bool update_context_geometry);
1155
1157
1160 void scaleInternodeMaxLength(float scale_factor);
1161
1163
1166 void setInternodeMaxLength(float internode_length_max_new);
1167
1169
1172 void setInternodeMaxRadius(float internode_radius_max_new);
1173
1175
1179 void setLeafScaleFraction(uint petiole_index, float leaf_scale_factor_fraction);
1180
1182
1185 void setLeafScaleFraction(float leaf_scale_factor_fraction);
1186
1188
1192 void setLeafPrototypeScale(uint petiole_index, float leaf_prototype_scale);
1193
1195
1198 void setLeafPrototypeScale(float leaf_prototype_scale);
1199
1201
1206 void scaleLeafPrototypeScale(uint petiole_index, float scale_factor);
1207
1209
1212 void scaleLeafPrototypeScale(float scale_factor);
1213
1215
1222 void scalePetioleGeometry(uint petiole_index, float target_length, float target_base_radius);
1223
1230 void setInflorescenceScaleFraction(FloralBud &fbud, float inflorescence_scale_factor_fraction) const;
1231
1240 void setPetioleBase(const helios::vec3 &base_position);
1241
1252 void rotateLeaf(uint petiole_index, uint leaf_index, const AxisRotation &rotation);
1253
1259 void setVegetativeBudState(BudState state);
1260
1268 void setVegetativeBudState(BudState state, uint petiole_index, uint bud_index);
1269
1276 static void setVegetativeBudState(BudState state, VegetativeBud &vbud);
1277
1283 void setFloralBudState(BudState state);
1284
1292 void setFloralBudState(BudState state, uint petiole_index, uint bud_index);
1293
1300 void setFloralBudState(BudState state, FloralBud &fbud);
1301
1309 void removeLeaf();
1310
1318 void deletePhytomer();
1319
1320private:
1332 helios::vec3 calculateCollisionAvoidanceDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &collision_detection_active) const;
1333
1345 bool applySolidObstacleAvoidance(const helios::vec3 &current_position, helios::vec3 &internode_axis) const;
1346
1359 helios::vec3 calculateAttractionPointDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &attraction_active) const;
1360
1373 helios::vec3 calculatePetioleCollisionAvoidanceDirection(const helios::vec3 &petiole_base_origin, const helios::vec3 &proposed_petiole_axis, bool &collision_detection_active) const;
1374
1376
1382 helios::vec3 calculateFruitCollisionAvoidanceDirection(const helios::vec3 &fruit_base_origin, const helios::vec3 &proposed_fruit_axis, bool &collision_detection_active) const;
1383
1384
1385public:
1386 // ---- phytomer data ---- //
1387
1389 std::vector<std::vector<helios::vec3>> petiole_vertices; // first index is petiole within internode, second index is tube segment within petiole tube
1390 std::vector<std::vector<helios::vec3>> leaf_bases; // first index is petiole within internode, second index is leaf within petiole
1391 std::vector<std::vector<std::vector<helios::vec3>>> peduncle_vertices; // first index is petiole within internode, second index is floral bud within petiole, third index is tube segment within peduncle
1392 std::vector<std::vector<std::vector<float>>> peduncle_radii; // first index is petiole within internode, second index is floral bud within petiole, third index is tube segment within peduncle
1393 std::vector<std::vector<float>> peduncle_length; // actual sampled length for each peduncle - first index is petiole, second is bud
1394 std::vector<std::vector<float>> peduncle_radius; // actual sampled radius for each peduncle - first index is petiole, second is bud
1395 std::vector<std::vector<float>> peduncle_pitch; // actual sampled pitch for each peduncle - first index is petiole, second is bud
1396 std::vector<std::vector<float>> peduncle_curvature; // actual sampled curvature for each peduncle - first index is petiole, second is bud
1397 float internode_pitch, internode_phyllotactic_angle;
1398
1399 std::vector<std::vector<float>> petiole_radii; // first index is petiole within internode, second index is segment within petiole tube
1400 std::vector<float> petiole_length; // index is petiole within internode
1401 std::vector<float> petiole_pitch; // index is petiole within internode
1402 std::vector<float> petiole_curvature; // index is petiole within internode
1403 std::vector<float> petiole_taper; // taper value for each petiole (tip_radius/base_radius ratio)
1404 std::vector<helios::vec3> petiole_axis_initial; // initial petiole axis (before curvature) for each petiole
1405 std::vector<helios::vec3> petiole_rotation_axis; // rotation axis for curvature application for each petiole
1406 std::vector<std::vector<float>> leaf_size_max; // first index is petiole within internode, second index is leaf within petiole
1407 std::vector<std::vector<AxisRotation>> leaf_rotation; // first index is petiole within internode, second index is leaf within petiole
1408
1409 std::vector<helios::RGBcolor> internode_colors; // index is segment within internode tube
1410 std::vector<helios::RGBcolor> petiole_colors; // index is segment within petiole tube
1411
1412 std::vector<std::vector<uint>> petiole_objIDs; // first index is petiole within internode, second index is segment within petiole tube
1413 std::vector<std::vector<uint>> leaf_objIDs; // first index is petiole within internode, second index is leaf within petiole tube
1414
1415 PhytomerParameters phytomer_parameters;
1416
1417 uint rank;
1420
1421 uint plantID;
1422 uint parent_shoot_ID;
1423 Shoot *parent_shoot_ptr;
1424
1426 float age = 0;
1427 bool isdormant = false;
1428
1429 float current_internode_scale_factor = 1;
1430 std::vector<float> current_leaf_scale_factor; // index is petiole within internode
1431
1432 float old_phytomer_volume = 0;
1433
1434 float downstream_leaf_area = 0;
1435
1436 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds; // first index is petiole within internode, second index is bud within petiole
1437 std::vector<std::vector<FloralBud>> floral_buds; // first index is petiole within internode, second index is bud within petiole
1438
1439 float internode_radius_initial;
1440 float internode_radius_max;
1441 float internode_length_max;
1442
1444 std::vector<float> internode_yaw_perturbations;
1445
1446 bool build_context_geometry_petiole = true;
1447 bool build_context_geometry_peduncle = true;
1448
1449protected:
1450 helios::vec3 inflorescence_bending_axis;
1451
1452 helios::Context *context_ptr;
1453
1454 PlantArchitecture *plantarchitecture_ptr;
1455
1456 void updateInflorescence(FloralBud &fbud);
1457
1474 void createInflorescenceGeometry(FloralBud &fbud, const helios::vec3 &fruit_base, const helios::vec3 &peduncle_axis, float pitch, float roll, float azimuth, float yaw_compound, float scale_factor, bool is_open_flower);
1475
1481 [[nodiscard]] float calculatePhytomerConstructionCosts() const;
1482
1490 [[nodiscard]] float calculateFlowerConstructionCosts(const FloralBud &fbud) const;
1491
1497 [[nodiscard]] float calculateFruitConstructionCosts(const FloralBud &fbud) const;
1498
1499 friend struct Shoot;
1500 friend class PlantArchitecture;
1501};
1502
1503struct Shoot {
1504
1506 Shoot(uint plant_ID, int shoot_ID, int parent_shoot_ID, uint parent_node, uint parent_petiole_index, uint rank, const helios::vec3 &shoot_base_position, const AxisRotation &shoot_base_rotation, uint current_node_number,
1507 float internode_length_shoot_initial, ShootParameters &shoot_params, std::string shoot_type_label, PlantArchitecture *plant_architecture_ptr);
1508
1510
1517 void buildShootPhytomers(float internode_radius, float internode_length, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper);
1518
1520
1527 int appendPhytomer(float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, const PhytomerParameters &phytomer_parameters);
1528
1530
1533 [[nodiscard]] std::string sampleChildShootType() const;
1534
1536
1540 [[nodiscard]] bool sampleVegetativeBudBreak(uint node_index) const;
1541
1542 [[nodiscard]] bool sampleVegetativeBudBreak_carb(uint node_index) const;
1543
1545
1550 uint sampleEpicormicShoot(float dt, std::vector<float> &epicormic_positions_fraction) const;
1551
1556 void terminateApicalBud();
1557
1563
1571 void addTerminalFloralBud();
1572
1579 [[nodiscard]] float calculateShootInternodeVolume() const;
1580
1586 [[nodiscard]] float calculateShootLength() const;
1587
1594 [[nodiscard]] helios::vec3 getShootAxisVector(float shoot_fraction) const;
1595
1603 [[nodiscard]] float sumShootLeafArea(uint start_node_index = 0) const;
1604
1611 [[nodiscard]] float sumChildVolume(uint start_node_index = 0) const;
1612
1620 void propagateDownstreamLeafArea(const Shoot *shoot, uint node_index, float leaf_area);
1621
1626 void updateShootNodes(bool update_context_geometry = true);
1627
1628 uint current_node_number = 0;
1629 uint nodes_this_season = 0;
1630
1631 helios::vec3 base_position;
1632 AxisRotation base_rotation;
1633 helios::vec3 radial_outward_axis;
1634
1635 const int ID;
1636 const int parent_shoot_ID;
1637 const uint plantID;
1638 const uint parent_node_index;
1639 const uint rank;
1640 const uint parent_petiole_index;
1641
1642 float carbohydrate_pool_molC = 0; // mol C
1643
1645 std::map<uint, float> leaf_nitrogen_gN_m2;
1646
1647 float old_shoot_volume = 0;
1648
1649 float phyllochron_increase = 5;
1650 float phyllochron_recovery = phyllochron_increase;
1651
1652 float days_with_negative_carbon_balance = 0;
1653
1654 void breakDormancy();
1655 void makeDormant();
1656
1657 bool isdormant;
1658 uint dormancy_cycles = 0;
1659
1660 bool meristem_is_alive = true;
1661
1662 float phyllochron_counter = 0;
1663 float phyllochron_min = 6.f;
1664 float elongation_max = 0.25;
1665
1666 float curvature_perturbation = 0;
1667 float yaw_perturbation = 0;
1668
1669 float gravitropic_curvature = 0;
1670
1671 const float internode_length_max_shoot_initial;
1672
1673 uint internode_tube_objID = 4294967294;
1674
1675 std::vector<std::vector<helios::vec3>> shoot_internode_vertices; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1676 std::vector<std::vector<float>> shoot_internode_radii; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1677
1678 bool build_context_geometry_internode = true;
1679
1680 // map of node number (key) to IDs of shoot children (value)
1681 std::map<int, std::vector<int>> childIDs;
1682
1683 ShootParameters shoot_parameters;
1684
1685 std::string shoot_type_label;
1686
1687 float phyllochron_instantaneous;
1688 float elongation_rate_instantaneous;
1689
1690 std::vector<std::shared_ptr<Phytomer>> phytomers;
1691
1692 PlantArchitecture *plantarchitecture_ptr;
1693
1694 helios::Context *context_ptr;
1695};
1696
1698
1699 PlantInstance(const helios::vec3 &a_base_position, float a_current_age, const std::string &a_plant_name, helios::Context *a_context_ptr) :
1700 base_position(a_base_position), current_age(a_current_age), plant_name(a_plant_name), context_ptr(a_context_ptr) {
1701 }
1702 std::vector<std::shared_ptr<Shoot>> shoot_tree;
1703 helios::vec3 base_position;
1704 float current_age;
1705 float time_since_dormancy = 0;
1706 helios::Context *context_ptr;
1707 std::string plant_name;
1708 std::pair<std::string, float> epicormic_shoot_probability_perlength_per_day; //.first is the epicormic shoot label string, .second is the probability
1709
1710 // Phenological thresholds
1711 float dd_to_dormancy_break = 0;
1712 float dd_to_flower_initiation = 0;
1713 float dd_to_flower_opening = 0;
1714 float dd_to_fruit_set = 0;
1715 float dd_to_fruit_maturity = 0;
1716 float dd_to_dormancy = 0;
1717 float max_leaf_lifespan = 1e6;
1718 bool is_evergreen = false;
1719
1720 float max_age = 999;
1721
1722 CarbohydrateParameters carb_parameters;
1723
1724 // --- Nitrogen Model --- //
1725
1728
1735
1738 std::map<std::string, ShootParameters> shoot_types_snapshot;
1739
1740 // --- Per-plant Attraction Points --- //
1741
1744
1746 std::vector<helios::vec3> attraction_points;
1747
1749 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
1750
1753
1755 float attraction_weight = 0.6f;
1756
1759};
1760
1762public:
1764
1767 explicit PlantArchitecture(helios::Context *context_ptr);
1768
1771
1773 static int selfTest(int argc, char **argv);
1774
1776
1779 void optionalOutputObjectData(const std::string &object_data_label);
1780
1782
1785 void optionalOutputObjectData(const std::vector<std::string> &object_data_labels);
1786
1787 // ********* Methods for Building Plants from Existing Library ********* //
1788
1790
1793 void loadPlantModelFromLibrary(const std::string &plant_label);
1794
1796 [[nodiscard]] std::vector<std::string> getAvailablePlantModels() const;
1797
1799
1806 uint buildPlantInstanceFromLibrary(const helios::vec3 &base_position, float age, const std::map<std::string, float> &build_parameters = {});
1807
1809
1819 std::vector<uint> buildPlantCanopyFromLibrary(const helios::vec3 &canopy_center_position, const helios::vec2 &plant_spacing_xy, const helios::int2 &plant_count_xy, float age, float germination_rate = 1.f,
1820 const std::map<std::string, float> &build_parameters = {});
1821
1823
1832 std::vector<uint> buildPlantCanopyFromLibrary(const helios::vec3 &canopy_center_position, const helios::vec2 &canopy_extent_xy, uint plant_count, float age, const std::map<std::string, float> &build_parameters = {});
1833
1835
1839 ShootParameters getCurrentShootParameters(const std::string &shoot_type_label);
1840
1842
1845 std::map<std::string, ShootParameters> getCurrentShootParameters();
1846
1848
1852 std::map<std::string, PhytomerParameters> getCurrentPhytomerParameters();
1853
1855
1860 [[nodiscard]] std::vector<std::string> listShootTypeLabels() const;
1861
1863
1868 [[nodiscard]] std::vector<std::string> listShootTypeLabels(const std::string &plant_model_name);
1869
1871
1876 [[nodiscard]] std::vector<std::string> listShootTypeLabels(uint plantID) const;
1877
1879
1884 void updateCurrentShootParameters(const std::string &shoot_type_label, const ShootParameters &params);
1885
1887
1891 void updateCurrentShootParameters(const std::map<std::string, ShootParameters> &params);
1892
1893 // ********* Methods for Building Custom Plant Geometry from Scratch ********* //
1894
1896
1901 uint addPlantInstance(const helios::vec3 &base_position, float current_age);
1902
1904
1911 uint duplicatePlantInstance(uint plantID, const helios::vec3 &base_position, const AxisRotation &base_rotation, float current_age);
1912
1914
1917 void deletePlantInstance(uint plantID);
1918
1920
1923 void deletePlantInstance(const std::vector<uint> &plantIDs);
1924
1926
1938 void setPlantPhenologicalThresholds(uint plantID, float time_to_dormancy_break, float time_to_flower_initiation, float time_to_flower_opening, float time_to_fruit_set, float time_to_fruit_maturity, float time_to_dormancy,
1939 float max_leaf_lifespan = 1e6, bool is_evergreen = false);
1940
1942
1946 void setPlantCarbohydrateModelParameters(uint plantID, const CarbohydrateParameters &carb_parameters);
1947
1949
1953 void setPlantCarbohydrateModelParameters(const std::vector<uint> &plantIDs, const CarbohydrateParameters &carb_parameters);
1954
1960 void disablePlantPhenology(uint plantID);
1961
1963
1966 void advanceTime(float time_step_days);
1967
1969
1973 void advanceTime(int time_step_years, float time_step_days);
1974
1976
1980 void advanceTime(uint plantID, float time_step_days);
1981
1983
1987 void advanceTime(const std::vector<uint> &plantIDs, float time_step_days);
1988
1990
1998
2000
2008
2009 // -- plant building methods -- //
2010
2012
2016 void defineShootType(const std::string &shoot_type_label, const ShootParameters &shoot_params);
2017
2019
2031 uint addBaseStemShoot(uint plantID, uint current_node_number, const AxisRotation &base_rotation, float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction,
2032 float radius_taper, const std::string &shoot_type_label);
2033
2035
2048 uint appendShoot(uint plantID, int parent_shoot_ID, uint current_node_number, const AxisRotation &base_rotation, float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction,
2049 float radius_taper, const std::string &shoot_type_label);
2050
2052
2067 uint addChildShoot(uint plantID, int parent_shoot_ID, uint parent_node_index, uint current_node_number, const AxisRotation &shoot_base_rotation, float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction,
2068 float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label, uint petiole_index = 0);
2069
2071
2085 uint addEpicormicShoot(uint plantID, int parent_shoot_ID, float parent_position_fraction, uint current_node_number, float zenith_perturbation_degrees, float internode_radius, float internode_length_max,
2086 float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label);
2087
2089
2099 int appendPhytomerToShoot(uint plantID, uint shootID, const PhytomerParameters &phytomer_parameters, float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction);
2100
2102
2108 void enableEpicormicChildShoots(uint plantID, const std::string &epicormic_shoot_type_label, float epicormic_probability_perlength_perday);
2109
2112
2115
2118
2120
2123 void enableGroundClipping(float ground_height = 0.f);
2124
2125 // -- collision detection methods -- //
2126
2128
2134 void enableSoftCollisionAvoidance(const std::vector<uint> &target_object_UUIDs = {}, const std::vector<uint> &target_object_IDs = {}, bool enable_petiole_collision = false, bool enable_fruit_collision = false);
2135
2138
2140
2146 void setSoftCollisionAvoidanceParameters(float view_half_angle_deg, float look_ahead_distance, int sample_count, float inertia_weight);
2147
2149
2152 void setStaticObstacles(const std::vector<uint> &target_UUIDs);
2153
2155
2158 [[nodiscard]] CollisionDetection *getCollisionDetection() const;
2159
2161
2168 void setCollisionRelevantOrgans(bool include_internodes, bool include_leaves, bool include_petioles, bool include_flowers, bool include_fruit);
2169
2171
2183 void enableSolidObstacleAvoidance(const std::vector<uint> &obstacle_UUIDs, float avoidance_distance = 0.5f, bool enable_fruit_adjustment = false, bool enable_obstacle_pruning = false);
2184
2186
2190 void setGeometryUpdateScheduling(int update_frequency = 3, bool force_update_on_collision = true);
2191
2192 // -- attraction points methods -- //
2193
2195
2201 void enableAttractionPoints(const std::vector<helios::vec3> &attraction_points, float view_half_angle_deg = 45.0f, float look_ahead_distance = 0.1f, float attraction_weight = 0.6f);
2202
2205
2207
2210 void updateAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2211
2213
2216 void appendAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2217
2219
2225 void setAttractionParameters(float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2226
2228
2235 void enableAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points, float view_half_angle_deg = 80.0f, float look_ahead_distance = 0.1f, float attraction_weight = 0.6f);
2236
2238
2241 void disableAttractionPoints(uint plantID);
2242
2244
2248 void updateAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2249
2251
2255 void appendAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2256
2258
2265 void setAttractionParameters(uint plantID, float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2266
2281 bool detectAttractionPointsInCone(const helios::vec3 &vertex, const helios::vec3 &look_direction, float look_ahead_distance, float half_angle_degrees, helios::vec3 &direction_to_closest) const;
2282
2284 bool detectAttractionPointsInCone(const std::vector<helios::vec3> &attraction_points, const helios::vec3 &vertex, const helios::vec3 &look_direction, float look_ahead_distance, float half_angle_degrees, helios::vec3 &direction_to_closest) const;
2285
2286 // -- methods for modifying the current plant state -- //
2287
2292 void initializeCarbohydratePool(float carbohydrate_concentration_molC_m3) const;
2293
2300 void initializePlantCarbohydratePool(uint plantID, float carbohydrate_concentration_molC_m3);
2301
2310 void initializeShootCarbohydratePool(uint plantID, uint shootID, float carbohydrate_concentration_molC_m3);
2311
2320 void setPhytomerLeafScale(uint plantID, uint shootID, uint node_number, float leaf_scale_factor_fraction);
2321
2327 void setPlantBasePosition(uint plantID, const helios::vec3 &base_position);
2328
2341 void setPlantLeafElevationAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination) const;
2342
2355 void setPlantLeafElevationAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination) const;
2356
2369 void setPlantLeafAzimuthAngleDistribution(uint plantID, float eccentricity, float ellipse_rotation_degrees) const;
2370
2383 void setPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, float eccentricity, float ellipse_rotation_degrees) const;
2384
2399 void setPlantLeafAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2400
2415 void setPlantLeafAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2416
2418 void setPlantAge(uint plantID, float current_age);
2419
2425 void harvestPlant(uint plantID);
2426
2433 void removeShootLeaves(uint plantID, uint shootID);
2434
2441 void removeShootVegetativeBuds(uint plantID, uint shootID);
2442
2449 void removeShootFloralBuds(uint plantID, uint shootID);
2450
2456 void removePlantLeaves(uint plantID);
2457
2463 void makePlantDormant(uint plantID);
2464
2470 void breakPlantDormancy(uint plantID);
2471
2479 void pruneBranch(uint plantID, uint shootID, uint node_index);
2480
2481 // -- methods for querying information about the plant -- //
2482
2489 [[nodiscard]] std::string getPlantName(uint plantID) const;
2490
2497 [[nodiscard]] float getPlantAge(uint plantID) const;
2498
2506 [[nodiscard]] uint getShootNodeCount(uint plantID, uint shootID) const;
2507
2515 [[nodiscard]] float getShootTaper(uint plantID, uint shootID) const;
2516
2523 [[nodiscard]] helios::vec3 getPlantBasePosition(uint plantID) const;
2524
2531 [[nodiscard]] std::vector<helios::vec3> getPlantBasePosition(const std::vector<uint> &plantIDs) const;
2532
2534
2538 [[nodiscard]] float sumPlantLeafArea(uint plantID) const;
2539
2541
2545 [[nodiscard]] float getPlantStemHeight(uint plantID) const;
2546
2548
2552 [[nodiscard]] float getPlantHeight(uint plantID) const;
2553
2555
2561 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2562
2564
2570 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2571
2573
2579 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2580
2582
2588 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2589
2591
2595 [[nodiscard]] uint getPlantLeafCount(uint plantID) const;
2596
2598
2602 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(uint plantID) const;
2603
2605
2609 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(const std::vector<uint> &plantIDs) const;
2610
2612
2616 [[nodiscard]] bool isPlantDormant(uint plantID) const;
2617
2619
2623 [[nodiscard]] std::string determinePhenologyStage(uint plantID) const;
2624
2626
2630 void writePlantMeshVertices(uint plantID, const std::string &filename) const;
2631
2633
2636 [[nodiscard]] std::vector<uint> getAllPlantIDs() const;
2637
2639
2643 [[nodiscard]] std::vector<uint> getAllPlantObjectIDs(uint plantID) const;
2644
2646
2650 [[nodiscard]] std::vector<uint> getAllPlantUUIDs(uint plantID) const;
2651
2653
2657 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID) const;
2658
2660
2666 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID, const std::string &shoot_type_label) const;
2667
2669
2673 [[nodiscard]] std::vector<uint> getPlantPetioleObjectIDs(uint plantID) const;
2674
2676
2680 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(uint plantID) const;
2681
2683
2687 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(const std::vector<uint> &plantIDs) const;
2688
2690
2694 [[nodiscard]] std::vector<uint> getPlantPeduncleObjectIDs(uint plantID) const;
2695
2697
2701 [[nodiscard]] std::vector<uint> getPlantFlowerObjectIDs(uint plantID) const;
2702
2704
2708 [[nodiscard]] std::vector<uint> getPlantFruitObjectIDs(uint plantID) const;
2709
2711
2715 void updateShootFruitCounts(uint plantID) const;
2716
2717
2719
2724 [[nodiscard]] std::vector<uint> getShootInternodeObjectIDs(uint plantID) const;
2725
2727
2734 [[nodiscard]] std::vector<uint> getPlantCollisionRelevantObjectIDs(uint plantID) const;
2735
2737
2740 [[nodiscard]] std::vector<uint> getAllUUIDs() const;
2741
2743
2746 [[nodiscard]] std::vector<uint> getAllLeafUUIDs() const;
2747
2749
2752 [[nodiscard]] std::vector<uint> getAllInternodeUUIDs() const;
2753
2755
2758 [[nodiscard]] std::vector<uint> getAllPetioleUUIDs() const;
2759
2761
2764 [[nodiscard]] std::vector<uint> getAllPeduncleUUIDs() const;
2765
2767
2770 [[nodiscard]] std::vector<uint> getAllFlowerUUIDs() const;
2771
2773
2776 [[nodiscard]] std::vector<uint> getAllFruitUUIDs() const;
2777
2779
2782 [[nodiscard]] std::vector<uint> getAllObjectIDs() const;
2783
2784 // -- carbohydrate model -- //
2785
2790
2795
2796 // -- Nitrogen Model Methods -- //
2797
2807 void enableNitrogenModel();
2808
2812 void disableNitrogenModel();
2813
2818 [[nodiscard]] bool isNitrogenModelEnabled() const;
2819
2826 void setPlantNitrogenParameters(uint plantID, const NitrogenParameters &params);
2827
2834 void setPlantNitrogenParameters(const std::vector<uint> &plantIDs, const NitrogenParameters &params);
2835
2840 void initializeNitrogenPools(float initial_leaf_N_concentration);
2841
2848 void initializePlantNitrogenPools(uint plantID, float initial_leaf_N_concentration);
2849
2856 void addPlantNitrogen(uint plantID, float amount_gN);
2857
2864 void addPlantNitrogen(const std::vector<uint> &plantIDs, float amount_gN);
2865
2866 // -- manual plant generation from input string -- //
2867
2874 [[nodiscard]] std::string getPlantString(uint plantID) const;
2875
2883 uint generatePlantFromString(const std::string &generation_string, const PhytomerParameters &phytomer_parameters);
2884
2893 uint generatePlantFromString(const std::string &generation_string, const std::map<std::string, PhytomerParameters> &phytomer_parameters);
2894
2903 void writePlantStructureXML(uint plantID, const std::string &filename) const;
2904
2906
2917 void writeQSMCylinderFile(uint plantID, const std::string &filename) const;
2918
2930 std::vector<uint> readPlantStructureXML(const std::string &filename, bool quiet = false);
2931
2933 void disableMessages();
2934
2936 void enableMessages();
2937
2939
2957 static std::string resolveTextureFile(const std::string &texture_file);
2958
2959 friend struct Phytomer;
2960 friend struct Shoot;
2961
2962private:
2964
2974 float 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;
2975
2977 void clearBVHCache() const;
2978
2980 void rebuildBVHForTimestep();
2981
2983
2993 void setPlantAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points, float view_half_angle_deg = 80.0f, float look_ahead_distance = 0.1f, float attraction_weight = 0.6f, float obstacle_reduction_factor = 0.75f);
2994
2996
3002 void ensureInflorescencePrototypesInitialized(const PhytomerParameters &params);
3003
3004protected:
3005 helios::Context *context_ptr;
3006
3007 std::minstd_rand0 *generator = nullptr;
3008
3009 uint plant_count = 0;
3010
3011 std::string current_plant_model;
3012
3013 // Current build parameters for plant construction (set before calling builder functions)
3014 std::map<std::string, float> current_build_parameters;
3015
3016 // Function pointer maps for plant model registration
3017 std::map<std::string, std::function<void()>> shoot_initializers;
3018 std::map<std::string, std::function<uint(const helios::vec3 &)>> plant_builders;
3019 std::map<std::string, std::string> plant_type_map;
3020
3021 std::map<uint, PlantInstance> plant_instances;
3022
3023 [[nodiscard]] std::string makeShootString(const std::string &current_string, const std::shared_ptr<Shoot> &shoot, const std::vector<std::shared_ptr<Shoot>> &shoot_tree) const;
3024
3025 std::map<std::string, ShootParameters> shoot_types;
3026
3027 // Key is the prototype function pointer; value first index is the unique leaf prototype, second index is the leaflet along a compound leaf (if applicable)
3028 // std::map<uint(*)(helios::Context* context_ptr, LeafPrototype* prototype_parameters, int compound_leaf_index),std::vector<std::vector<uint>> > unique_leaf_prototype_objIDs;
3029 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
3030
3031 // Key is the prototype function pointer; value index is the unique flower prototype
3032 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
3033 // Key is the prototype function pointer; value index is the unique flower prototype
3034 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
3035 // Key is the prototype function pointer; value index is the unique fruit prototype
3036 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
3037
3038 bool build_context_geometry_internode = true;
3039 bool build_context_geometry_petiole = true;
3040 bool build_context_geometry_peduncle = true;
3041
3042 float ground_clipping_height = -99999;
3043
3044 void validateShootTypes(ShootParameters &shoot_parameters, const std::map<std::string, ShootParameters> &shoot_types_ref) const;
3045
3047 void registerPlantModel(const std::string &name, std::function<void()> shoot_init, std::function<uint(const helios::vec3 &)> plant_build, const std::string &plant_type = "herbaceous");
3048
3050 void initializePlantModelRegistrations();
3051
3052 void parseStringShoot(const std::string &LString_shoot, uint plantID, int parentID, uint parent_node, const std::map<std::string, PhytomerParameters> &phytomer_parameters, ShootParameters &shoot_parameters);
3053
3054 void parseShootArgument(const std::string &shoot_argument, const std::map<std::string, PhytomerParameters> &phytomer_parameters, ShootParameters &shoot_parameters, AxisRotation &base_rotation, std::string &phytomer_label);
3055
3056 void parseInternodeArgument(const std::string &internode_argument, float &internode_radius, float &internode_length, PhytomerParameters &phytomer_parameters);
3057
3058 void parsePetioleArgument(const std::string &petiole_argument, PhytomerParameters &phytomer_parameters);
3059
3060 void parseLeafArgument(const std::string &leaf_argument, PhytomerParameters &phytomer_parameters);
3061
3062 void initializeDefaultShoots(const std::string &plant_label);
3063
3064 [[nodiscard]] bool detectGroundCollision(uint objID);
3065
3066 [[nodiscard]] bool detectGroundCollision(const std::vector<uint> &objID) const;
3067
3068 void setPlantLeafAngleDistribution_private(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity_azimuth, float ellipse_rotation_azimuth_degrees, bool set_elevation, bool set_azimuth) const;
3069
3070 static float interpolateTube(const std::vector<float> &P, float frac);
3071
3072 static helios::vec3 interpolateTube(const std::vector<helios::vec3> &P, float frac);
3073
3075 std::map<std::string, bool> output_object_data;
3076
3077 // --- Plant Growth --- //
3078
3079 void incrementPhytomerInternodeGirth(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3080 void incrementPhytomerInternodeGirth_carb(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3081
3082 void pruneGroundCollisions(uint plantID);
3083
3084 void pruneSolidBoundaryCollisions();
3085
3086 // --- Carbohydrate Model --- //
3087
3088 void accumulateShootPhotosynthesis() const;
3089
3090 void subtractShootMaintenanceCarbon(float dt) const;
3091 void subtractShootGrowthCarbon();
3092
3093 void checkCarbonPool_abortOrgans(float dt);
3094 void checkCarbonPool_adjustPhyllochron(float dt);
3095 void checkCarbonPool_transferCarbon(float dt);
3096
3097 bool carbon_model_enabled = false;
3098
3099 // --- Nitrogen Model --- //
3100
3101 void accumulateLeafNitrogen(float dt);
3102 void remobilizeNitrogen(float dt);
3103 void updateNitrogenStressFactor();
3104 void removeFruitNitrogen();
3105
3106 bool nitrogen_model_enabled = false;
3107
3108 // --- Collision Detection --- //
3109
3111 CollisionDetection *collision_detection_ptr = nullptr;
3112
3114 bool owns_collision_detection = false;
3115
3117 bool collision_detection_enabled = false;
3118
3120 std::vector<uint> collision_target_UUIDs;
3121
3123 std::vector<uint> collision_target_object_IDs;
3124
3126 float collision_cone_half_angle_rad = 80.f * M_PI / 180.f;
3127
3129 float collision_cone_height = 0.1f;
3130
3132 int collision_sample_count = 256;
3133
3135 float collision_inertia_weight = 0.4f;
3136
3138 int geometry_update_frequency = 3;
3139
3141 bool force_update_on_collision = true;
3142
3144 bool collision_include_internodes = false;
3145 bool collision_include_leaves = true;
3146 bool collision_include_petioles = false;
3147 bool collision_include_flowers = false;
3148 bool collision_include_fruit = false;
3149
3151 bool petiole_collision_detection_enabled = false;
3152
3154 bool fruit_collision_detection_enabled = false;
3155
3157 int geometry_update_counter = 0;
3158
3160 mutable bool collision_avoidance_applied = false;
3161
3163 bool spatial_filtering_enabled = false;
3164
3166 float spatial_max_distance = 5.0f;
3167
3169 mutable bool bvh_cached_for_current_growth = false;
3170 mutable std::vector<uint> cached_target_geometry;
3171 mutable std::vector<uint> cached_filtered_geometry;
3172
3174 bool solid_obstacle_avoidance_enabled = false;
3175 std::vector<uint> solid_obstacle_UUIDs;
3176 float solid_obstacle_avoidance_distance = 0.5f;
3177 float solid_obstacle_minimum_distance = 0.05f;
3178 bool solid_obstacle_fruit_adjustment_enabled = false;
3179 bool solid_obstacle_pruning_enabled = false;
3180
3181 // --- Attraction Points --- //
3182
3184 bool attraction_points_enabled = false;
3185
3187 std::vector<helios::vec3> attraction_points;
3188
3190 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
3191
3193 float attraction_cone_height = 0.1f;
3194
3196 float attraction_weight = 0.6f;
3197
3199 float attraction_obstacle_reduction_factor = 0.5f;
3200
3202 bool printmessages = true;
3203
3204 // --- Plant Library --- //
3205
3206 void initializeAlmondTreeShoots();
3207
3208 uint buildAlmondTree(const helios::vec3 &base_position);
3209
3210 void initializeAlmondTreeAldrichShoots();
3211
3212 uint buildAlmondTreeAldrich(const helios::vec3 &base_position);
3213
3214 void initializeAlmondTreeWoodColonyShoots();
3215
3216 uint buildAlmondTreeWoodColony(const helios::vec3 &base_position);
3217
3218 void initializeAppleTreeShoots();
3219
3220 uint buildAppleTree(const helios::vec3 &base_position);
3221
3222 void initializeAppleFruitingWallShoots();
3223
3224 uint buildAppleFruitingWall(const helios::vec3 &base_position);
3225
3226 void initializeAsparagusShoots();
3227
3228 uint buildAsparagusPlant(const helios::vec3 &base_position);
3229
3230 void initializeBindweedShoots();
3231
3232 uint buildBindweedPlant(const helios::vec3 &base_position);
3233
3234 void initializeBeanShoots();
3235
3236 uint buildBeanPlant(const helios::vec3 &base_position);
3237
3238 void initializeBougainvilleaShoots();
3239
3240 uint buildBougainvilleaPlant(const helios::vec3 &base_position);
3241
3242 void initializeCapsicumShoots();
3243
3244 uint buildCapsicumPlant(const helios::vec3 &base_position);
3245
3246 void initializeCheeseweedShoots();
3247
3248 uint buildCheeseweedPlant(const helios::vec3 &base_position);
3249
3250 void initializeCowpeaShoots();
3251
3252 uint buildCowpeaPlant(const helios::vec3 &base_position);
3253
3254 void initializeGrapevineVSPShoots();
3255
3256 uint buildGrapevineVSP(const helios::vec3 &base_position);
3257
3258 void initializeGrapevineWyeShoots();
3259
3260 uint buildGrapevineWye(const helios::vec3 &base_position);
3261
3262 void initializeGroundCherryWeedShoots();
3263
3264 uint buildGroundCherryWeedPlant(const helios::vec3 &base_position);
3265
3266 void initializeMaizeShoots();
3267
3268 uint buildMaizePlant(const helios::vec3 &base_position);
3269
3270 void initializeOliveTreeShoots();
3271
3272 uint buildOliveTree(const helios::vec3 &base_position);
3273
3274 void initializePistachioTreeShoots();
3275
3276 uint buildPistachioTree(const helios::vec3 &base_position);
3277
3278 void initializePuncturevineShoots();
3279
3280 uint buildPuncturevinePlant(const helios::vec3 &base_position);
3281
3282 void initializeEasternRedbudShoots();
3283
3284 uint buildEasternRedbudPlant(const helios::vec3 &base_position);
3285
3286 void initializeRiceShoots();
3287
3288 uint buildRicePlant(const helios::vec3 &base_position);
3289
3290 void initializeButterLettuceShoots();
3291
3292 uint buildButterLettucePlant(const helios::vec3 &base_position);
3293
3294 void initializeSoybeanShoots();
3295
3296 uint buildSoybeanPlant(const helios::vec3 &base_position);
3297
3298 void initializeSorghumShoots();
3299
3300 uint buildSorghumPlant(const helios::vec3 &base_position);
3301
3302 void initializeStrawberryShoots();
3303
3304 uint buildStrawberryPlant(const helios::vec3 &base_position);
3305
3306 void initializeSugarbeetShoots();
3307
3308 uint buildSugarbeetPlant(const helios::vec3 &base_position);
3309
3310 void initializeTomatoShoots();
3311
3312 uint buildTomatoPlant(const helios::vec3 &base_position);
3313
3314 void initializeCherryTomatoShoots();
3315
3316 uint buildCherryTomatoPlant(const helios::vec3 &base_position);
3317
3318 void initializeWalnutTreeShoots();
3319
3320 uint buildWalnutTree(const helios::vec3 &base_position);
3321
3322 void initializeWheatShoots();
3323
3324 uint buildWheatPlant(const helios::vec3 &base_position);
3325};
3326
3327#include "Assets.h"
3328
3329#endif // PLANT_ARCHITECTURE