1.3.77
 
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 = 675000;
280 float stem_carbon_percentage = 0.457; //DeJong & Walton 1989
282 float stem_carbohydrate_percentage = 1 - (1 / 1.14); //DeJong & Walton 1989
286 float maturity_age = 120;
291
292 // -- Leaf Growth Parameters -- //
293
295 float leaf_total_carbon_percentage = 0.453; //DeJong & Walton 1989
297 float SLA = 9.2/10000/leaf_total_carbon_percentage*12.01; //AL Pica 2022
298 float leaf_carbohydrate_percentage = 1 - (1/1.13); //DeJong & Walton 1989
299 float leaf_carbon_percentage = leaf_total_carbon_percentage;
300 // -- Flower Growth Parameters -- //
302 float total_flower_cost = 8.33e-4;
303
304 // -- Fruit Growth Parameters -- //
306 float fruit_density = 525000;
308 float fruit_carbon_percentage = 0.475; //DeJong & Walton 1989
309
310 // -- Respiration Parameters -- //
312 float r_m_w_20 = 5.25164e-05; // DeJong & Goudriaan 1989
314 float r_m_r_20 = 5.25164e-03; // DeJong & Goudriaan 1989
318 float growth_respiration_fraction = 0.211; //DeJong & Goudriaan 1989
319
320 // -- Organ Abortion Thresholds -- //
329
330 // -- Phyllochron Adjustment Parameters -- //
335
338
339 // -- Carbon Transfer Parameters -- //
342 float carbohydrate_transfer_threshold_down = 0.025;
343 float carbohydrate_transfer_threshold_up = 0.04;
344 float carbon_conductance_down = 0.95; //<= 1.0
345 float carbon_conductance_up = carbon_conductance_down * 0.5; // Conductance of carbon from parent to child shoots << conductance from child to parent
346};
347
350
351 // -- Leaf Nitrogen Content (Area Basis) -- //
353 float target_leaf_N_area = 1.5f;
356
357 // -- Allocation Parameters -- //
360
361 // -- Rate Limiting Parameters -- //
364
365 // -- Remobilization Parameters -- //
370
371 // -- Fruit Nitrogen Parameters -- //
373 float fruit_N_area = 1.0f;
374};
375
377
385std::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);
386
388
389 // state of the bud
390 BudState state = BUD_DORMANT;
391 // label of the shoot type that will be produced if the bud breaks into a shoot
392 std::string shoot_type_label;
393 // ID of the shoot that the bud will produce if it breaks into a shoot
394 uint shoot_ID = -1;
395};
396
397struct FloralBud {
398
399 // state of the bud
400 BudState state = BUD_DORMANT;
401 // amount of time since the bud flowered (=0 if it has not yet flowered)
402 float time_counter = 0;
403 // cumulative age since bud break (first transition out of dormant/active state)
404 float age = 0;
405 //=0 for axillary buds, =1 for terminal buds
406 bool isterminal = false;
407 // For axillary buds: index of the petiole within the internode that this floral bud originates from
408 // For terminal buds: index of the phytomer within the shoot that this floral bud originates from
409 uint parent_index = 0;
410 // Index of the bud within the petiole that this floral bud originates from
411 uint bud_index = 0;
412 // Scaling factor fraction of the fruit (if present), ranging from 0 to 1
413 float current_fruit_scale_factor = 1;
414 float previous_fruit_scale_factor = 0;
415
416
417 helios::vec3 base_position;
418 AxisRotation base_rotation;
419 helios::vec3 bending_axis;
420
421 std::vector<helios::vec3> inflorescence_bases;
422 std::vector<AxisRotation> inflorescence_rotation; // pitch, yaw, roll for each flower/fruit
423 std::vector<float> inflorescence_base_scales; // individual base scale for each flower/fruit (before growth scaling)
424 std::vector<uint> peduncle_objIDs;
425 std::vector<uint> inflorescence_objIDs;
426};
427
429public:
431 explicit LeafPrototype(std::minstd_rand0 *generator);
432
434 LeafPrototype() = default;
435
437 uint (*prototype_function)(helios::Context *, LeafPrototype *prototype_parameters, int compound_leaf_index) = nullptr;
438
440
443 std::string OBJ_model_file;
444
446
449 std::map<int, std::string> leaf_texture_file;
450
451 // Ratio of leaf width to leaf length
452 RandomParameter_float leaf_aspect_ratio;
453
456
457 // Parameters for leaf curvature
462
465
466 // Parameters for leaf wave/wrinkles
471
472 // Parameters for leaf buckling
477
480
483
486
488 bool build_petiolule = false;
489
490 uint unique_prototype_identifier = 0;
491
492 void duplicate(const LeafPrototype &a) {
493 this->leaf_texture_file = a.leaf_texture_file;
494 this->OBJ_model_file = a.OBJ_model_file;
495 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
496 this->midrib_fold_fraction = a.midrib_fold_fraction;
497 this->longitudinal_curvature = a.longitudinal_curvature;
498 this->lateral_curvature = a.lateral_curvature;
499 this->petiole_roll = a.petiole_roll;
500 this->wave_period = a.wave_period;
501 this->wave_amplitude = a.wave_amplitude;
502 this->leaf_buckle_length = a.leaf_buckle_length;
503 this->leaf_buckle_angle = a.leaf_buckle_angle;
504 this->leaf_offset = a.leaf_offset;
505 this->subdivisions = a.subdivisions;
506 this->unique_prototypes = a.unique_prototypes;
507 this->unique_prototype_identifier = a.unique_prototype_identifier;
508 this->build_petiolule = a.build_petiolule;
510 this->generator = a.generator;
511 }
512
515 if (this != &a) {
516 this->leaf_texture_file = a.leaf_texture_file;
517 this->OBJ_model_file = a.OBJ_model_file;
518 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
519 if (a.leaf_aspect_ratio.distribution != "constant")
520 this->leaf_aspect_ratio.resample();
521 this->midrib_fold_fraction = a.midrib_fold_fraction;
522 if (a.midrib_fold_fraction.distribution != "constant")
523 this->midrib_fold_fraction.resample();
524 this->longitudinal_curvature = a.longitudinal_curvature;
525 if (a.longitudinal_curvature.distribution != "constant")
526 this->longitudinal_curvature.resample();
527 this->lateral_curvature = a.lateral_curvature;
528 if (a.lateral_curvature.distribution != "constant")
529 this->lateral_curvature.resample();
530 this->petiole_roll = a.petiole_roll;
531 if (a.petiole_roll.distribution != "constant")
532 this->petiole_roll.resample();
533 this->wave_period = a.wave_period;
534 if (a.wave_period.distribution != "constant")
535 this->wave_period.resample();
536 this->wave_amplitude = a.wave_amplitude;
537 if (a.wave_amplitude.distribution != "constant")
538 this->wave_amplitude.resample();
539 this->leaf_buckle_length = a.leaf_buckle_length;
540 if (a.leaf_buckle_length.distribution != "constant")
541 this->leaf_buckle_length.resample();
542 this->leaf_buckle_angle = a.leaf_buckle_angle;
543 if (a.leaf_buckle_angle.distribution != "constant")
544 this->leaf_buckle_angle.resample();
545 this->leaf_offset = a.leaf_offset;
546 this->subdivisions = a.subdivisions;
547 this->unique_prototypes = a.unique_prototypes;
548 this->unique_prototype_identifier = a.unique_prototype_identifier;
549 this->build_petiolule = a.build_petiolule;
551 this->generator = a.generator;
552 if (this->generator != nullptr) {
553 this->sampleIdentifier();
554 }
555 }
556 return *this;
557 }
558
559 void sampleIdentifier() {
560 assert(generator != nullptr);
561 std::uniform_int_distribution<uint> unif_distribution;
562 this->unique_prototype_identifier = unif_distribution(*generator);
563 }
564
565private:
566 std::minstd_rand0 *generator{};
567};
568
570private:
571 struct InternodeParameters {
572
576 RandomParameter_float phyllotactic_angle;
578 RandomParameter_float radius_initial;
580 RandomParameter_int max_vegetative_buds_per_petiole;
582 RandomParameter_int max_floral_buds_per_petiole;
584 helios::RGBcolor color;
586 std::string image_texture;
588 uint length_segments;
590 uint radial_subdivisions;
591
592 InternodeParameters &operator=(const InternodeParameters &a) {
593 if (this != &a) {
594 this->pitch = a.pitch;
595 if (a.pitch.distribution != "constant")
596 this->pitch.resample();
597 this->phyllotactic_angle = a.phyllotactic_angle;
598 if (a.phyllotactic_angle.distribution != "constant")
599 this->phyllotactic_angle.resample();
600 this->radius_initial = a.radius_initial;
601 if (a.radius_initial.distribution != "constant")
602 this->radius_initial.resample();
603 this->max_vegetative_buds_per_petiole = a.max_vegetative_buds_per_petiole;
604 if (a.max_vegetative_buds_per_petiole.distribution != "constant")
605 this->max_vegetative_buds_per_petiole.resample();
606 this->max_floral_buds_per_petiole = a.max_floral_buds_per_petiole;
607 if (a.max_floral_buds_per_petiole.distribution != "constant")
608 this->max_floral_buds_per_petiole.resample();
609 this->color = a.color;
610 this->image_texture = a.image_texture;
611 this->length_segments = a.length_segments;
612 this->radial_subdivisions = a.radial_subdivisions;
613 }
614 return *this;
615 }
616 };
617
618 struct PetioleParameters {
619
621 uint petioles_per_internode;
629 RandomParameter_float curvature;
633 helios::RGBcolor color;
635 uint length_segments;
637 uint radial_subdivisions;
638
639 PetioleParameters &operator=(const PetioleParameters &a) {
640 if (this != &a) {
641 this->petioles_per_internode = a.petioles_per_internode;
642 this->pitch = a.pitch;
643 if (a.pitch.distribution != "constant")
644 this->pitch.resample();
645 this->radius = a.radius;
646 if (a.radius.distribution != "constant")
647 this->radius.resample();
648 this->length = a.length;
649 if (a.length.distribution != "constant")
650 this->length.resample();
651 this->curvature = a.curvature;
652 if (a.curvature.distribution != "constant")
653 this->curvature.resample();
654 this->taper = a.taper;
655 if (a.taper.distribution != "constant")
656 this->taper.resample();
657 this->color = a.color;
658 this->length_segments = a.length_segments;
659 this->radial_subdivisions = a.radial_subdivisions;
660 }
661 return *this;
662 }
663 };
664
665 struct LeafParameters {
667 RandomParameter_int leaves_per_petiole;
675 RandomParameter_float leaflet_offset;
677 RandomParameter_float leaflet_scale;
679 RandomParameter_float prototype_scale;
681 LeafPrototype prototype;
682
683 LeafParameters &operator=(const LeafParameters &a) {
684 if (this != &a) {
685 this->leaves_per_petiole = a.leaves_per_petiole;
686 if (a.leaves_per_petiole.distribution != "constant")
687 this->leaves_per_petiole.resample();
688 this->pitch = a.pitch;
689 if (a.pitch.distribution != "constant")
690 this->pitch.resample();
691 this->yaw = a.yaw;
692 if (a.yaw.distribution != "constant")
693 this->yaw.resample();
694 this->roll = a.roll;
695 if (a.roll.distribution != "constant")
696 this->roll.resample();
697 this->leaflet_offset = a.leaflet_offset;
698 if (a.leaflet_offset.distribution != "constant")
699 this->leaflet_offset.resample();
700 this->leaflet_scale = a.leaflet_scale;
701 if (a.leaflet_scale.distribution != "constant")
702 this->leaflet_scale.resample();
703 this->prototype_scale = a.prototype_scale;
704 if (a.prototype_scale.distribution != "constant")
705 this->prototype_scale.resample();
706 this->prototype.duplicate(a.prototype);
707 }
708 return *this;
709 }
710 };
711
712 struct PeduncleParameters {
722 RandomParameter_float curvature;
724 helios::RGBcolor color;
726 uint length_segments;
728 uint radial_subdivisions;
729
730 PeduncleParameters &operator=(const PeduncleParameters &a) {
731 if (this != &a) {
732 this->length = a.length;
733 if (a.length.distribution != "constant")
734 this->length.resample();
735 this->radius = a.radius;
736 if (a.radius.distribution != "constant")
737 this->radius.resample();
738 this->pitch = a.pitch;
739 if (a.pitch.distribution != "constant")
740 this->pitch.resample();
741 this->roll = a.roll;
742 if (a.roll.distribution != "constant")
743 this->roll.resample();
744 this->curvature = a.curvature;
745 if (a.curvature.distribution != "constant")
746 this->curvature.resample();
747 this->color = a.color;
748 this->length_segments = a.length_segments;
749 this->radial_subdivisions = a.radial_subdivisions;
750 }
751 return *this;
752 }
753 };
754
755 struct InflorescenceParameters {
756
758 RandomParameter_int flowers_per_peduncle;
760 RandomParameter_float flower_offset;
766 RandomParameter_float flower_prototype_scale;
768 uint (*flower_prototype_function)(helios::Context *, uint subdivisions, bool flower_is_open) = nullptr;
770 RandomParameter_float fruit_prototype_scale;
772 uint (*fruit_prototype_function)(helios::Context *, uint subdivisions) = nullptr;
774 RandomParameter_float fruit_gravity_factor_fraction;
776 uint unique_prototypes;
777
778 InflorescenceParameters &operator=(const InflorescenceParameters &a) {
779 if (this != &a) {
780 this->flowers_per_peduncle = a.flowers_per_peduncle;
781 this->flowers_per_peduncle.resample();
782 this->flower_offset = a.flower_offset;
783 this->flower_offset.resample();
784 this->pitch = a.pitch;
785 this->pitch.resample();
786 this->roll = a.roll;
787 this->roll.resample();
788 this->flower_prototype_scale = a.flower_prototype_scale;
789 this->flower_prototype_scale.resample();
790 this->flower_prototype_function = a.flower_prototype_function;
791 this->fruit_prototype_scale = a.fruit_prototype_scale;
792 this->fruit_prototype_scale.resample();
793 this->fruit_prototype_function = a.fruit_prototype_function;
794 this->fruit_gravity_factor_fraction = a.fruit_gravity_factor_fraction;
795 this->fruit_gravity_factor_fraction.resample();
796 this->unique_prototypes = a.unique_prototypes;
797 }
798 return *this;
799 }
800 };
801
802public:
810 InternodeParameters internode;
811
818 PetioleParameters petiole;
819
827 LeafParameters leaf;
828
835 PeduncleParameters peduncle;
836
845 InflorescenceParameters inflorescence;
846
847 // Custom user-defined function that is called when a phytomer is created
855 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;
856
857 // Custom user-defined function that is called for each phytomer on every time step
861 void (*phytomer_callback_function)(std::shared_ptr<Phytomer> phytomer_ptr) = nullptr;
862
863
866
868 explicit PhytomerParameters(std::minstd_rand0 *generator);
869
870 friend class PlantArchitecture;
871 friend struct Phytomer;
872 friend struct Shoot;
873};
874
876
879
881 explicit ShootParameters(std::minstd_rand0 *generator);
882
889
890 // ---- Geometric Parameters ---- //
891
917
918 // --- Growth Parameters --- //
919
927
944
945 // ---- Custom Functions ---- //
946
955 void defineChildShootTypes(const std::vector<std::string> &child_shoot_type_labels, const std::vector<float> &child_shoot_type_probabilities);
956
957 ShootParameters &operator=(const ShootParameters &a) {
958 this->phytomer_parameters = a.phytomer_parameters;
959 this->max_nodes = a.max_nodes;
960 this->max_nodes.resample();
961 this->max_nodes_per_season = a.max_nodes_per_season;
962 this->max_nodes_per_season.resample();
963 this->phyllochron_min = a.phyllochron_min;
964 this->phyllochron_min.resample();
965 this->girth_area_factor = a.girth_area_factor;
966 this->girth_area_factor.resample();
967 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
968 this->vegetative_bud_break_probability_min.resample();
969 this->vegetative_bud_break_probability_max = a.vegetative_bud_break_probability_max;
970 this->vegetative_bud_break_probability_max.resample();
971 this->flower_bud_break_probability = a.flower_bud_break_probability;
972 this->flower_bud_break_probability.resample();
973 this->fruit_set_probability = a.fruit_set_probability;
974 this->fruit_set_probability.resample();
975 this->gravitropic_curvature = a.gravitropic_curvature;
976 this->gravitropic_curvature.resample();
977 this->tortuosity = a.tortuosity;
978 this->tortuosity.resample();
979 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
980 this->vegetative_bud_break_probability_min.resample();
981 this->vegetative_bud_break_probability_decay_rate = a.vegetative_bud_break_probability_decay_rate;
982 this->vegetative_bud_break_probability_decay_rate.resample();
983 this->max_terminal_floral_buds = a.max_terminal_floral_buds;
984 this->max_terminal_floral_buds.resample();
985 this->flower_bud_break_probability = a.flower_bud_break_probability;
986 this->flower_bud_break_probability.resample();
987 this->fruit_set_probability = a.fruit_set_probability;
988 this->fruit_set_probability.resample();
989 this->vegetative_bud_break_time = a.vegetative_bud_break_time;
990 this->vegetative_bud_break_time.resample();
991 this->insertion_angle_tip = a.insertion_angle_tip;
992 this->insertion_angle_tip.resample();
993 this->insertion_angle_decay_rate = a.insertion_angle_decay_rate;
994 this->insertion_angle_decay_rate.resample();
995 this->internode_length_max = a.internode_length_max;
996 this->internode_length_max.resample();
997 this->internode_length_min = a.internode_length_min;
998 this->internode_length_min.resample();
999 this->internode_length_decay_rate = a.internode_length_decay_rate;
1000 this->internode_length_decay_rate.resample();
1001 this->base_roll = a.base_roll;
1002 this->base_roll.resample();
1003 this->base_yaw = a.base_yaw;
1004 this->base_yaw.resample();
1005 this->flowers_require_dormancy = a.flowers_require_dormancy;
1006 this->growth_requires_dormancy = a.growth_requires_dormancy;
1007 this->child_shoot_type_labels = a.child_shoot_type_labels;
1008 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1009 this->determinate_shoot_growth = a.determinate_shoot_growth;
1010 this->child_shoot_type_labels = a.child_shoot_type_labels;
1011 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1012 return *this;
1013 }
1014
1015 friend class PlantArchitecture;
1016 friend struct Shoot;
1017
1018protected:
1019 std::vector<std::string> child_shoot_type_labels;
1020 std::vector<float> child_shoot_type_probabilities;
1021};
1022
1023struct Phytomer {
1024public:
1026 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,
1027 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);
1028
1029 // ---- query info about the phytomer ---- //
1030
1036 [[nodiscard]] std::vector<helios::vec3> getInternodeNodePositions() const;
1037
1043 [[nodiscard]] std::vector<float> getInternodeNodeRadii() const;
1044
1051 [[nodiscard]] float calculatePhytomerVolume(uint node_number) const;
1052
1059 [[nodiscard]] helios::vec3 getInternodeAxisVector(float stem_fraction) const;
1060
1068 [[nodiscard]] helios::vec3 getPetioleAxisVector(float stem_fraction, uint petiole_index) const;
1069
1078 [[nodiscard]] helios::vec3 getPeduncleAxisVector(float stem_fraction, uint petiole_index, uint bud_index) const;
1079
1087 [[nodiscard]] static helios::vec3 getAxisVector(float stem_fraction, const std::vector<helios::vec3> &axis_vertices);
1088
1094 [[nodiscard]] float getInternodeLength() const;
1095
1101 [[nodiscard]] float getInternodeRadius() const;
1102
1108 [[nodiscard]] float getPetioleLength() const;
1109
1116 [[nodiscard]] float getInternodeRadius(float stem_fraction) const;
1117
1123 [[nodiscard]] float getLeafArea() const;
1124
1132 [[nodiscard]] helios::vec3 getLeafBasePosition(uint petiole_index, uint leaf_index) const;
1133
1139 [[nodiscard]] bool hasLeaf() const;
1140
1146 [[nodiscard]] float calculateDownstreamLeafArea() const;
1147
1148 // ---- modify the phytomer ---- //
1149
1151
1155 void setInternodeLengthScaleFraction(float internode_scale_factor_fraction, bool update_context_geometry);
1156
1158
1161 void scaleInternodeMaxLength(float scale_factor);
1162
1164
1167 void setInternodeMaxLength(float internode_length_max_new);
1168
1170
1173 void setInternodeMaxRadius(float internode_radius_max_new);
1174
1176
1180 void setLeafScaleFraction(uint petiole_index, float leaf_scale_factor_fraction);
1181
1183
1186 void setLeafScaleFraction(float leaf_scale_factor_fraction);
1187
1189
1193 void setLeafPrototypeScale(uint petiole_index, float leaf_prototype_scale);
1194
1196
1199 void setLeafPrototypeScale(float leaf_prototype_scale);
1200
1202
1207 void scaleLeafPrototypeScale(uint petiole_index, float scale_factor);
1208
1210
1213 void scaleLeafPrototypeScale(float scale_factor);
1214
1216
1223 void scalePetioleGeometry(uint petiole_index, float target_length, float target_base_radius);
1224
1231 void setInflorescenceScaleFraction(FloralBud &fbud, float inflorescence_scale_factor_fraction) const;
1232
1241 void setPetioleBase(const helios::vec3 &base_position);
1242
1253 void rotateLeaf(uint petiole_index, uint leaf_index, const AxisRotation &rotation);
1254
1271 void rotatePetiole(uint petiole_index, const AxisRotation &rotation);
1272
1278 void setVegetativeBudState(BudState state);
1279
1287 void setVegetativeBudState(BudState state, uint petiole_index, uint bud_index);
1288
1295 static void setVegetativeBudState(BudState state, VegetativeBud &vbud);
1296
1302 void setFloralBudState(BudState state);
1303
1311 void setFloralBudState(BudState state, uint petiole_index, uint bud_index);
1312
1319 void setFloralBudState(BudState state, FloralBud &fbud);
1320
1328 void removeLeaf();
1329
1337 void deletePhytomer();
1338
1339private:
1351 helios::vec3 calculateCollisionAvoidanceDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &collision_detection_active) const;
1352
1364 bool applySolidObstacleAvoidance(const helios::vec3 &current_position, helios::vec3 &internode_axis) const;
1365
1378 helios::vec3 calculateAttractionPointDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &attraction_active) const;
1379
1392 helios::vec3 calculatePetioleCollisionAvoidanceDirection(const helios::vec3 &petiole_base_origin, const helios::vec3 &proposed_petiole_axis, bool &collision_detection_active) const;
1393
1395
1401 helios::vec3 calculateFruitCollisionAvoidanceDirection(const helios::vec3 &fruit_base_origin, const helios::vec3 &proposed_fruit_axis, bool &collision_detection_active) const;
1402
1403
1404public:
1405 // ---- phytomer data ---- //
1406
1408 std::vector<std::vector<helios::vec3>> petiole_vertices; // first index is petiole within internode, second index is tube segment within petiole tube
1409 std::vector<std::vector<helios::vec3>> leaf_bases; // first index is petiole within internode, second index is leaf within petiole
1410 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
1411 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
1412 std::vector<std::vector<float>> peduncle_length; // actual sampled length for each peduncle - first index is petiole, second is bud
1413 std::vector<std::vector<float>> peduncle_radius; // actual sampled radius for each peduncle - first index is petiole, second is bud
1414 std::vector<std::vector<float>> peduncle_pitch; // actual sampled pitch for each peduncle - first index is petiole, second is bud
1415 std::vector<std::vector<float>> peduncle_curvature; // actual sampled curvature for each peduncle - first index is petiole, second is bud
1416 float internode_pitch, internode_phyllotactic_angle;
1417
1418 std::vector<std::vector<float>> petiole_radii; // first index is petiole within internode, second index is segment within petiole tube
1419 std::vector<float> petiole_length; // index is petiole within internode
1420 std::vector<float> petiole_pitch; // index is petiole within internode
1421 std::vector<float> petiole_curvature; // index is petiole within internode
1422 std::vector<float> petiole_taper; // taper value for each petiole (tip_radius/base_radius ratio)
1423 std::vector<helios::vec3> petiole_axis_initial; // initial petiole axis (before curvature) for each petiole
1424 std::vector<helios::vec3> petiole_rotation_axis; // rotation axis for curvature application for each petiole
1425 std::vector<std::vector<float>> leaf_size_max; // first index is petiole within internode, second index is leaf within petiole
1426 std::vector<std::vector<AxisRotation>> leaf_rotation; // first index is petiole within internode, second index is leaf within petiole
1427
1428 std::vector<helios::RGBcolor> internode_colors; // index is segment within internode tube
1429 std::vector<helios::RGBcolor> petiole_colors; // index is segment within petiole tube
1430
1431 std::vector<std::vector<uint>> petiole_objIDs; // first index is petiole within internode, second index is segment within petiole tube
1432 std::vector<std::vector<uint>> leaf_objIDs; // first index is petiole within internode, second index is leaf within petiole tube
1433
1434 PhytomerParameters phytomer_parameters;
1435
1436 uint rank;
1439
1440 uint plantID;
1441 uint parent_shoot_ID;
1442 Shoot *parent_shoot_ptr;
1443
1445 float age = 0;
1446 bool isdormant = false;
1447
1448 float current_internode_scale_factor = 1;
1449 std::vector<float> current_leaf_scale_factor; // index is petiole within internode
1450
1451 float old_phytomer_volume = 0;
1452
1453 float downstream_leaf_area = 0;
1454
1455 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds; // first index is petiole within internode, second index is bud within petiole
1456 std::vector<std::vector<FloralBud>> floral_buds; // first index is petiole within internode, second index is bud within petiole
1457
1458 float internode_radius_initial;
1459 float internode_radius_max;
1460 float internode_length_max;
1461
1463 std::vector<float> internode_yaw_perturbations;
1464
1465 bool build_context_geometry_petiole = true;
1466 bool build_context_geometry_peduncle = true;
1467
1468protected:
1469 helios::vec3 inflorescence_bending_axis;
1470
1471 helios::Context *context_ptr;
1472
1473 PlantArchitecture *plantarchitecture_ptr;
1474
1475 void updateInflorescence(FloralBud &fbud);
1476
1493 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);
1494
1500 [[nodiscard]] float calculatePhytomerConstructionCosts() const;
1501
1509 [[nodiscard]] float calculateFlowerConstructionCosts(const FloralBud &fbud) const;
1510
1516 [[nodiscard]] float calculateFruitConstructionCosts(const FloralBud &fbud) const;
1517
1518 friend struct Shoot;
1519 friend class PlantArchitecture;
1520};
1521
1522struct Shoot {
1523
1525 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,
1526 float internode_length_shoot_initial, ShootParameters &shoot_params, std::string shoot_type_label, PlantArchitecture *plant_architecture_ptr);
1527
1529
1536 void buildShootPhytomers(float internode_radius, float internode_length, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper);
1537
1539
1546 int appendPhytomer(float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, const PhytomerParameters &phytomer_parameters);
1547
1549
1552 [[nodiscard]] std::string sampleChildShootType() const;
1553
1555
1559 [[nodiscard]] bool sampleVegetativeBudBreak(uint node_index) const;
1560
1561 [[nodiscard]] bool sampleVegetativeBudBreak_carb(uint node_index) const;
1562
1564
1569 uint sampleEpicormicShoot(float dt, std::vector<float> &epicormic_positions_fraction) const;
1570
1575 void terminateApicalBud();
1576
1582
1590 void addTerminalFloralBud();
1591
1598 [[nodiscard]] float calculateShootInternodeVolume() const;
1599
1605 [[nodiscard]] float calculateShootLength() const;
1606
1613 [[nodiscard]] helios::vec3 getShootAxisVector(float shoot_fraction) const;
1614
1622 [[nodiscard]] float sumShootLeafArea(uint start_node_index = 0) const;
1623
1630 [[nodiscard]] float sumChildVolume(uint start_node_index = 0) const;
1631
1639 void propagateDownstreamLeafArea(const Shoot *shoot, uint node_index, float leaf_area);
1640
1645 void updateShootNodes(bool update_context_geometry = true);
1646
1647 uint current_node_number = 0;
1648 uint nodes_this_season = 0;
1649
1650 helios::vec3 base_position;
1651 AxisRotation base_rotation;
1652 helios::vec3 radial_outward_axis;
1653
1654 const int ID;
1655 const int parent_shoot_ID;
1656 const uint plantID;
1657 const uint parent_node_index;
1658 const uint rank;
1659 const uint parent_petiole_index;
1660
1661 float sugar_pool_molC = 0; // mol C
1662 float starch_pool_molC = 0;
1663 float total_carbohydrate_pool_molC = sugar_pool_molC + starch_pool_molC;
1664
1666 std::map<uint, float> leaf_nitrogen_gN_m2;
1667
1668 float old_shoot_volume = 0;
1669
1670 float phyllochron_increase = 5;
1671 float phyllochron_recovery = phyllochron_increase;
1672
1673 float days_with_negative_carbon_balance = 0;
1674
1675 void breakDormancy();
1676 void makeDormant();
1677
1678 void mobilizeStarch();
1679
1680 bool isdormant;
1681 uint dormancy_cycles = 0;
1682
1683 bool meristem_is_alive = true;
1684
1685 float phyllochron_counter = 0;
1686 float phyllochron_min = 3.f;
1687 float elongation_max = 0.25;
1688
1689 float curvature_perturbation = 0;
1690 float yaw_perturbation = 0;
1691
1692 float gravitropic_curvature = 0;
1693
1694 const float internode_length_max_shoot_initial;
1695
1696 uint internode_tube_objID = 4294967294;
1697
1698 std::vector<std::vector<helios::vec3>> shoot_internode_vertices; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1699 std::vector<std::vector<float>> shoot_internode_radii; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1700
1701 bool build_context_geometry_internode = true;
1702
1703 // map of node number (key) to IDs of shoot children (value)
1704 std::map<int, std::vector<int>> childIDs;
1705
1706 ShootParameters shoot_parameters;
1707
1708 std::string shoot_type_label;
1709
1710 float phyllochron_instantaneous;
1711 float elongation_rate_instantaneous;
1712
1713 std::vector<std::shared_ptr<Phytomer>> phytomers;
1714
1715 PlantArchitecture *plantarchitecture_ptr;
1716
1717 helios::Context *context_ptr;
1718};
1719
1721
1722 PlantInstance(const helios::vec3 &a_base_position, float a_current_age, const std::string &a_plant_name, helios::Context *a_context_ptr) :
1723 base_position(a_base_position), current_age(a_current_age), plant_name(a_plant_name), context_ptr(a_context_ptr) {
1724 }
1725 std::vector<std::shared_ptr<Shoot>> shoot_tree;
1726 helios::vec3 base_position;
1727 float current_age;
1728 float time_since_dormancy = 0;
1729 helios::Context *context_ptr;
1730 std::string plant_name;
1731 std::pair<std::string, float> epicormic_shoot_probability_perlength_per_day; //.first is the epicormic shoot label string, .second is the probability
1732
1733 // Phenological thresholds
1734 float dd_to_dormancy_break = 0;
1735 float dd_to_flower_initiation = 0;
1736 float dd_to_flower_opening = 0;
1737 float dd_to_fruit_set = 0;
1738 float dd_to_fruit_maturity = 0;
1739 float dd_to_dormancy = 0;
1740 float max_leaf_lifespan = 1e6;
1741 bool is_evergreen = false;
1742
1743 float max_age = 999;
1744
1749
1750 CarbohydrateParameters carb_parameters;
1751
1756
1757 // --- Nitrogen Model --- //
1758
1761
1768
1771 std::map<std::string, ShootParameters> shoot_types_snapshot;
1772
1773 // --- Per-plant Attraction Points --- //
1774
1777
1779 std::vector<helios::vec3> attraction_points;
1780
1782 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
1783
1786
1788 float attraction_weight = 0.6f;
1789
1792};
1793
1797 float elastic_modulus = 5e9f;
1799 float wood_density = 800.f;
1801 float damping_ratio = 0.1f;
1803 float static_friction = 0.5f;
1805 float dynamic_friction = 0.3f;
1807 float restitution = 0.1f;
1813 float leaf_mass_per_area = 0.05f;
1815 float fruit_mass = 0.01f;
1817 float flower_mass = 0.002f;
1821 float min_segment_length = 0.001f;
1822};
1823
1825struct GrowthFrameSnapshot;
1826
1830 std::map<uint, std::vector<std::shared_ptr<GrowthFrameSnapshot>>> plant_frames;
1831};
1832
1834public:
1836
1839 explicit PlantArchitecture(helios::Context *context_ptr);
1840
1843
1845 static int selfTest(int argc, char **argv);
1846
1848
1853 void setProgressCallback(std::function<void(float, const std::string&)> callback);
1854
1856
1866 void setCancelFlag(volatile int *flag);
1867
1869
1872 void optionalOutputObjectData(const std::string &object_data_label);
1873
1875
1878 void optionalOutputObjectData(const std::vector<std::string> &object_data_labels);
1879
1880 // ********* Methods for Building Plants from Existing Library ********* //
1881
1883
1886 void loadPlantModelFromLibrary(const std::string &plant_label);
1887
1889 [[nodiscard]] std::vector<std::string> getAvailablePlantModels() const;
1890
1892
1899 uint buildPlantInstanceFromLibrary(const helios::vec3 &base_position, float age, const std::map<std::string, float> &build_parameters = {});
1900
1902
1912 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,
1913 const std::map<std::string, float> &build_parameters = {});
1914
1916
1925 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 = {});
1926
1928
1932 ShootParameters getCurrentShootParameters(const std::string &shoot_type_label);
1933
1935
1938 std::map<std::string, ShootParameters> getCurrentShootParameters();
1939
1941
1945 std::map<std::string, PhytomerParameters> getCurrentPhytomerParameters();
1946
1948
1953 [[nodiscard]] std::vector<std::string> listShootTypeLabels() const;
1954
1956
1961 [[nodiscard]] std::vector<std::string> listShootTypeLabels(const std::string &plant_model_name);
1962
1964
1969 [[nodiscard]] std::vector<std::string> listShootTypeLabels(uint plantID) const;
1970
1972
1977 void updateCurrentShootParameters(const std::string &shoot_type_label, const ShootParameters &params);
1978
1980
1984 void updateCurrentShootParameters(const std::map<std::string, ShootParameters> &params);
1985
1986 // ********* Methods for Building Custom Plant Geometry from Scratch ********* //
1987
1989
1994 uint addPlantInstance(const helios::vec3 &base_position, float current_age);
1995
1997
2004 uint duplicatePlantInstance(uint plantID, const helios::vec3 &base_position, const AxisRotation &base_rotation, float current_age);
2005
2007
2011 void deletePlantInstance(uint plantID);
2012
2014
2017 void deletePlantInstance(const std::vector<uint> &plantIDs);
2018
2020
2032 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,
2033 float max_leaf_lifespan = 1e6, bool is_evergreen = false);
2034
2036
2040 void setPlantCarbohydrateModelParameters(uint plantID, const CarbohydrateParameters &carb_parameters);
2041
2043
2047 void setPlantCarbohydrateModelParameters(const std::vector<uint> &plantIDs, const CarbohydrateParameters &carb_parameters);
2048
2054 void disablePlantPhenology(uint plantID);
2055
2057
2060 void advanceTime(float time_step_days);
2061
2063
2067 void advanceTime(int time_step_years, float time_step_days);
2068
2070
2074 void advanceTime(uint plantID, float time_step_days);
2075
2077
2081 void advanceTime(const std::vector<uint> &plantIDs, float time_step_days);
2082
2084
2092
2094
2102
2104
2110 void adjustPlantMaintenanceRespiration(float Ta);
2111
2112 // -- plant building methods -- //
2113
2115
2119 void defineShootType(const std::string &shoot_type_label, const ShootParameters &shoot_params);
2120
2122
2134 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,
2135 float radius_taper, const std::string &shoot_type_label);
2136
2138
2151 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,
2152 float radius_taper, const std::string &shoot_type_label);
2153
2155
2170 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,
2171 float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label, uint petiole_index = 0);
2172
2174
2188 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,
2189 float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label);
2190
2192
2202 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);
2203
2205
2211 void enableEpicormicChildShoots(uint plantID, const std::string &epicormic_shoot_type_label, float epicormic_probability_perlength_perday);
2212
2215
2218
2221
2223
2226 void enableGroundClipping(float ground_height = 0.f);
2227
2228 // -- collision detection methods -- //
2229
2231
2237 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);
2238
2241
2243
2249 void setSoftCollisionAvoidanceParameters(float view_half_angle_deg, float look_ahead_distance, int sample_count, float inertia_weight);
2250
2252
2255 void setStaticObstacles(const std::vector<uint> &target_UUIDs);
2256
2258
2261 [[nodiscard]] CollisionDetection *getCollisionDetection() const;
2262
2264
2271 void setCollisionRelevantOrgans(bool include_internodes, bool include_leaves, bool include_petioles, bool include_flowers, bool include_fruit);
2272
2274
2286 void enableSolidObstacleAvoidance(const std::vector<uint> &obstacle_UUIDs, float avoidance_distance = 0.5f, bool enable_fruit_adjustment = false, bool enable_obstacle_pruning = false);
2287
2289
2293 void setGeometryUpdateScheduling(int update_frequency = 3, bool force_update_on_collision = true);
2294
2295 // -- attraction points methods -- //
2296
2298
2304 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);
2305
2308
2310
2313 void updateAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2314
2316
2319 void appendAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2320
2322
2328 void setAttractionParameters(float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2329
2331
2338 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);
2339
2341
2344 void disableAttractionPoints(uint plantID);
2345
2347
2351 void updateAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2352
2354
2358 void appendAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2359
2361
2368 void setAttractionParameters(uint plantID, float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2369
2384 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;
2385
2387 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;
2388
2389 // -- methods for modifying the current plant state -- //
2390
2395 void initializeCarbohydratePool(float carbohydrate_concentration_molC_m3) const;
2396
2403 void initializePlantCarbohydratePool(uint plantID, float carbohydrate_concentration_molC_m3);
2404
2413 void initializeShootCarbohydratePool(uint plantID, uint shootID, float carbohydrate_concentration_molC_m3);
2414
2423 void setPhytomerLeafScale(uint plantID, uint shootID, uint node_number, float leaf_scale_factor_fraction);
2424
2430 void setPlantBasePosition(uint plantID, const helios::vec3 &base_position);
2431
2444 void setPlantLeafElevationAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination) const;
2445
2458 void setPlantLeafElevationAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination) const;
2459
2472 void setPlantLeafAzimuthAngleDistribution(uint plantID, float eccentricity, float ellipse_rotation_degrees) const;
2473
2486 void setPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, float eccentricity, float ellipse_rotation_degrees) const;
2487
2502 void setPlantLeafAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2503
2518 void setPlantLeafAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2519
2521 void setPlantAge(uint plantID, float current_age);
2522
2528 void harvestPlant(uint plantID);
2529
2536 void removeShootLeaves(uint plantID, uint shootID);
2537
2544 void removeShootVegetativeBuds(uint plantID, uint shootID);
2545
2552 void removeShootFloralBuds(uint plantID, uint shootID);
2553
2559 void removePlantLeaves(uint plantID);
2560
2566 void makePlantDormant(uint plantID);
2567
2573 void breakPlantDormancy(uint plantID);
2574
2582 void pruneBranch(uint plantID, uint shootID, uint node_index);
2583
2584 // -- methods for querying information about the plant -- //
2585
2592 [[nodiscard]] std::string getPlantName(uint plantID) const;
2593
2600 [[nodiscard]] float getPlantAge(uint plantID) const;
2601
2609 [[nodiscard]] uint getShootNodeCount(uint plantID, uint shootID) const;
2610
2620 [[nodiscard]] std::vector<uint> getAllShootIDs(uint plantID) const;
2621
2632 [[nodiscard]] const std::shared_ptr<Shoot> &getPlantShoot(uint plantID, uint shootID) const;
2633
2641 [[nodiscard]] float getShootTaper(uint plantID, uint shootID) const;
2642
2649 [[nodiscard]] helios::vec3 getPlantBasePosition(uint plantID) const;
2650
2657 [[nodiscard]] std::vector<helios::vec3> getPlantBasePosition(const std::vector<uint> &plantIDs) const;
2658
2660
2664 [[nodiscard]] float sumPlantLeafArea(uint plantID) const;
2665
2667
2671 [[nodiscard]] float getPlantStemHeight(uint plantID) const;
2672
2674
2678 [[nodiscard]] float getPlantHeight(uint plantID) const;
2679
2681
2687 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2688
2690
2696 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2697
2699
2705 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2706
2708
2714 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2715
2717
2721 [[nodiscard]] uint getPlantLeafCount(uint plantID) const;
2722
2724
2728 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(uint plantID) const;
2729
2731
2735 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(const std::vector<uint> &plantIDs) const;
2736
2738
2742 [[nodiscard]] bool isPlantDormant(uint plantID) const;
2743
2745
2749 [[nodiscard]] std::string determinePhenologyStage(uint plantID) const;
2750
2752
2756 void writePlantMeshVertices(uint plantID, const std::string &filename) const;
2757
2759
2762 [[nodiscard]] std::vector<uint> getAllPlantIDs() const;
2763
2765
2769 [[nodiscard]] std::vector<uint> getAllPlantObjectIDs(uint plantID) const;
2770
2772
2777 [[nodiscard]] std::vector<uint> getAllPlantUUIDs(uint plantID, bool include_hidden = false) const;
2778
2780
2784 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID) const;
2785
2787
2793 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID, const std::string &shoot_type_label) const;
2794
2796
2800 [[nodiscard]] std::vector<uint> getPlantPetioleObjectIDs(uint plantID) const;
2801
2803
2807 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(uint plantID) const;
2808
2810
2814 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(const std::vector<uint> &plantIDs) const;
2815
2817
2821 [[nodiscard]] std::vector<uint> getPlantPeduncleObjectIDs(uint plantID) const;
2822
2824
2828 [[nodiscard]] std::vector<uint> getPlantFlowerObjectIDs(uint plantID) const;
2829
2831
2835 [[nodiscard]] std::vector<uint> getPlantFruitObjectIDs(uint plantID) const;
2836
2838
2842 void updateShootFruitCounts(uint plantID) const;
2843
2844
2846
2851 [[nodiscard]] std::vector<uint> getShootInternodeObjectIDs(uint plantID) const;
2852
2854
2861 [[nodiscard]] std::vector<uint> getPlantCollisionRelevantObjectIDs(uint plantID) const;
2862
2864
2867 [[nodiscard]] std::vector<uint> getAllUUIDs() const;
2868
2870
2873 [[nodiscard]] std::vector<uint> getAllLeafUUIDs() const;
2874
2876
2879 [[nodiscard]] std::vector<uint> getAllInternodeUUIDs() const;
2880
2882
2885 [[nodiscard]] std::vector<uint> getAllPetioleUUIDs() const;
2886
2888
2891 [[nodiscard]] std::vector<uint> getAllPeduncleUUIDs() const;
2892
2894
2897 [[nodiscard]] std::vector<uint> getAllFlowerUUIDs() const;
2898
2900
2903 [[nodiscard]] std::vector<uint> getAllFruitUUIDs() const;
2904
2906
2909 [[nodiscard]] std::vector<uint> getAllObjectIDs() const;
2910
2911 // -- carbohydrate model -- //
2912
2917
2922
2923 // -- Nitrogen Model Methods -- //
2924
2934 void enableNitrogenModel();
2935
2939 void disableNitrogenModel();
2940
2945 [[nodiscard]] bool isNitrogenModelEnabled() const;
2946
2953 void setPlantNitrogenParameters(uint plantID, const NitrogenParameters &params);
2954
2961 void setPlantNitrogenParameters(const std::vector<uint> &plantIDs, const NitrogenParameters &params);
2962
2967 void initializeNitrogenPools(float initial_leaf_N_concentration);
2968
2975 void initializePlantNitrogenPools(uint plantID, float initial_leaf_N_concentration);
2976
2983 void addPlantNitrogen(uint plantID, float amount_gN);
2984
2991 void addPlantNitrogen(const std::vector<uint> &plantIDs, float amount_gN);
2992
2993 // -- manual plant generation from input string -- //
2994
3001 [[nodiscard]] std::string getPlantString(uint plantID) const;
3002
3010 uint generatePlantFromString(const std::string &generation_string, const PhytomerParameters &phytomer_parameters);
3011
3020 uint generatePlantFromString(const std::string &generation_string, const std::map<std::string, PhytomerParameters> &phytomer_parameters);
3021
3030 void writePlantStructureXML(uint plantID, const std::string &filename) const;
3031
3033
3044 void writeQSMCylinderFile(uint plantID, const std::string &filename) const;
3045
3047
3058 void writePlantStructureUSD(uint plantID, const std::string &filename, const USDExportParameters &params = USDExportParameters()) const;
3059
3061
3069 void registerGrowthFrame(uint plantID, float min_segment_length = 0.001f);
3070
3072
3085 void writePlantGrowthUSD(uint plantID, const std::string &filename, float seconds_per_frame = 1.0f) const;
3086
3088
3091 void clearGrowthFrames(uint plantID);
3092
3094
3098 [[nodiscard]] uint getGrowthFrameCount(uint plantID) const;
3099
3111 std::vector<uint> readPlantStructureXML(const std::string &filename, bool quiet = false);
3112
3114 void disableMessages();
3115
3117 void enableMessages();
3118
3120
3138 static std::string resolveTextureFile(const std::string &texture_file);
3139
3140 friend struct Phytomer;
3141 friend struct Shoot;
3142
3143private:
3145
3155 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;
3156
3158 void clearBVHCache() const;
3159
3161 void rebuildBVHForTimestep();
3162
3164
3174 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);
3175
3177
3183 void ensureInflorescencePrototypesInitialized(const PhytomerParameters &params, const std::string &plant_name);
3184
3185protected:
3186 helios::Context *context_ptr;
3187
3188 std::minstd_rand0 *generator = nullptr;
3189
3190 uint plant_count = 0;
3191
3192 std::string current_plant_model;
3193
3194 std::function<void(float, const std::string&)> progress_callback;
3195
3199 volatile int *cancel_flag = nullptr;
3200
3201 // Current build parameters for plant construction (set before calling builder functions)
3202 std::map<std::string, float> current_build_parameters;
3203
3204 // Function pointer maps for plant model registration
3205 std::map<std::string, std::function<void()>> shoot_initializers;
3206 std::map<std::string, std::function<uint(const helios::vec3 &)>> plant_builders;
3207 std::map<std::string, std::string> plant_type_map;
3208
3209 std::map<uint, PlantInstance> plant_instances;
3210
3211 [[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;
3212
3213 std::map<std::string, ShootParameters> shoot_types;
3214
3215 // 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)
3216 // std::map<uint(*)(helios::Context* context_ptr, LeafPrototype* prototype_parameters, int compound_leaf_index),std::vector<std::vector<uint>> > unique_leaf_prototype_objIDs;
3217 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
3218
3219 // Key is the prototype function pointer; value index is the unique flower prototype
3220 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
3221 // Key is the prototype function pointer; value index is the unique flower prototype
3222 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
3223 // Key is the prototype function pointer; value index is the unique fruit prototype
3224 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
3225
3227 [[nodiscard]] std::vector<uint> getAllPrototypeObjectIDs() const;
3228
3230 void deleteAllPrototypes();
3231
3232 bool build_context_geometry_internode = true;
3233 bool build_context_geometry_petiole = true;
3234 bool build_context_geometry_peduncle = true;
3235
3236 float ground_clipping_height = -99999;
3237
3238 void validateShootTypes(ShootParameters &shoot_parameters, const std::map<std::string, ShootParameters> &shoot_types_ref) const;
3239
3241
3247 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");
3248
3250 void initializePlantModelRegistrations();
3251
3252 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);
3253
3254 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);
3255
3256 void parseInternodeArgument(const std::string &internode_argument, float &internode_radius, float &internode_length, PhytomerParameters &phytomer_parameters);
3257
3258 void parsePetioleArgument(const std::string &petiole_argument, PhytomerParameters &phytomer_parameters);
3259
3260 void parseLeafArgument(const std::string &leaf_argument, PhytomerParameters &phytomer_parameters);
3261
3262 void initializeDefaultShoots(const std::string &plant_label);
3263
3264 [[nodiscard]] bool detectGroundCollision(uint objID);
3265
3266 [[nodiscard]] bool detectGroundCollision(const std::vector<uint> &objID) const;
3267
3268 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;
3269
3270 static float interpolateTube(const std::vector<float> &P, float frac);
3271
3272 static helios::vec3 interpolateTube(const std::vector<helios::vec3> &P, float frac);
3273
3275 std::map<std::string, bool> output_object_data;
3276
3277 // --- Plant Growth --- //
3278
3279 void incrementPhytomerInternodeGirth(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3280 void incrementPhytomerInternodeGirth_carb(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3281
3282 void pruneGroundCollisions(uint plantID);
3283
3284 void pruneSolidBoundaryCollisions();
3285
3286 // --- Carbohydrate Model --- //
3287
3288 void accumulateShootPhotosynthesis() const;
3289
3290 void subtractShootMaintenanceCarbon(float dt) const;
3291
3292 void subtractShootGrowthCarbon();
3293
3294 void checkCarbonPool_abortOrgans(float dt);
3295 void checkCarbonPool_adjustPhyllochron(float dt);
3296 void checkCarbonPool_transferCarbon(float dt);
3297
3298 bool carbon_model_enabled = false;
3299
3300 // --- Nitrogen Model --- //
3301
3302 void accumulateLeafNitrogen(float dt);
3303 void remobilizeNitrogen(float dt);
3304 void updateNitrogenStressFactor();
3305 void removeFruitNitrogen();
3306
3307 bool nitrogen_model_enabled = false;
3308
3309 // --- Collision Detection --- //
3310
3312 CollisionDetection *collision_detection_ptr = nullptr;
3313
3315 bool owns_collision_detection = false;
3316
3318 bool collision_detection_enabled = false;
3319
3321 std::vector<uint> collision_target_UUIDs;
3322
3324 std::vector<uint> collision_target_object_IDs;
3325
3327 float collision_cone_half_angle_rad = 80.f * M_PI / 180.f;
3328
3330 float collision_cone_height = 0.1f;
3331
3333 int collision_sample_count = 256;
3334
3336 float collision_inertia_weight = 0.4f;
3337
3339 int geometry_update_frequency = 3;
3340
3342 bool force_update_on_collision = true;
3343
3345 bool collision_include_internodes = false;
3346 bool collision_include_leaves = true;
3347 bool collision_include_petioles = false;
3348 bool collision_include_flowers = false;
3349 bool collision_include_fruit = false;
3350
3352 bool petiole_collision_detection_enabled = false;
3353
3355 bool fruit_collision_detection_enabled = false;
3356
3358 int geometry_update_counter = 0;
3359
3361 mutable bool collision_avoidance_applied = false;
3362
3364 bool spatial_filtering_enabled = false;
3365
3367 float spatial_max_distance = 5.0f;
3368
3370 mutable bool bvh_cached_for_current_growth = false;
3371 mutable std::vector<uint> cached_target_geometry;
3372 mutable std::vector<uint> cached_filtered_geometry;
3373
3375 bool solid_obstacle_avoidance_enabled = false;
3376 std::vector<uint> solid_obstacle_UUIDs;
3377 float solid_obstacle_avoidance_distance = 0.5f;
3378 float solid_obstacle_minimum_distance = 0.05f;
3379 bool solid_obstacle_fruit_adjustment_enabled = false;
3380 bool solid_obstacle_pruning_enabled = false;
3381
3382 // --- Attraction Points --- //
3383
3385 bool attraction_points_enabled = false;
3386
3388 std::vector<helios::vec3> attraction_points;
3389
3391 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
3392
3394 float attraction_cone_height = 0.1f;
3395
3397 float attraction_weight = 0.6f;
3398
3400 float attraction_obstacle_reduction_factor = 0.5f;
3401
3403 bool printmessages = true;
3404
3405 // --- Growth Animation Frame Storage --- //
3406
3408 PlantGrowthAnimationStorage growth_animation_storage;
3409
3410 // --- Plant Library --- //
3411
3412 void initializeAlmondTreeShoots();
3413
3414 uint buildAlmondTree(const helios::vec3 &base_position);
3415
3416 void initializeAlmondTreeAldrichShoots();
3417
3418 uint buildAlmondTreeAldrich(const helios::vec3 &base_position);
3419
3420 void initializeAlmondTreeWoodColonyShoots();
3421
3422 uint buildAlmondTreeWoodColony(const helios::vec3 &base_position);
3423
3424 void initializeAppleTreeShoots();
3425
3426 uint buildAppleTree(const helios::vec3 &base_position);
3427
3428 void initializeAppleFruitingWallShoots();
3429
3430 uint buildAppleFruitingWall(const helios::vec3 &base_position);
3431
3432 void initializeAsparagusShoots();
3433
3434 uint buildAsparagusPlant(const helios::vec3 &base_position);
3435
3436 void initializeBindweedShoots();
3437
3438 uint buildBindweedPlant(const helios::vec3 &base_position);
3439
3440 void initializeBeanShoots();
3441
3442 uint buildBeanPlant(const helios::vec3 &base_position);
3443
3444 void initializeBougainvilleaShoots();
3445
3446 uint buildBougainvilleaPlant(const helios::vec3 &base_position);
3447
3448 void initializeCapsicumShoots();
3449
3450 uint buildCapsicumPlant(const helios::vec3 &base_position);
3451
3452 void initializeCheeseweedShoots();
3453
3454 uint buildCheeseweedPlant(const helios::vec3 &base_position);
3455
3456 void initializeCowpeaShoots();
3457
3458 uint buildCowpeaPlant(const helios::vec3 &base_position);
3459
3460 void initializeGrapevineVSPShoots();
3461
3462 uint buildGrapevineVSP(const helios::vec3 &base_position);
3463
3464 void initializeGrapevineWyeShoots();
3465
3466 uint buildGrapevineWye(const helios::vec3 &base_position);
3467
3468 void initializeGroundCherryWeedShoots();
3469
3470 uint buildGroundCherryWeedPlant(const helios::vec3 &base_position);
3471
3472 void initializeMaizeShoots();
3473
3474 uint buildMaizePlant(const helios::vec3 &base_position);
3475
3476 void initializeOliveTreeShoots();
3477
3478 uint buildOliveTree(const helios::vec3 &base_position);
3479
3480 void initializePistachioTreeShoots();
3481
3482 uint buildPistachioTree(const helios::vec3 &base_position);
3483
3484 void initializePuncturevineShoots();
3485
3486 uint buildPuncturevinePlant(const helios::vec3 &base_position);
3487
3488 void initializeEasternRedbudShoots();
3489
3490 uint buildEasternRedbudPlant(const helios::vec3 &base_position);
3491
3492 void initializeRiceShoots();
3493
3494 uint buildRicePlant(const helios::vec3 &base_position);
3495
3496 void initializeButterLettuceShoots();
3497
3498 uint buildButterLettucePlant(const helios::vec3 &base_position);
3499
3500 void initializeSoybeanShoots();
3501
3502 uint buildSoybeanPlant(const helios::vec3 &base_position);
3503
3504 void initializeSorghumShoots();
3505
3506 uint buildSorghumPlant(const helios::vec3 &base_position);
3507
3508 void initializeStrawberryShoots();
3509
3510 uint buildStrawberryPlant(const helios::vec3 &base_position);
3511
3512 void initializeSugarbeetShoots();
3513
3514 uint buildSugarbeetPlant(const helios::vec3 &base_position);
3515
3516 void initializeTomatoShoots();
3517
3518 uint buildTomatoPlant(const helios::vec3 &base_position);
3519
3520 void initializeCherryTomatoShoots();
3521
3522 uint buildCherryTomatoPlant(const helios::vec3 &base_position);
3523
3524 void initializeWalnutTreeShoots();
3525
3526 uint buildWalnutTree(const helios::vec3 &base_position);
3527
3528 void initializeWheatShoots();
3529
3530 uint buildWheatPlant(const helios::vec3 &base_position);
3531};
3532
3533#include "Assets.h"
3534
3535#endif // PLANT_ARCHITECTURE