1.3.72
 
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 // cumulative age since bud break (first transition out of dormant/active state)
405 float age = 0;
406 //=0 for axillary buds, =1 for terminal buds
407 bool isterminal = false;
408 // For axillary buds: index of the petiole within the internode that this floral bud originates from
409 // For terminal buds: index of the phytomer within the shoot that this floral bud originates from
410 uint parent_index = 0;
411 // Index of the bud within the petiole that this floral bud originates from
412 uint bud_index = 0;
413 // Scaling factor fraction of the fruit (if present), ranging from 0 to 1
414 float current_fruit_scale_factor = 1;
415 float previous_fruit_scale_factor = 0;
416
417
418 helios::vec3 base_position;
419 AxisRotation base_rotation;
420 helios::vec3 bending_axis;
421
422 std::vector<helios::vec3> inflorescence_bases;
423 std::vector<AxisRotation> inflorescence_rotation; // pitch, yaw, roll for each flower/fruit
424 std::vector<float> inflorescence_base_scales; // individual base scale for each flower/fruit (before growth scaling)
425 std::vector<uint> peduncle_objIDs;
426 std::vector<uint> inflorescence_objIDs;
427};
428
430public:
432 explicit LeafPrototype(std::minstd_rand0 *generator);
433
435 LeafPrototype() = default;
436
438 uint (*prototype_function)(helios::Context *, LeafPrototype *prototype_parameters, int compound_leaf_index) = nullptr;
439
441
444 std::string OBJ_model_file;
445
447
450 std::map<int, std::string> leaf_texture_file;
451
452 // Ratio of leaf width to leaf length
453 RandomParameter_float leaf_aspect_ratio;
454
457
458 // Parameters for leaf curvature
463
466
467 // Parameters for leaf wave/wrinkles
472
473 // Parameters for leaf buckling
478
481
484
487
489 bool build_petiolule = false;
490
491 uint unique_prototype_identifier = 0;
492
493 void duplicate(const LeafPrototype &a) {
494 this->leaf_texture_file = a.leaf_texture_file;
495 this->OBJ_model_file = a.OBJ_model_file;
496 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
497 this->midrib_fold_fraction = a.midrib_fold_fraction;
498 this->longitudinal_curvature = a.longitudinal_curvature;
499 this->lateral_curvature = a.lateral_curvature;
500 this->petiole_roll = a.petiole_roll;
501 this->wave_period = a.wave_period;
502 this->wave_amplitude = a.wave_amplitude;
503 this->leaf_buckle_length = a.leaf_buckle_length;
504 this->leaf_buckle_angle = a.leaf_buckle_angle;
505 this->leaf_offset = a.leaf_offset;
506 this->subdivisions = a.subdivisions;
507 this->unique_prototypes = a.unique_prototypes;
508 this->unique_prototype_identifier = a.unique_prototype_identifier;
509 this->build_petiolule = a.build_petiolule;
511 this->generator = a.generator;
512 }
513
516 if (this != &a) {
517 this->leaf_texture_file = a.leaf_texture_file;
518 this->OBJ_model_file = a.OBJ_model_file;
519 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
520 if (a.leaf_aspect_ratio.distribution != "constant")
521 this->leaf_aspect_ratio.resample();
522 this->midrib_fold_fraction = a.midrib_fold_fraction;
523 if (a.midrib_fold_fraction.distribution != "constant")
524 this->midrib_fold_fraction.resample();
525 this->longitudinal_curvature = a.longitudinal_curvature;
526 if (a.longitudinal_curvature.distribution != "constant")
527 this->longitudinal_curvature.resample();
528 this->lateral_curvature = a.lateral_curvature;
529 if (a.lateral_curvature.distribution != "constant")
530 this->lateral_curvature.resample();
531 this->petiole_roll = a.petiole_roll;
532 if (a.petiole_roll.distribution != "constant")
533 this->petiole_roll.resample();
534 this->wave_period = a.wave_period;
535 if (a.wave_period.distribution != "constant")
536 this->wave_period.resample();
537 this->wave_amplitude = a.wave_amplitude;
538 if (a.wave_amplitude.distribution != "constant")
539 this->wave_amplitude.resample();
540 this->leaf_buckle_length = a.leaf_buckle_length;
541 if (a.leaf_buckle_length.distribution != "constant")
542 this->leaf_buckle_length.resample();
543 this->leaf_buckle_angle = a.leaf_buckle_angle;
544 if (a.leaf_buckle_angle.distribution != "constant")
545 this->leaf_buckle_angle.resample();
546 this->leaf_offset = a.leaf_offset;
547 this->subdivisions = a.subdivisions;
548 this->unique_prototypes = a.unique_prototypes;
549 this->unique_prototype_identifier = a.unique_prototype_identifier;
550 this->build_petiolule = a.build_petiolule;
552 this->generator = a.generator;
553 if (this->generator != nullptr) {
554 this->sampleIdentifier();
555 }
556 }
557 return *this;
558 }
559
560 void sampleIdentifier() {
561 assert(generator != nullptr);
562 std::uniform_int_distribution<uint> unif_distribution;
563 this->unique_prototype_identifier = unif_distribution(*generator);
564 }
565
566private:
567 std::minstd_rand0 *generator{};
568};
569
571private:
572 struct InternodeParameters {
573
577 RandomParameter_float phyllotactic_angle;
579 RandomParameter_float radius_initial;
581 RandomParameter_int max_vegetative_buds_per_petiole;
583 RandomParameter_int max_floral_buds_per_petiole;
585 helios::RGBcolor color;
587 std::string image_texture;
589 uint length_segments;
591 uint radial_subdivisions;
592
593 InternodeParameters &operator=(const InternodeParameters &a) {
594 if (this != &a) {
595 this->pitch = a.pitch;
596 if (a.pitch.distribution != "constant")
597 this->pitch.resample();
598 this->phyllotactic_angle = a.phyllotactic_angle;
599 if (a.phyllotactic_angle.distribution != "constant")
600 this->phyllotactic_angle.resample();
601 this->radius_initial = a.radius_initial;
602 if (a.radius_initial.distribution != "constant")
603 this->radius_initial.resample();
604 this->max_vegetative_buds_per_petiole = a.max_vegetative_buds_per_petiole;
605 if (a.max_vegetative_buds_per_petiole.distribution != "constant")
606 this->max_vegetative_buds_per_petiole.resample();
607 this->max_floral_buds_per_petiole = a.max_floral_buds_per_petiole;
608 if (a.max_floral_buds_per_petiole.distribution != "constant")
609 this->max_floral_buds_per_petiole.resample();
610 this->color = a.color;
611 this->image_texture = a.image_texture;
612 this->length_segments = a.length_segments;
613 this->radial_subdivisions = a.radial_subdivisions;
614 }
615 return *this;
616 }
617 };
618
619 struct PetioleParameters {
620
622 uint petioles_per_internode;
630 RandomParameter_float curvature;
634 helios::RGBcolor color;
636 uint length_segments;
638 uint radial_subdivisions;
639
640 PetioleParameters &operator=(const PetioleParameters &a) {
641 if (this != &a) {
642 this->petioles_per_internode = a.petioles_per_internode;
643 this->pitch = a.pitch;
644 if (a.pitch.distribution != "constant")
645 this->pitch.resample();
646 this->radius = a.radius;
647 if (a.radius.distribution != "constant")
648 this->radius.resample();
649 this->length = a.length;
650 if (a.length.distribution != "constant")
651 this->length.resample();
652 this->curvature = a.curvature;
653 if (a.curvature.distribution != "constant")
654 this->curvature.resample();
655 this->taper = a.taper;
656 if (a.taper.distribution != "constant")
657 this->taper.resample();
658 this->color = a.color;
659 this->length_segments = a.length_segments;
660 this->radial_subdivisions = a.radial_subdivisions;
661 }
662 return *this;
663 }
664 };
665
666 struct LeafParameters {
668 RandomParameter_int leaves_per_petiole;
676 RandomParameter_float leaflet_offset;
678 RandomParameter_float leaflet_scale;
680 RandomParameter_float prototype_scale;
682 LeafPrototype prototype;
683
684 LeafParameters &operator=(const LeafParameters &a) {
685 if (this != &a) {
686 this->leaves_per_petiole = a.leaves_per_petiole;
687 if (a.leaves_per_petiole.distribution != "constant")
688 this->leaves_per_petiole.resample();
689 this->pitch = a.pitch;
690 if (a.pitch.distribution != "constant")
691 this->pitch.resample();
692 this->yaw = a.yaw;
693 if (a.yaw.distribution != "constant")
694 this->yaw.resample();
695 this->roll = a.roll;
696 if (a.roll.distribution != "constant")
697 this->roll.resample();
698 this->leaflet_offset = a.leaflet_offset;
699 if (a.leaflet_offset.distribution != "constant")
700 this->leaflet_offset.resample();
701 this->leaflet_scale = a.leaflet_scale;
702 if (a.leaflet_scale.distribution != "constant")
703 this->leaflet_scale.resample();
704 this->prototype_scale = a.prototype_scale;
705 if (a.prototype_scale.distribution != "constant")
706 this->prototype_scale.resample();
707 this->prototype.duplicate(a.prototype);
708 }
709 return *this;
710 }
711 };
712
713 struct PeduncleParameters {
723 RandomParameter_float curvature;
725 helios::RGBcolor color;
727 uint length_segments;
729 uint radial_subdivisions;
730
731 PeduncleParameters &operator=(const PeduncleParameters &a) {
732 if (this != &a) {
733 this->length = a.length;
734 if (a.length.distribution != "constant")
735 this->length.resample();
736 this->radius = a.radius;
737 if (a.radius.distribution != "constant")
738 this->radius.resample();
739 this->pitch = a.pitch;
740 if (a.pitch.distribution != "constant")
741 this->pitch.resample();
742 this->roll = a.roll;
743 if (a.roll.distribution != "constant")
744 this->roll.resample();
745 this->curvature = a.curvature;
746 if (a.curvature.distribution != "constant")
747 this->curvature.resample();
748 this->color = a.color;
749 this->length_segments = a.length_segments;
750 this->radial_subdivisions = a.radial_subdivisions;
751 }
752 return *this;
753 }
754 };
755
756 struct InflorescenceParameters {
757
759 RandomParameter_int flowers_per_peduncle;
761 RandomParameter_float flower_offset;
767 RandomParameter_float flower_prototype_scale;
769 uint (*flower_prototype_function)(helios::Context *, uint subdivisions, bool flower_is_open) = nullptr;
771 RandomParameter_float fruit_prototype_scale;
773 uint (*fruit_prototype_function)(helios::Context *, uint subdivisions) = nullptr;
775 RandomParameter_float fruit_gravity_factor_fraction;
777 uint unique_prototypes;
778
779 InflorescenceParameters &operator=(const InflorescenceParameters &a) {
780 if (this != &a) {
781 this->flowers_per_peduncle = a.flowers_per_peduncle;
782 this->flowers_per_peduncle.resample();
783 this->flower_offset = a.flower_offset;
784 this->flower_offset.resample();
785 this->pitch = a.pitch;
786 this->pitch.resample();
787 this->roll = a.roll;
788 this->roll.resample();
789 this->flower_prototype_scale = a.flower_prototype_scale;
790 this->flower_prototype_scale.resample();
791 this->flower_prototype_function = a.flower_prototype_function;
792 this->fruit_prototype_scale = a.fruit_prototype_scale;
793 this->fruit_prototype_scale.resample();
794 this->fruit_prototype_function = a.fruit_prototype_function;
795 this->fruit_gravity_factor_fraction = a.fruit_gravity_factor_fraction;
796 this->fruit_gravity_factor_fraction.resample();
797 this->unique_prototypes = a.unique_prototypes;
798 }
799 return *this;
800 }
801 };
802
803public:
811 InternodeParameters internode;
812
819 PetioleParameters petiole;
820
828 LeafParameters leaf;
829
836 PeduncleParameters peduncle;
837
846 InflorescenceParameters inflorescence;
847
848 // Custom user-defined function that is called when a phytomer is created
856 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;
857
858 // Custom user-defined function that is called for each phytomer on every time step
862 void (*phytomer_callback_function)(std::shared_ptr<Phytomer> phytomer_ptr) = nullptr;
863
864
867
869 explicit PhytomerParameters(std::minstd_rand0 *generator);
870
871 friend class PlantArchitecture;
872 friend struct Phytomer;
873 friend struct Shoot;
874};
875
877
880
882 explicit ShootParameters(std::minstd_rand0 *generator);
883
890
891 // ---- Geometric Parameters ---- //
892
918
919 // --- Growth Parameters --- //
920
928
945
946 // ---- Custom Functions ---- //
947
956 void defineChildShootTypes(const std::vector<std::string> &child_shoot_type_labels, const std::vector<float> &child_shoot_type_probabilities);
957
958 ShootParameters &operator=(const ShootParameters &a) {
959 this->phytomer_parameters = a.phytomer_parameters;
960 this->max_nodes = a.max_nodes;
961 this->max_nodes.resample();
962 this->max_nodes_per_season = a.max_nodes_per_season;
963 this->max_nodes_per_season.resample();
964 this->phyllochron_min = a.phyllochron_min;
965 this->phyllochron_min.resample();
966 this->girth_area_factor = a.girth_area_factor;
967 this->girth_area_factor.resample();
968 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
969 this->vegetative_bud_break_probability_min.resample();
970 this->vegetative_bud_break_probability_max = a.vegetative_bud_break_probability_max;
971 this->vegetative_bud_break_probability_max.resample();
972 this->flower_bud_break_probability = a.flower_bud_break_probability;
973 this->flower_bud_break_probability.resample();
974 this->fruit_set_probability = a.fruit_set_probability;
975 this->fruit_set_probability.resample();
976 this->gravitropic_curvature = a.gravitropic_curvature;
977 this->gravitropic_curvature.resample();
978 this->tortuosity = a.tortuosity;
979 this->tortuosity.resample();
980 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
981 this->vegetative_bud_break_probability_min.resample();
982 this->vegetative_bud_break_probability_decay_rate = a.vegetative_bud_break_probability_decay_rate;
983 this->vegetative_bud_break_probability_decay_rate.resample();
984 this->max_terminal_floral_buds = a.max_terminal_floral_buds;
985 this->max_terminal_floral_buds.resample();
986 this->flower_bud_break_probability = a.flower_bud_break_probability;
987 this->flower_bud_break_probability.resample();
988 this->fruit_set_probability = a.fruit_set_probability;
989 this->fruit_set_probability.resample();
990 this->vegetative_bud_break_time = a.vegetative_bud_break_time;
991 this->vegetative_bud_break_time.resample();
992 this->insertion_angle_tip = a.insertion_angle_tip;
993 this->insertion_angle_tip.resample();
994 this->insertion_angle_decay_rate = a.insertion_angle_decay_rate;
995 this->insertion_angle_decay_rate.resample();
996 this->internode_length_max = a.internode_length_max;
997 this->internode_length_max.resample();
998 this->internode_length_min = a.internode_length_min;
999 this->internode_length_min.resample();
1000 this->internode_length_decay_rate = a.internode_length_decay_rate;
1001 this->internode_length_decay_rate.resample();
1002 this->base_roll = a.base_roll;
1003 this->base_roll.resample();
1004 this->base_yaw = a.base_yaw;
1005 this->base_yaw.resample();
1006 this->flowers_require_dormancy = a.flowers_require_dormancy;
1007 this->growth_requires_dormancy = a.growth_requires_dormancy;
1008 this->child_shoot_type_labels = a.child_shoot_type_labels;
1009 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1010 this->determinate_shoot_growth = a.determinate_shoot_growth;
1011 this->child_shoot_type_labels = a.child_shoot_type_labels;
1012 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
1013 return *this;
1014 }
1015
1016 friend class PlantArchitecture;
1017 friend struct Shoot;
1018
1019protected:
1020 std::vector<std::string> child_shoot_type_labels;
1021 std::vector<float> child_shoot_type_probabilities;
1022};
1023
1024struct Phytomer {
1025public:
1027 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,
1028 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);
1029
1030 // ---- query info about the phytomer ---- //
1031
1037 [[nodiscard]] std::vector<helios::vec3> getInternodeNodePositions() const;
1038
1044 [[nodiscard]] std::vector<float> getInternodeNodeRadii() const;
1045
1052 [[nodiscard]] float calculatePhytomerVolume(uint node_number) const;
1053
1060 [[nodiscard]] helios::vec3 getInternodeAxisVector(float stem_fraction) const;
1061
1069 [[nodiscard]] helios::vec3 getPetioleAxisVector(float stem_fraction, uint petiole_index) const;
1070
1079 [[nodiscard]] helios::vec3 getPeduncleAxisVector(float stem_fraction, uint petiole_index, uint bud_index) const;
1080
1088 [[nodiscard]] static helios::vec3 getAxisVector(float stem_fraction, const std::vector<helios::vec3> &axis_vertices);
1089
1095 [[nodiscard]] float getInternodeLength() const;
1096
1102 [[nodiscard]] float getInternodeRadius() const;
1103
1109 [[nodiscard]] float getPetioleLength() const;
1110
1117 [[nodiscard]] float getInternodeRadius(float stem_fraction) const;
1118
1124 [[nodiscard]] float getLeafArea() const;
1125
1133 [[nodiscard]] helios::vec3 getLeafBasePosition(uint petiole_index, uint leaf_index) const;
1134
1140 [[nodiscard]] bool hasLeaf() const;
1141
1147 [[nodiscard]] float calculateDownstreamLeafArea() const;
1148
1149 // ---- modify the phytomer ---- //
1150
1152
1156 void setInternodeLengthScaleFraction(float internode_scale_factor_fraction, bool update_context_geometry);
1157
1159
1162 void scaleInternodeMaxLength(float scale_factor);
1163
1165
1168 void setInternodeMaxLength(float internode_length_max_new);
1169
1171
1174 void setInternodeMaxRadius(float internode_radius_max_new);
1175
1177
1181 void setLeafScaleFraction(uint petiole_index, float leaf_scale_factor_fraction);
1182
1184
1187 void setLeafScaleFraction(float leaf_scale_factor_fraction);
1188
1190
1194 void setLeafPrototypeScale(uint petiole_index, float leaf_prototype_scale);
1195
1197
1200 void setLeafPrototypeScale(float leaf_prototype_scale);
1201
1203
1208 void scaleLeafPrototypeScale(uint petiole_index, float scale_factor);
1209
1211
1214 void scaleLeafPrototypeScale(float scale_factor);
1215
1217
1224 void scalePetioleGeometry(uint petiole_index, float target_length, float target_base_radius);
1225
1232 void setInflorescenceScaleFraction(FloralBud &fbud, float inflorescence_scale_factor_fraction) const;
1233
1242 void setPetioleBase(const helios::vec3 &base_position);
1243
1254 void rotateLeaf(uint petiole_index, uint leaf_index, const AxisRotation &rotation);
1255
1272 void rotatePetiole(uint petiole_index, const AxisRotation &rotation);
1273
1279 void setVegetativeBudState(BudState state);
1280
1288 void setVegetativeBudState(BudState state, uint petiole_index, uint bud_index);
1289
1296 static void setVegetativeBudState(BudState state, VegetativeBud &vbud);
1297
1303 void setFloralBudState(BudState state);
1304
1312 void setFloralBudState(BudState state, uint petiole_index, uint bud_index);
1313
1320 void setFloralBudState(BudState state, FloralBud &fbud);
1321
1329 void removeLeaf();
1330
1338 void deletePhytomer();
1339
1340private:
1352 helios::vec3 calculateCollisionAvoidanceDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &collision_detection_active) const;
1353
1365 bool applySolidObstacleAvoidance(const helios::vec3 &current_position, helios::vec3 &internode_axis) const;
1366
1379 helios::vec3 calculateAttractionPointDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &attraction_active) const;
1380
1393 helios::vec3 calculatePetioleCollisionAvoidanceDirection(const helios::vec3 &petiole_base_origin, const helios::vec3 &proposed_petiole_axis, bool &collision_detection_active) const;
1394
1396
1402 helios::vec3 calculateFruitCollisionAvoidanceDirection(const helios::vec3 &fruit_base_origin, const helios::vec3 &proposed_fruit_axis, bool &collision_detection_active) const;
1403
1404
1405public:
1406 // ---- phytomer data ---- //
1407
1409 std::vector<std::vector<helios::vec3>> petiole_vertices; // first index is petiole within internode, second index is tube segment within petiole tube
1410 std::vector<std::vector<helios::vec3>> leaf_bases; // first index is petiole within internode, second index is leaf within petiole
1411 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
1412 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
1413 std::vector<std::vector<float>> peduncle_length; // actual sampled length for each peduncle - first index is petiole, second is bud
1414 std::vector<std::vector<float>> peduncle_radius; // actual sampled radius for each peduncle - first index is petiole, second is bud
1415 std::vector<std::vector<float>> peduncle_pitch; // actual sampled pitch for each peduncle - first index is petiole, second is bud
1416 std::vector<std::vector<float>> peduncle_curvature; // actual sampled curvature for each peduncle - first index is petiole, second is bud
1417 float internode_pitch, internode_phyllotactic_angle;
1418
1419 std::vector<std::vector<float>> petiole_radii; // first index is petiole within internode, second index is segment within petiole tube
1420 std::vector<float> petiole_length; // index is petiole within internode
1421 std::vector<float> petiole_pitch; // index is petiole within internode
1422 std::vector<float> petiole_curvature; // index is petiole within internode
1423 std::vector<float> petiole_taper; // taper value for each petiole (tip_radius/base_radius ratio)
1424 std::vector<helios::vec3> petiole_axis_initial; // initial petiole axis (before curvature) for each petiole
1425 std::vector<helios::vec3> petiole_rotation_axis; // rotation axis for curvature application for each petiole
1426 std::vector<std::vector<float>> leaf_size_max; // first index is petiole within internode, second index is leaf within petiole
1427 std::vector<std::vector<AxisRotation>> leaf_rotation; // first index is petiole within internode, second index is leaf within petiole
1428
1429 std::vector<helios::RGBcolor> internode_colors; // index is segment within internode tube
1430 std::vector<helios::RGBcolor> petiole_colors; // index is segment within petiole tube
1431
1432 std::vector<std::vector<uint>> petiole_objIDs; // first index is petiole within internode, second index is segment within petiole tube
1433 std::vector<std::vector<uint>> leaf_objIDs; // first index is petiole within internode, second index is leaf within petiole tube
1434
1435 PhytomerParameters phytomer_parameters;
1436
1437 uint rank;
1440
1441 uint plantID;
1442 uint parent_shoot_ID;
1443 Shoot *parent_shoot_ptr;
1444
1446 float age = 0;
1447 bool isdormant = false;
1448
1449 float current_internode_scale_factor = 1;
1450 std::vector<float> current_leaf_scale_factor; // index is petiole within internode
1451
1452 float old_phytomer_volume = 0;
1453
1454 float downstream_leaf_area = 0;
1455
1456 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds; // first index is petiole within internode, second index is bud within petiole
1457 std::vector<std::vector<FloralBud>> floral_buds; // first index is petiole within internode, second index is bud within petiole
1458
1459 float internode_radius_initial;
1460 float internode_radius_max;
1461 float internode_length_max;
1462
1464 std::vector<float> internode_yaw_perturbations;
1465
1466 bool build_context_geometry_petiole = true;
1467 bool build_context_geometry_peduncle = true;
1468
1469protected:
1470 helios::vec3 inflorescence_bending_axis;
1471
1472 helios::Context *context_ptr;
1473
1474 PlantArchitecture *plantarchitecture_ptr;
1475
1476 void updateInflorescence(FloralBud &fbud);
1477
1494 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);
1495
1501 [[nodiscard]] float calculatePhytomerConstructionCosts() const;
1502
1510 [[nodiscard]] float calculateFlowerConstructionCosts(const FloralBud &fbud) const;
1511
1517 [[nodiscard]] float calculateFruitConstructionCosts(const FloralBud &fbud) const;
1518
1519 friend struct Shoot;
1520 friend class PlantArchitecture;
1521};
1522
1523struct Shoot {
1524
1526 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,
1527 float internode_length_shoot_initial, ShootParameters &shoot_params, std::string shoot_type_label, PlantArchitecture *plant_architecture_ptr);
1528
1530
1537 void buildShootPhytomers(float internode_radius, float internode_length, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper);
1538
1540
1547 int appendPhytomer(float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, const PhytomerParameters &phytomer_parameters);
1548
1550
1553 [[nodiscard]] std::string sampleChildShootType() const;
1554
1556
1560 [[nodiscard]] bool sampleVegetativeBudBreak(uint node_index) const;
1561
1562 [[nodiscard]] bool sampleVegetativeBudBreak_carb(uint node_index) const;
1563
1565
1570 uint sampleEpicormicShoot(float dt, std::vector<float> &epicormic_positions_fraction) const;
1571
1576 void terminateApicalBud();
1577
1583
1591 void addTerminalFloralBud();
1592
1599 [[nodiscard]] float calculateShootInternodeVolume() const;
1600
1606 [[nodiscard]] float calculateShootLength() const;
1607
1614 [[nodiscard]] helios::vec3 getShootAxisVector(float shoot_fraction) const;
1615
1623 [[nodiscard]] float sumShootLeafArea(uint start_node_index = 0) const;
1624
1631 [[nodiscard]] float sumChildVolume(uint start_node_index = 0) const;
1632
1640 void propagateDownstreamLeafArea(const Shoot *shoot, uint node_index, float leaf_area);
1641
1646 void updateShootNodes(bool update_context_geometry = true);
1647
1648 uint current_node_number = 0;
1649 uint nodes_this_season = 0;
1650
1651 helios::vec3 base_position;
1652 AxisRotation base_rotation;
1653 helios::vec3 radial_outward_axis;
1654
1655 const int ID;
1656 const int parent_shoot_ID;
1657 const uint plantID;
1658 const uint parent_node_index;
1659 const uint rank;
1660 const uint parent_petiole_index;
1661
1662 float carbohydrate_pool_molC = 0; // mol C
1663
1665 std::map<uint, float> leaf_nitrogen_gN_m2;
1666
1667 float old_shoot_volume = 0;
1668
1669 float phyllochron_increase = 5;
1670 float phyllochron_recovery = phyllochron_increase;
1671
1672 float days_with_negative_carbon_balance = 0;
1673
1674 void breakDormancy();
1675 void makeDormant();
1676
1677 bool isdormant;
1678 uint dormancy_cycles = 0;
1679
1680 bool meristem_is_alive = true;
1681
1682 float phyllochron_counter = 0;
1683 float phyllochron_min = 6.f;
1684 float elongation_max = 0.25;
1685
1686 float curvature_perturbation = 0;
1687 float yaw_perturbation = 0;
1688
1689 float gravitropic_curvature = 0;
1690
1691 const float internode_length_max_shoot_initial;
1692
1693 uint internode_tube_objID = 4294967294;
1694
1695 std::vector<std::vector<helios::vec3>> shoot_internode_vertices; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1696 std::vector<std::vector<float>> shoot_internode_radii; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1697
1698 bool build_context_geometry_internode = true;
1699
1700 // map of node number (key) to IDs of shoot children (value)
1701 std::map<int, std::vector<int>> childIDs;
1702
1703 ShootParameters shoot_parameters;
1704
1705 std::string shoot_type_label;
1706
1707 float phyllochron_instantaneous;
1708 float elongation_rate_instantaneous;
1709
1710 std::vector<std::shared_ptr<Phytomer>> phytomers;
1711
1712 PlantArchitecture *plantarchitecture_ptr;
1713
1714 helios::Context *context_ptr;
1715};
1716
1718
1719 PlantInstance(const helios::vec3 &a_base_position, float a_current_age, const std::string &a_plant_name, helios::Context *a_context_ptr) :
1720 base_position(a_base_position), current_age(a_current_age), plant_name(a_plant_name), context_ptr(a_context_ptr) {
1721 }
1722 std::vector<std::shared_ptr<Shoot>> shoot_tree;
1723 helios::vec3 base_position;
1724 float current_age;
1725 float time_since_dormancy = 0;
1726 helios::Context *context_ptr;
1727 std::string plant_name;
1728 std::pair<std::string, float> epicormic_shoot_probability_perlength_per_day; //.first is the epicormic shoot label string, .second is the probability
1729
1730 // Phenological thresholds
1731 float dd_to_dormancy_break = 0;
1732 float dd_to_flower_initiation = 0;
1733 float dd_to_flower_opening = 0;
1734 float dd_to_fruit_set = 0;
1735 float dd_to_fruit_maturity = 0;
1736 float dd_to_dormancy = 0;
1737 float max_leaf_lifespan = 1e6;
1738 bool is_evergreen = false;
1739
1740 float max_age = 999;
1741
1742 CarbohydrateParameters carb_parameters;
1743
1744 // --- Nitrogen Model --- //
1745
1748
1755
1758 std::map<std::string, ShootParameters> shoot_types_snapshot;
1759
1760 // --- Per-plant Attraction Points --- //
1761
1764
1766 std::vector<helios::vec3> attraction_points;
1767
1769 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
1770
1773
1775 float attraction_weight = 0.6f;
1776
1779};
1780
1784 float elastic_modulus = 5e9f;
1786 float wood_density = 800.f;
1788 float damping_ratio = 0.1f;
1790 float static_friction = 0.5f;
1792 float dynamic_friction = 0.3f;
1794 float restitution = 0.1f;
1800 float leaf_mass_per_area = 0.05f;
1802 float fruit_mass = 0.01f;
1804 float flower_mass = 0.002f;
1808 float min_segment_length = 0.001f;
1809};
1810
1812struct GrowthFrameSnapshot;
1813
1817 std::map<uint, std::vector<std::shared_ptr<GrowthFrameSnapshot>>> plant_frames;
1818};
1819
1821public:
1823
1826 explicit PlantArchitecture(helios::Context *context_ptr);
1827
1830
1832 static int selfTest(int argc, char **argv);
1833
1835
1840 void setProgressCallback(std::function<void(float, const std::string&)> callback);
1841
1843
1846 void optionalOutputObjectData(const std::string &object_data_label);
1847
1849
1852 void optionalOutputObjectData(const std::vector<std::string> &object_data_labels);
1853
1854 // ********* Methods for Building Plants from Existing Library ********* //
1855
1857
1860 void loadPlantModelFromLibrary(const std::string &plant_label);
1861
1863 [[nodiscard]] std::vector<std::string> getAvailablePlantModels() const;
1864
1866
1873 uint buildPlantInstanceFromLibrary(const helios::vec3 &base_position, float age, const std::map<std::string, float> &build_parameters = {});
1874
1876
1886 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,
1887 const std::map<std::string, float> &build_parameters = {});
1888
1890
1899 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 = {});
1900
1902
1906 ShootParameters getCurrentShootParameters(const std::string &shoot_type_label);
1907
1909
1912 std::map<std::string, ShootParameters> getCurrentShootParameters();
1913
1915
1919 std::map<std::string, PhytomerParameters> getCurrentPhytomerParameters();
1920
1922
1927 [[nodiscard]] std::vector<std::string> listShootTypeLabels() const;
1928
1930
1935 [[nodiscard]] std::vector<std::string> listShootTypeLabels(const std::string &plant_model_name);
1936
1938
1943 [[nodiscard]] std::vector<std::string> listShootTypeLabels(uint plantID) const;
1944
1946
1951 void updateCurrentShootParameters(const std::string &shoot_type_label, const ShootParameters &params);
1952
1954
1958 void updateCurrentShootParameters(const std::map<std::string, ShootParameters> &params);
1959
1960 // ********* Methods for Building Custom Plant Geometry from Scratch ********* //
1961
1963
1968 uint addPlantInstance(const helios::vec3 &base_position, float current_age);
1969
1971
1978 uint duplicatePlantInstance(uint plantID, const helios::vec3 &base_position, const AxisRotation &base_rotation, float current_age);
1979
1981
1985 void deletePlantInstance(uint plantID);
1986
1988
1991 void deletePlantInstance(const std::vector<uint> &plantIDs);
1992
1994
2006 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,
2007 float max_leaf_lifespan = 1e6, bool is_evergreen = false);
2008
2010
2014 void setPlantCarbohydrateModelParameters(uint plantID, const CarbohydrateParameters &carb_parameters);
2015
2017
2021 void setPlantCarbohydrateModelParameters(const std::vector<uint> &plantIDs, const CarbohydrateParameters &carb_parameters);
2022
2028 void disablePlantPhenology(uint plantID);
2029
2031
2034 void advanceTime(float time_step_days);
2035
2037
2041 void advanceTime(int time_step_years, float time_step_days);
2042
2044
2048 void advanceTime(uint plantID, float time_step_days);
2049
2051
2055 void advanceTime(const std::vector<uint> &plantIDs, float time_step_days);
2056
2058
2066
2068
2076
2077 // -- plant building methods -- //
2078
2080
2084 void defineShootType(const std::string &shoot_type_label, const ShootParameters &shoot_params);
2085
2087
2099 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,
2100 float radius_taper, const std::string &shoot_type_label);
2101
2103
2116 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,
2117 float radius_taper, const std::string &shoot_type_label);
2118
2120
2135 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,
2136 float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label, uint petiole_index = 0);
2137
2139
2153 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,
2154 float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label);
2155
2157
2167 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);
2168
2170
2176 void enableEpicormicChildShoots(uint plantID, const std::string &epicormic_shoot_type_label, float epicormic_probability_perlength_perday);
2177
2180
2183
2186
2188
2191 void enableGroundClipping(float ground_height = 0.f);
2192
2193 // -- collision detection methods -- //
2194
2196
2202 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);
2203
2206
2208
2214 void setSoftCollisionAvoidanceParameters(float view_half_angle_deg, float look_ahead_distance, int sample_count, float inertia_weight);
2215
2217
2220 void setStaticObstacles(const std::vector<uint> &target_UUIDs);
2221
2223
2226 [[nodiscard]] CollisionDetection *getCollisionDetection() const;
2227
2229
2236 void setCollisionRelevantOrgans(bool include_internodes, bool include_leaves, bool include_petioles, bool include_flowers, bool include_fruit);
2237
2239
2251 void enableSolidObstacleAvoidance(const std::vector<uint> &obstacle_UUIDs, float avoidance_distance = 0.5f, bool enable_fruit_adjustment = false, bool enable_obstacle_pruning = false);
2252
2254
2258 void setGeometryUpdateScheduling(int update_frequency = 3, bool force_update_on_collision = true);
2259
2260 // -- attraction points methods -- //
2261
2263
2269 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);
2270
2273
2275
2278 void updateAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2279
2281
2284 void appendAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2285
2287
2293 void setAttractionParameters(float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2294
2296
2303 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);
2304
2306
2309 void disableAttractionPoints(uint plantID);
2310
2312
2316 void updateAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2317
2319
2323 void appendAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2324
2326
2333 void setAttractionParameters(uint plantID, float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2334
2349 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;
2350
2352 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;
2353
2354 // -- methods for modifying the current plant state -- //
2355
2360 void initializeCarbohydratePool(float carbohydrate_concentration_molC_m3) const;
2361
2368 void initializePlantCarbohydratePool(uint plantID, float carbohydrate_concentration_molC_m3);
2369
2378 void initializeShootCarbohydratePool(uint plantID, uint shootID, float carbohydrate_concentration_molC_m3);
2379
2388 void setPhytomerLeafScale(uint plantID, uint shootID, uint node_number, float leaf_scale_factor_fraction);
2389
2395 void setPlantBasePosition(uint plantID, const helios::vec3 &base_position);
2396
2409 void setPlantLeafElevationAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination) const;
2410
2423 void setPlantLeafElevationAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination) const;
2424
2437 void setPlantLeafAzimuthAngleDistribution(uint plantID, float eccentricity, float ellipse_rotation_degrees) const;
2438
2451 void setPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, float eccentricity, float ellipse_rotation_degrees) const;
2452
2467 void setPlantLeafAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2468
2483 void setPlantLeafAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2484
2486 void setPlantAge(uint plantID, float current_age);
2487
2493 void harvestPlant(uint plantID);
2494
2501 void removeShootLeaves(uint plantID, uint shootID);
2502
2509 void removeShootVegetativeBuds(uint plantID, uint shootID);
2510
2517 void removeShootFloralBuds(uint plantID, uint shootID);
2518
2524 void removePlantLeaves(uint plantID);
2525
2531 void makePlantDormant(uint plantID);
2532
2538 void breakPlantDormancy(uint plantID);
2539
2547 void pruneBranch(uint plantID, uint shootID, uint node_index);
2548
2549 // -- methods for querying information about the plant -- //
2550
2557 [[nodiscard]] std::string getPlantName(uint plantID) const;
2558
2565 [[nodiscard]] float getPlantAge(uint plantID) const;
2566
2574 [[nodiscard]] uint getShootNodeCount(uint plantID, uint shootID) const;
2575
2583 [[nodiscard]] float getShootTaper(uint plantID, uint shootID) const;
2584
2591 [[nodiscard]] helios::vec3 getPlantBasePosition(uint plantID) const;
2592
2599 [[nodiscard]] std::vector<helios::vec3> getPlantBasePosition(const std::vector<uint> &plantIDs) const;
2600
2602
2606 [[nodiscard]] float sumPlantLeafArea(uint plantID) const;
2607
2609
2613 [[nodiscard]] float getPlantStemHeight(uint plantID) const;
2614
2616
2620 [[nodiscard]] float getPlantHeight(uint plantID) const;
2621
2623
2629 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2630
2632
2638 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2639
2641
2647 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2648
2650
2656 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2657
2659
2663 [[nodiscard]] uint getPlantLeafCount(uint plantID) const;
2664
2666
2670 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(uint plantID) const;
2671
2673
2677 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(const std::vector<uint> &plantIDs) const;
2678
2680
2684 [[nodiscard]] bool isPlantDormant(uint plantID) const;
2685
2687
2691 [[nodiscard]] std::string determinePhenologyStage(uint plantID) const;
2692
2694
2698 void writePlantMeshVertices(uint plantID, const std::string &filename) const;
2699
2701
2704 [[nodiscard]] std::vector<uint> getAllPlantIDs() const;
2705
2707
2711 [[nodiscard]] std::vector<uint> getAllPlantObjectIDs(uint plantID) const;
2712
2714
2719 [[nodiscard]] std::vector<uint> getAllPlantUUIDs(uint plantID, bool include_hidden = false) const;
2720
2722
2726 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID) const;
2727
2729
2735 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID, const std::string &shoot_type_label) const;
2736
2738
2742 [[nodiscard]] std::vector<uint> getPlantPetioleObjectIDs(uint plantID) const;
2743
2745
2749 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(uint plantID) const;
2750
2752
2756 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(const std::vector<uint> &plantIDs) const;
2757
2759
2763 [[nodiscard]] std::vector<uint> getPlantPeduncleObjectIDs(uint plantID) const;
2764
2766
2770 [[nodiscard]] std::vector<uint> getPlantFlowerObjectIDs(uint plantID) const;
2771
2773
2777 [[nodiscard]] std::vector<uint> getPlantFruitObjectIDs(uint plantID) const;
2778
2780
2784 void updateShootFruitCounts(uint plantID) const;
2785
2786
2788
2793 [[nodiscard]] std::vector<uint> getShootInternodeObjectIDs(uint plantID) const;
2794
2796
2803 [[nodiscard]] std::vector<uint> getPlantCollisionRelevantObjectIDs(uint plantID) const;
2804
2806
2809 [[nodiscard]] std::vector<uint> getAllUUIDs() const;
2810
2812
2815 [[nodiscard]] std::vector<uint> getAllLeafUUIDs() const;
2816
2818
2821 [[nodiscard]] std::vector<uint> getAllInternodeUUIDs() const;
2822
2824
2827 [[nodiscard]] std::vector<uint> getAllPetioleUUIDs() const;
2828
2830
2833 [[nodiscard]] std::vector<uint> getAllPeduncleUUIDs() const;
2834
2836
2839 [[nodiscard]] std::vector<uint> getAllFlowerUUIDs() const;
2840
2842
2845 [[nodiscard]] std::vector<uint> getAllFruitUUIDs() const;
2846
2848
2851 [[nodiscard]] std::vector<uint> getAllObjectIDs() const;
2852
2853 // -- carbohydrate model -- //
2854
2859
2864
2865 // -- Nitrogen Model Methods -- //
2866
2876 void enableNitrogenModel();
2877
2881 void disableNitrogenModel();
2882
2887 [[nodiscard]] bool isNitrogenModelEnabled() const;
2888
2895 void setPlantNitrogenParameters(uint plantID, const NitrogenParameters &params);
2896
2903 void setPlantNitrogenParameters(const std::vector<uint> &plantIDs, const NitrogenParameters &params);
2904
2909 void initializeNitrogenPools(float initial_leaf_N_concentration);
2910
2917 void initializePlantNitrogenPools(uint plantID, float initial_leaf_N_concentration);
2918
2925 void addPlantNitrogen(uint plantID, float amount_gN);
2926
2933 void addPlantNitrogen(const std::vector<uint> &plantIDs, float amount_gN);
2934
2935 // -- manual plant generation from input string -- //
2936
2943 [[nodiscard]] std::string getPlantString(uint plantID) const;
2944
2952 uint generatePlantFromString(const std::string &generation_string, const PhytomerParameters &phytomer_parameters);
2953
2962 uint generatePlantFromString(const std::string &generation_string, const std::map<std::string, PhytomerParameters> &phytomer_parameters);
2963
2972 void writePlantStructureXML(uint plantID, const std::string &filename) const;
2973
2975
2986 void writeQSMCylinderFile(uint plantID, const std::string &filename) const;
2987
2989
3000 void writePlantStructureUSD(uint plantID, const std::string &filename, const USDExportParameters &params = USDExportParameters()) const;
3001
3003
3011 void registerGrowthFrame(uint plantID, float min_segment_length = 0.001f);
3012
3014
3027 void writePlantGrowthUSD(uint plantID, const std::string &filename, float seconds_per_frame = 1.0f) const;
3028
3030
3033 void clearGrowthFrames(uint plantID);
3034
3036
3040 [[nodiscard]] uint getGrowthFrameCount(uint plantID) const;
3041
3053 std::vector<uint> readPlantStructureXML(const std::string &filename, bool quiet = false);
3054
3056 void disableMessages();
3057
3059 void enableMessages();
3060
3062
3080 static std::string resolveTextureFile(const std::string &texture_file);
3081
3082 friend struct Phytomer;
3083 friend struct Shoot;
3084
3085private:
3087
3097 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;
3098
3100 void clearBVHCache() const;
3101
3103 void rebuildBVHForTimestep();
3104
3106
3116 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);
3117
3119
3125 void ensureInflorescencePrototypesInitialized(const PhytomerParameters &params, const std::string &plant_name);
3126
3127protected:
3128 helios::Context *context_ptr;
3129
3130 std::minstd_rand0 *generator = nullptr;
3131
3132 uint plant_count = 0;
3133
3134 std::string current_plant_model;
3135
3136 std::function<void(float, const std::string&)> progress_callback;
3137
3138 // Current build parameters for plant construction (set before calling builder functions)
3139 std::map<std::string, float> current_build_parameters;
3140
3141 // Function pointer maps for plant model registration
3142 std::map<std::string, std::function<void()>> shoot_initializers;
3143 std::map<std::string, std::function<uint(const helios::vec3 &)>> plant_builders;
3144 std::map<std::string, std::string> plant_type_map;
3145
3146 std::map<uint, PlantInstance> plant_instances;
3147
3148 [[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;
3149
3150 std::map<std::string, ShootParameters> shoot_types;
3151
3152 // 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)
3153 // std::map<uint(*)(helios::Context* context_ptr, LeafPrototype* prototype_parameters, int compound_leaf_index),std::vector<std::vector<uint>> > unique_leaf_prototype_objIDs;
3154 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
3155
3156 // Key is the prototype function pointer; value index is the unique flower prototype
3157 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
3158 // Key is the prototype function pointer; value index is the unique flower prototype
3159 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
3160 // Key is the prototype function pointer; value index is the unique fruit prototype
3161 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
3162
3164 [[nodiscard]] std::vector<uint> getAllPrototypeObjectIDs() const;
3165
3167 void deleteAllPrototypes();
3168
3169 bool build_context_geometry_internode = true;
3170 bool build_context_geometry_petiole = true;
3171 bool build_context_geometry_peduncle = true;
3172
3173 float ground_clipping_height = -99999;
3174
3175 void validateShootTypes(ShootParameters &shoot_parameters, const std::map<std::string, ShootParameters> &shoot_types_ref) const;
3176
3178 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");
3179
3181 void initializePlantModelRegistrations();
3182
3183 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);
3184
3185 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);
3186
3187 void parseInternodeArgument(const std::string &internode_argument, float &internode_radius, float &internode_length, PhytomerParameters &phytomer_parameters);
3188
3189 void parsePetioleArgument(const std::string &petiole_argument, PhytomerParameters &phytomer_parameters);
3190
3191 void parseLeafArgument(const std::string &leaf_argument, PhytomerParameters &phytomer_parameters);
3192
3193 void initializeDefaultShoots(const std::string &plant_label);
3194
3195 [[nodiscard]] bool detectGroundCollision(uint objID);
3196
3197 [[nodiscard]] bool detectGroundCollision(const std::vector<uint> &objID) const;
3198
3199 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;
3200
3201 static float interpolateTube(const std::vector<float> &P, float frac);
3202
3203 static helios::vec3 interpolateTube(const std::vector<helios::vec3> &P, float frac);
3204
3206 std::map<std::string, bool> output_object_data;
3207
3208 // --- Plant Growth --- //
3209
3210 void incrementPhytomerInternodeGirth(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3211 void incrementPhytomerInternodeGirth_carb(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
3212
3213 void pruneGroundCollisions(uint plantID);
3214
3215 void pruneSolidBoundaryCollisions();
3216
3217 // --- Carbohydrate Model --- //
3218
3219 void accumulateShootPhotosynthesis() const;
3220
3221 void subtractShootMaintenanceCarbon(float dt) const;
3222 void subtractShootGrowthCarbon();
3223
3224 void checkCarbonPool_abortOrgans(float dt);
3225 void checkCarbonPool_adjustPhyllochron(float dt);
3226 void checkCarbonPool_transferCarbon(float dt);
3227
3228 bool carbon_model_enabled = false;
3229
3230 // --- Nitrogen Model --- //
3231
3232 void accumulateLeafNitrogen(float dt);
3233 void remobilizeNitrogen(float dt);
3234 void updateNitrogenStressFactor();
3235 void removeFruitNitrogen();
3236
3237 bool nitrogen_model_enabled = false;
3238
3239 // --- Collision Detection --- //
3240
3242 CollisionDetection *collision_detection_ptr = nullptr;
3243
3245 bool owns_collision_detection = false;
3246
3248 bool collision_detection_enabled = false;
3249
3251 std::vector<uint> collision_target_UUIDs;
3252
3254 std::vector<uint> collision_target_object_IDs;
3255
3257 float collision_cone_half_angle_rad = 80.f * M_PI / 180.f;
3258
3260 float collision_cone_height = 0.1f;
3261
3263 int collision_sample_count = 256;
3264
3266 float collision_inertia_weight = 0.4f;
3267
3269 int geometry_update_frequency = 3;
3270
3272 bool force_update_on_collision = true;
3273
3275 bool collision_include_internodes = false;
3276 bool collision_include_leaves = true;
3277 bool collision_include_petioles = false;
3278 bool collision_include_flowers = false;
3279 bool collision_include_fruit = false;
3280
3282 bool petiole_collision_detection_enabled = false;
3283
3285 bool fruit_collision_detection_enabled = false;
3286
3288 int geometry_update_counter = 0;
3289
3291 mutable bool collision_avoidance_applied = false;
3292
3294 bool spatial_filtering_enabled = false;
3295
3297 float spatial_max_distance = 5.0f;
3298
3300 mutable bool bvh_cached_for_current_growth = false;
3301 mutable std::vector<uint> cached_target_geometry;
3302 mutable std::vector<uint> cached_filtered_geometry;
3303
3305 bool solid_obstacle_avoidance_enabled = false;
3306 std::vector<uint> solid_obstacle_UUIDs;
3307 float solid_obstacle_avoidance_distance = 0.5f;
3308 float solid_obstacle_minimum_distance = 0.05f;
3309 bool solid_obstacle_fruit_adjustment_enabled = false;
3310 bool solid_obstacle_pruning_enabled = false;
3311
3312 // --- Attraction Points --- //
3313
3315 bool attraction_points_enabled = false;
3316
3318 std::vector<helios::vec3> attraction_points;
3319
3321 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
3322
3324 float attraction_cone_height = 0.1f;
3325
3327 float attraction_weight = 0.6f;
3328
3330 float attraction_obstacle_reduction_factor = 0.5f;
3331
3333 bool printmessages = true;
3334
3335 // --- Growth Animation Frame Storage --- //
3336
3338 PlantGrowthAnimationStorage growth_animation_storage;
3339
3340 // --- Plant Library --- //
3341
3342 void initializeAlmondTreeShoots();
3343
3344 uint buildAlmondTree(const helios::vec3 &base_position);
3345
3346 void initializeAlmondTreeAldrichShoots();
3347
3348 uint buildAlmondTreeAldrich(const helios::vec3 &base_position);
3349
3350 void initializeAlmondTreeWoodColonyShoots();
3351
3352 uint buildAlmondTreeWoodColony(const helios::vec3 &base_position);
3353
3354 void initializeAppleTreeShoots();
3355
3356 uint buildAppleTree(const helios::vec3 &base_position);
3357
3358 void initializeAppleFruitingWallShoots();
3359
3360 uint buildAppleFruitingWall(const helios::vec3 &base_position);
3361
3362 void initializeAsparagusShoots();
3363
3364 uint buildAsparagusPlant(const helios::vec3 &base_position);
3365
3366 void initializeBindweedShoots();
3367
3368 uint buildBindweedPlant(const helios::vec3 &base_position);
3369
3370 void initializeBeanShoots();
3371
3372 uint buildBeanPlant(const helios::vec3 &base_position);
3373
3374 void initializeBougainvilleaShoots();
3375
3376 uint buildBougainvilleaPlant(const helios::vec3 &base_position);
3377
3378 void initializeCapsicumShoots();
3379
3380 uint buildCapsicumPlant(const helios::vec3 &base_position);
3381
3382 void initializeCheeseweedShoots();
3383
3384 uint buildCheeseweedPlant(const helios::vec3 &base_position);
3385
3386 void initializeCowpeaShoots();
3387
3388 uint buildCowpeaPlant(const helios::vec3 &base_position);
3389
3390 void initializeGrapevineVSPShoots();
3391
3392 uint buildGrapevineVSP(const helios::vec3 &base_position);
3393
3394 void initializeGrapevineWyeShoots();
3395
3396 uint buildGrapevineWye(const helios::vec3 &base_position);
3397
3398 void initializeGroundCherryWeedShoots();
3399
3400 uint buildGroundCherryWeedPlant(const helios::vec3 &base_position);
3401
3402 void initializeMaizeShoots();
3403
3404 uint buildMaizePlant(const helios::vec3 &base_position);
3405
3406 void initializeOliveTreeShoots();
3407
3408 uint buildOliveTree(const helios::vec3 &base_position);
3409
3410 void initializePistachioTreeShoots();
3411
3412 uint buildPistachioTree(const helios::vec3 &base_position);
3413
3414 void initializePuncturevineShoots();
3415
3416 uint buildPuncturevinePlant(const helios::vec3 &base_position);
3417
3418 void initializeEasternRedbudShoots();
3419
3420 uint buildEasternRedbudPlant(const helios::vec3 &base_position);
3421
3422 void initializeRiceShoots();
3423
3424 uint buildRicePlant(const helios::vec3 &base_position);
3425
3426 void initializeButterLettuceShoots();
3427
3428 uint buildButterLettucePlant(const helios::vec3 &base_position);
3429
3430 void initializeSoybeanShoots();
3431
3432 uint buildSoybeanPlant(const helios::vec3 &base_position);
3433
3434 void initializeSorghumShoots();
3435
3436 uint buildSorghumPlant(const helios::vec3 &base_position);
3437
3438 void initializeStrawberryShoots();
3439
3440 uint buildStrawberryPlant(const helios::vec3 &base_position);
3441
3442 void initializeSugarbeetShoots();
3443
3444 uint buildSugarbeetPlant(const helios::vec3 &base_position);
3445
3446 void initializeTomatoShoots();
3447
3448 uint buildTomatoPlant(const helios::vec3 &base_position);
3449
3450 void initializeCherryTomatoShoots();
3451
3452 uint buildCherryTomatoPlant(const helios::vec3 &base_position);
3453
3454 void initializeWalnutTreeShoots();
3455
3456 uint buildWalnutTree(const helios::vec3 &base_position);
3457
3458 void initializeWheatShoots();
3459
3460 uint buildWheatPlant(const helios::vec3 &base_position);
3461};
3462
3463#include "Assets.h"
3464
3465#endif // PLANT_ARCHITECTURE