1.3.49
 
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
219private:
220 bool sampled;
221 int constval;
222 std::string distribution;
223 std::vector<int> distribution_parameters;
224 std::minstd_rand0 *generator;
225};
226
228public:
229 AxisRotation() {
230 pitch = 0;
231 yaw = 0;
232 roll = 0;
233 }
234
235 AxisRotation(float a_pitch, float a_yaw, float a_roll) {
236 pitch = a_pitch;
237 yaw = a_yaw;
238 roll = a_roll;
239 }
240
241 float pitch;
242 float yaw;
243 float roll;
244
245 AxisRotation operator+(const AxisRotation &a) const;
246 AxisRotation operator-(const AxisRotation &a) const;
247
248 friend std::ostream &operator<<(std::ostream &os, const AxisRotation &rot) {
249 return os << "AxisRotation<" << rot.pitch << ", " << rot.yaw << ", " << rot.roll << ">";
250 }
251};
252
253inline AxisRotation make_AxisRotation(float a_pitch, float a_yaw, float a_roll) {
254 return {a_pitch, a_yaw, a_roll};
255}
256
257inline AxisRotation AxisRotation::operator+(const AxisRotation &a) const {
258 return {a.pitch + pitch, a.yaw + yaw, a.roll + roll};
259}
260
261inline AxisRotation AxisRotation::operator-(const AxisRotation &a) const {
262 return {a.pitch - pitch, a.yaw - yaw, a.roll - roll};
263}
264
265enum BudState { BUD_DORMANT = 0, BUD_ACTIVE = 1, BUD_FLOWER_CLOSED = 2, BUD_FLOWER_OPEN = 3, BUD_FRUITING = 4, BUD_DEAD = 5 };
266
268
269 // -- Stem Growth Parameters -- //
271 float stem_density = 540000;
275 float maturity_age = 180;
279 float shoot_root_ratio = 4.5;
280
281 // -- Leaf Growth Parameters -- //
283 float SLA = 2.5e-2;
286
287 // -- Flower Growth Parameters -- //
289 float total_flower_cost = 8.33e-4;
290
291 // -- Fruit Growth Parameters -- //
293 float fruit_density = 525000;
296
297 // -- Respiration Parameters -- //
304
305 // -- Organ Abortion Thresholds -- //
314
315 // -- Phyllochron Adjustment Parameters -- //
320
323
324 // -- Carbon Transfer Parameters -- //
327 float carbon_conductance_down = 0.9; //<= 1.0
328 float carbon_conductance_up = carbon_conductance_down / 5; // Conductance of carbon from parent to child shoots << conductance from child to parent
329};
330
332
340std::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);
341
343
344 // state of the bud
345 BudState state = BUD_DORMANT;
346 // label of the shoot type that will be produced if the bud breaks into a shoot
347 std::string shoot_type_label;
348 // ID of the shoot that the bud will produce if it breaks into a shoot
349 uint shoot_ID = -1;
350};
351
352struct FloralBud {
353
354 // state of the bud
355 BudState state = BUD_DORMANT;
356 // amount of time since the bud flowered (=0 if it has not yet flowered)
357 float time_counter = 0;
358 //=0 for axillary buds, =1 for terminal buds
359 bool isterminal = false;
360 // For axillary buds: index of the petiole within the internode that this floral bud originates from
361 // For terminal buds: index of the phytomer within the shoot that this floral bud originates from
362 uint parent_index = 0;
363 // Index of the bud within the petiole that this floral bud originates from
364 uint bud_index = 0;
365 // Scaling factor fraction of the fruit (if present), ranging from 0 to 1
366 float current_fruit_scale_factor = 1;
367 float previous_fruit_scale_factor = 0;
368
369
370 helios::vec3 base_position;
371 AxisRotation base_rotation;
372 helios::vec3 bending_axis;
373
374 std::vector<helios::vec3> inflorescence_bases;
375 std::vector<uint> peduncle_objIDs;
376 std::vector<uint> inflorescence_objIDs;
377};
378
380public:
382 explicit LeafPrototype(std::minstd_rand0 *generator);
383
385 LeafPrototype() = default;
386
388 uint (*prototype_function)(helios::Context *, LeafPrototype *prototype_parameters, int compound_leaf_index) = nullptr;
389
391
394 std::string OBJ_model_file;
395
397
400 std::map<int, std::string> leaf_texture_file;
401
402 // Ratio of leaf width to leaf length
403 RandomParameter_float leaf_aspect_ratio;
404
407
408 // Parameters for leaf curvature
413
416
417 // Parameters for leaf wave/wrinkles
422
423 // Parameters for leaf buckling
428
431
434
437
439 bool build_petiolule = false;
440
441 uint unique_prototype_identifier = 0;
442
443 void duplicate(const LeafPrototype &a) {
444 this->leaf_texture_file = a.leaf_texture_file;
445 this->OBJ_model_file = a.OBJ_model_file;
446 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
447 this->midrib_fold_fraction = a.midrib_fold_fraction;
448 this->longitudinal_curvature = a.longitudinal_curvature;
449 this->lateral_curvature = a.lateral_curvature;
450 this->petiole_roll = a.petiole_roll;
451 this->wave_period = a.wave_period;
452 this->wave_amplitude = a.wave_amplitude;
453 this->leaf_buckle_length = a.leaf_buckle_length;
454 this->leaf_buckle_angle = a.leaf_buckle_angle;
455 this->leaf_offset = a.leaf_offset;
456 this->subdivisions = a.subdivisions;
457 this->unique_prototypes = a.unique_prototypes;
458 this->unique_prototype_identifier = a.unique_prototype_identifier;
459 this->build_petiolule = a.build_petiolule;
461 this->generator = a.generator;
462 }
463
466 if (this != &a) {
467 this->leaf_texture_file = a.leaf_texture_file;
468 this->OBJ_model_file = a.OBJ_model_file;
469 this->leaf_aspect_ratio = a.leaf_aspect_ratio;
470 this->leaf_aspect_ratio.resample();
471 this->midrib_fold_fraction = a.midrib_fold_fraction;
472 this->midrib_fold_fraction.resample();
473 this->longitudinal_curvature = a.longitudinal_curvature;
474 this->longitudinal_curvature.resample();
475 this->lateral_curvature = a.lateral_curvature;
476 this->lateral_curvature.resample();
477 this->petiole_roll = a.petiole_roll;
478 this->petiole_roll.resample();
479 this->wave_period = a.wave_period;
480 this->wave_period.resample();
481 this->wave_amplitude = a.wave_amplitude;
482 this->wave_amplitude.resample();
483 this->leaf_buckle_length = a.leaf_buckle_length;
484 this->leaf_buckle_length.resample();
485 this->leaf_buckle_angle = a.leaf_buckle_angle;
486 this->leaf_buckle_angle.resample();
487 this->leaf_offset = a.leaf_offset;
488 this->subdivisions = a.subdivisions;
489 this->unique_prototypes = a.unique_prototypes;
490 this->unique_prototype_identifier = a.unique_prototype_identifier;
491 this->build_petiolule = a.build_petiolule;
493 this->generator = a.generator;
494 if (this->generator != nullptr) {
495 this->sampleIdentifier();
496 }
497 }
498 return *this;
499 }
500
501 void sampleIdentifier() {
502 assert(generator != nullptr);
503 std::uniform_int_distribution<uint> unif_distribution;
504 this->unique_prototype_identifier = unif_distribution(*generator);
505 }
506
507private:
508 std::minstd_rand0 *generator{};
509};
510
512private:
513 struct InternodeParameters {
514
518 RandomParameter_float phyllotactic_angle;
520 RandomParameter_float radius_initial;
522 RandomParameter_int max_vegetative_buds_per_petiole;
524 RandomParameter_int max_floral_buds_per_petiole;
526 helios::RGBcolor color;
528 std::string image_texture;
530 uint length_segments;
532 uint radial_subdivisions;
533
534 InternodeParameters &operator=(const InternodeParameters &a) {
535 if (this != &a) {
536 this->pitch = a.pitch;
537 this->pitch.resample();
538 this->phyllotactic_angle = a.phyllotactic_angle;
539 this->phyllotactic_angle.resample();
540 this->radius_initial = a.radius_initial;
541 this->radius_initial.resample();
542 this->max_vegetative_buds_per_petiole = a.max_vegetative_buds_per_petiole;
543 this->max_vegetative_buds_per_petiole.resample();
544 this->max_floral_buds_per_petiole = a.max_floral_buds_per_petiole;
545 this->max_floral_buds_per_petiole.resample();
546 this->color = a.color;
547 this->image_texture = a.image_texture;
548 this->length_segments = a.length_segments;
549 this->radial_subdivisions = a.radial_subdivisions;
550 }
551 return *this;
552 }
553 };
554
555 struct PetioleParameters {
556
558 uint petioles_per_internode;
566 RandomParameter_float curvature;
570 helios::RGBcolor color;
572 uint length_segments;
574 uint radial_subdivisions;
575
576 PetioleParameters &operator=(const PetioleParameters &a) {
577 if (this != &a) {
578 this->petioles_per_internode = a.petioles_per_internode;
579 this->pitch = a.pitch;
580 this->pitch.resample();
581 this->radius = a.radius;
582 this->radius.resample();
583 this->length = a.length;
584 this->length.resample();
585 this->curvature = a.curvature;
586 this->curvature.resample();
587 this->taper = a.taper;
588 this->taper.resample();
589 this->color = a.color;
590 this->length_segments = a.length_segments;
591 this->radial_subdivisions = a.radial_subdivisions;
592 }
593 return *this;
594 }
595 };
596
597 struct LeafParameters {
599 RandomParameter_int leaves_per_petiole;
607 RandomParameter_float leaflet_offset;
609 RandomParameter_float leaflet_scale;
611 RandomParameter_float prototype_scale;
613 LeafPrototype prototype;
614
615 LeafParameters &operator=(const LeafParameters &a) {
616 if (this != &a) {
617 this->leaves_per_petiole = a.leaves_per_petiole;
618 this->leaves_per_petiole.resample();
619 this->pitch = a.pitch;
620 this->pitch.resample();
621 this->yaw = a.yaw;
622 this->yaw.resample();
623 this->roll = a.roll;
624 this->roll.resample();
625 this->leaflet_offset = a.leaflet_offset;
626 this->leaflet_offset.resample();
627 this->leaflet_scale = a.leaflet_scale;
628 this->leaflet_scale.resample();
629 this->prototype_scale = a.prototype_scale;
630 this->prototype_scale.resample();
631 this->prototype.duplicate(a.prototype);
632 }
633 return *this;
634 }
635 };
636
637 struct PeduncleParameters {
647 RandomParameter_float curvature;
649 helios::RGBcolor color;
651 uint length_segments;
653 uint radial_subdivisions;
654
655 PeduncleParameters &operator=(const PeduncleParameters &a) {
656 if (this != &a) {
657 this->length = a.length;
658 this->length.resample();
659 this->radius = a.radius;
660 this->radius.resample();
661 this->pitch = a.pitch;
662 this->pitch.resample();
663 this->roll = a.roll;
664 this->roll.resample();
665 this->curvature = a.curvature;
666 this->curvature.resample();
667 this->color = a.color;
668 this->length_segments = a.length_segments;
669 this->radial_subdivisions = a.radial_subdivisions;
670 }
671 return *this;
672 }
673 };
674
675 struct InflorescenceParameters {
676
678 RandomParameter_int flowers_per_peduncle;
680 RandomParameter_float flower_offset;
686 RandomParameter_float flower_prototype_scale;
688 uint (*flower_prototype_function)(helios::Context *, uint subdivisions, bool flower_is_open) = nullptr;
690 RandomParameter_float fruit_prototype_scale;
692 uint (*fruit_prototype_function)(helios::Context *, uint subdivisions) = nullptr;
694 RandomParameter_float fruit_gravity_factor_fraction;
696 uint unique_prototypes;
697
698 InflorescenceParameters &operator=(const InflorescenceParameters &a) {
699 if (this != &a) {
700 this->flowers_per_peduncle = a.flowers_per_peduncle;
701 this->flowers_per_peduncle.resample();
702 this->flower_offset = a.flower_offset;
703 this->flower_offset.resample();
704 this->pitch = a.pitch;
705 this->pitch.resample();
706 this->roll = a.roll;
707 this->roll.resample();
708 this->flower_prototype_scale = a.flower_prototype_scale;
709 this->flower_prototype_scale.resample();
710 this->flower_prototype_function = a.flower_prototype_function;
711 this->fruit_prototype_scale = a.fruit_prototype_scale;
712 this->fruit_prototype_scale.resample();
713 this->fruit_prototype_function = a.fruit_prototype_function;
714 this->fruit_gravity_factor_fraction = a.fruit_gravity_factor_fraction;
715 this->fruit_gravity_factor_fraction.resample();
716 this->unique_prototypes = a.unique_prototypes;
717 }
718 return *this;
719 }
720 };
721
722public:
730 InternodeParameters internode;
731
738 PetioleParameters petiole;
739
747 LeafParameters leaf;
748
755 PeduncleParameters peduncle;
756
765 InflorescenceParameters inflorescence;
766
767 // Custom user-defined function that is called when a phytomer is created
775 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;
776
777 // Custom user-defined function that is called for each phytomer on every time step
781 void (*phytomer_callback_function)(std::shared_ptr<Phytomer> phytomer_ptr) = nullptr;
782
783
786
788 explicit PhytomerParameters(std::minstd_rand0 *generator);
789
790 friend class PlantArchitecture;
791 friend struct Phytomer;
792 friend struct Shoot;
793};
794
796
799
801 explicit ShootParameters(std::minstd_rand0 *generator);
802
809
810 // ---- Geometric Parameters ---- //
811
837
838 // --- Growth Parameters --- //
839
861
862 // ---- Custom Functions ---- //
863
872 void defineChildShootTypes(const std::vector<std::string> &child_shoot_type_labels, const std::vector<float> &child_shoot_type_probabilities);
873
874 ShootParameters &operator=(const ShootParameters &a) {
875 this->phytomer_parameters = a.phytomer_parameters;
876 this->max_nodes = a.max_nodes;
877 this->max_nodes.resample();
878 this->max_nodes_per_season = a.max_nodes_per_season;
879 this->max_nodes_per_season.resample();
880 this->phyllochron_min = a.phyllochron_min;
881 this->phyllochron_min.resample();
882 this->girth_area_factor = a.girth_area_factor;
883 this->girth_area_factor.resample();
884 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
885 this->vegetative_bud_break_probability_min.resample();
886 this->flower_bud_break_probability = a.flower_bud_break_probability;
887 this->flower_bud_break_probability.resample();
888 this->fruit_set_probability = a.fruit_set_probability;
889 this->fruit_set_probability.resample();
890 this->gravitropic_curvature = a.gravitropic_curvature;
891 this->gravitropic_curvature.resample();
892 this->tortuosity = a.tortuosity;
893 this->tortuosity.resample();
894 this->vegetative_bud_break_probability_min = a.vegetative_bud_break_probability_min;
895 this->vegetative_bud_break_probability_min.resample();
896 this->vegetative_bud_break_probability_decay_rate = a.vegetative_bud_break_probability_decay_rate;
897 this->vegetative_bud_break_probability_decay_rate.resample();
898 this->max_terminal_floral_buds = a.max_terminal_floral_buds;
899 this->max_terminal_floral_buds.resample();
900 this->flower_bud_break_probability = a.flower_bud_break_probability;
901 this->flower_bud_break_probability.resample();
902 this->fruit_set_probability = a.fruit_set_probability;
903 this->fruit_set_probability.resample();
904 this->vegetative_bud_break_time = a.vegetative_bud_break_time;
905 this->vegetative_bud_break_time.resample();
906 this->insertion_angle_tip = a.insertion_angle_tip;
907 this->insertion_angle_tip.resample();
908 this->insertion_angle_decay_rate = a.insertion_angle_decay_rate;
909 this->insertion_angle_decay_rate.resample();
910 this->internode_length_max = a.internode_length_max;
911 this->internode_length_max.resample();
912 this->internode_length_min = a.internode_length_min;
913 this->internode_length_min.resample();
914 this->internode_length_decay_rate = a.internode_length_decay_rate;
915 this->internode_length_decay_rate.resample();
916 this->base_roll = a.base_roll;
917 this->base_roll.resample();
918 this->base_yaw = a.base_yaw;
919 this->base_yaw.resample();
920 this->flowers_require_dormancy = a.flowers_require_dormancy;
921 this->growth_requires_dormancy = a.growth_requires_dormancy;
922 this->child_shoot_type_labels = a.child_shoot_type_labels;
923 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
924 this->determinate_shoot_growth = a.determinate_shoot_growth;
925 this->child_shoot_type_labels = a.child_shoot_type_labels;
926 this->child_shoot_type_probabilities = a.child_shoot_type_probabilities;
927 return *this;
928 }
929
930 friend class PlantArchitecture;
931 friend struct Shoot;
932
933protected:
934 std::vector<std::string> child_shoot_type_labels;
935 std::vector<float> child_shoot_type_probabilities;
936};
937
938struct Phytomer {
939public:
941 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,
942 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);
943
944 // ---- query info about the phytomer ---- //
945
951 [[nodiscard]] std::vector<helios::vec3> getInternodeNodePositions() const;
952
958 [[nodiscard]] std::vector<float> getInternodeNodeRadii() const;
959
966 [[nodiscard]] float calculatePhytomerVolume(uint node_number) const;
967
974 [[nodiscard]] helios::vec3 getInternodeAxisVector(float stem_fraction) const;
975
983 [[nodiscard]] helios::vec3 getPetioleAxisVector(float stem_fraction, uint petiole_index) const;
984
993 [[nodiscard]] helios::vec3 getPeduncleAxisVector(float stem_fraction, uint petiole_index, uint bud_index) const;
994
1002 [[nodiscard]] static helios::vec3 getAxisVector(float stem_fraction, const std::vector<helios::vec3> &axis_vertices);
1003
1009 [[nodiscard]] float getInternodeLength() const;
1010
1016 [[nodiscard]] float getInternodeRadius() const;
1017
1023 [[nodiscard]] float getPetioleLength() const;
1024
1031 [[nodiscard]] float getInternodeRadius(float stem_fraction) const;
1032
1038 [[nodiscard]] float getLeafArea() const;
1039
1047 [[nodiscard]] helios::vec3 getLeafBasePosition(uint petiole_index, uint leaf_index) const;
1048
1054 [[nodiscard]] bool hasLeaf() const;
1055
1061 [[nodiscard]] float calculateDownstreamLeafArea() const;
1062
1063 // ---- modify the phytomer ---- //
1064
1066
1070 void setInternodeLengthScaleFraction(float internode_scale_factor_fraction, bool update_context_geometry);
1071
1073
1076 void scaleInternodeMaxLength(float scale_factor);
1077
1079
1082 void setInternodeMaxLength(float internode_length_max_new);
1083
1085
1088 void setInternodeMaxRadius(float internode_radius_max_new);
1089
1091
1095 void setLeafScaleFraction(uint petiole_index, float leaf_scale_factor_fraction);
1096
1098
1101 void setLeafScaleFraction(float leaf_scale_factor_fraction);
1102
1104
1108 void setLeafPrototypeScale(uint petiole_index, float leaf_prototype_scale);
1109
1111
1114 void setLeafPrototypeScale(float leaf_prototype_scale);
1115
1117
1122 void scaleLeafPrototypeScale(uint petiole_index, float scale_factor);
1123
1125
1128 void scaleLeafPrototypeScale(float scale_factor);
1129
1136 void setInflorescenceScaleFraction(FloralBud &fbud, float inflorescence_scale_factor_fraction) const;
1137
1146 void setPetioleBase(const helios::vec3 &base_position);
1147
1158 void rotateLeaf(uint petiole_index, uint leaf_index, const AxisRotation &rotation);
1159
1165 void setVegetativeBudState(BudState state);
1166
1174 void setVegetativeBudState(BudState state, uint petiole_index, uint bud_index);
1175
1182 static void setVegetativeBudState(BudState state, VegetativeBud &vbud);
1183
1189 void setFloralBudState(BudState state);
1190
1198 void setFloralBudState(BudState state, uint petiole_index, uint bud_index);
1199
1206 void setFloralBudState(BudState state, FloralBud &fbud);
1207
1215 void removeLeaf();
1216
1224 void deletePhytomer();
1225
1226private:
1238 helios::vec3 calculateCollisionAvoidanceDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &collision_detection_active) const;
1239
1251 bool applySolidObstacleAvoidance(const helios::vec3 &current_position, helios::vec3 &internode_axis) const;
1252
1265 helios::vec3 calculateAttractionPointDirection(const helios::vec3 &internode_base_origin, const helios::vec3 &internode_axis, bool &attraction_active) const;
1266
1279 helios::vec3 calculatePetioleCollisionAvoidanceDirection(const helios::vec3 &petiole_base_origin, const helios::vec3 &proposed_petiole_axis, bool &collision_detection_active) const;
1280
1282
1288 helios::vec3 calculateFruitCollisionAvoidanceDirection(const helios::vec3 &fruit_base_origin, const helios::vec3 &proposed_fruit_axis, bool &collision_detection_active) const;
1289
1290
1291public:
1292 // ---- phytomer data ---- //
1293
1295 std::vector<std::vector<helios::vec3>> petiole_vertices; // first index is petiole within internode, second index is tube segment within petiole tube
1296 std::vector<std::vector<helios::vec3>> leaf_bases; // first index is petiole within internode, second index is leaf within petiole
1297 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
1298 float internode_pitch, internode_phyllotactic_angle;
1299
1300 std::vector<std::vector<float>> petiole_radii; // first index is petiole within internode, second index is segment within petiole tube
1301 std::vector<float> petiole_length; // index is petiole within internode
1302 std::vector<float> petiole_pitch; // index is petiole within internode
1303 std::vector<float> petiole_curvature; // index is petiole within internode
1304 std::vector<std::vector<float>> leaf_size_max; // first index is petiole within internode, second index is leaf within petiole
1305 std::vector<std::vector<AxisRotation>> leaf_rotation; // first index is petiole within internode, second index is leaf within petiole
1306
1307 std::vector<helios::RGBcolor> internode_colors; // index is segment within internode tube
1308 std::vector<helios::RGBcolor> petiole_colors; // index is segment within petiole tube
1309
1310 std::vector<std::vector<uint>> petiole_objIDs; // first index is petiole within internode, second index is segment within petiole tube
1311 std::vector<std::vector<uint>> leaf_objIDs; // first index is petiole within internode, second index is leaf within petiole tube
1312
1313 PhytomerParameters phytomer_parameters;
1314
1315 uint rank;
1318
1319 uint plantID;
1320 uint parent_shoot_ID;
1321 Shoot *parent_shoot_ptr;
1322
1324 float age = 0;
1325 bool isdormant = false;
1326
1327 float current_internode_scale_factor = 1;
1328 std::vector<float> current_leaf_scale_factor; // index is petiole within internode
1329
1330 float old_phytomer_volume = 0;
1331
1332 float downstream_leaf_area = 0;
1333
1334 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds; // first index is petiole within internode, second index is bud within petiole
1335 std::vector<std::vector<FloralBud>> floral_buds; // first index is petiole within internode, second index is bud within petiole
1336
1337 float internode_radius_initial;
1338 float internode_radius_max;
1339 float internode_length_max;
1340
1341 bool build_context_geometry_petiole = true;
1342 bool build_context_geometry_peduncle = true;
1343
1344protected:
1345 helios::vec3 inflorescence_bending_axis;
1346
1347 helios::Context *context_ptr;
1348
1349 PlantArchitecture *plantarchitecture_ptr;
1350
1351 void updateInflorescence(FloralBud &fbud);
1352
1358 [[nodiscard]] float calculatePhytomerConstructionCosts() const;
1359
1367 [[nodiscard]] float calculateFlowerConstructionCosts(const FloralBud &fbud) const;
1368
1374 [[nodiscard]] float calculateFruitConstructionCosts(const FloralBud &fbud) const;
1375
1376 friend struct Shoot;
1377 friend class PlantArchitecture;
1378};
1379
1380struct Shoot {
1381
1383 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,
1384 float internode_length_shoot_initial, ShootParameters &shoot_params, std::string shoot_type_label, PlantArchitecture *plant_architecture_ptr);
1385
1387
1394 void buildShootPhytomers(float internode_radius, float internode_length, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper);
1395
1397
1404 int appendPhytomer(float internode_radius, float internode_length_max, float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, const PhytomerParameters &phytomer_parameters);
1405
1407
1410 [[nodiscard]] std::string sampleChildShootType() const;
1411
1413
1417 [[nodiscard]] bool sampleVegetativeBudBreak(uint node_index) const;
1418
1419 [[nodiscard]] bool sampleVegetativeBudBreak_carb(uint node_index) const;
1420
1422
1427 uint sampleEpicormicShoot(float dt, std::vector<float> &epicormic_positions_fraction) const;
1428
1433 void terminateApicalBud();
1434
1440
1448 void addTerminalFloralBud();
1449
1456 [[nodiscard]] float calculateShootInternodeVolume() const;
1457
1463 [[nodiscard]] float calculateShootLength() const;
1464
1471 [[nodiscard]] helios::vec3 getShootAxisVector(float shoot_fraction) const;
1472
1480 [[nodiscard]] float sumShootLeafArea(uint start_node_index = 0) const;
1481
1488 [[nodiscard]] float sumChildVolume(uint start_node_index = 0) const;
1489
1497 void propagateDownstreamLeafArea(const Shoot *shoot, uint node_index, float leaf_area);
1498
1503 void updateShootNodes(bool update_context_geometry = true);
1504
1505 uint current_node_number = 0;
1506 uint nodes_this_season = 0;
1507
1508 helios::vec3 base_position;
1509 AxisRotation base_rotation;
1510 helios::vec3 radial_outward_axis;
1511
1512 const int ID;
1513 const int parent_shoot_ID;
1514 const uint plantID;
1515 const uint parent_node_index;
1516 const uint rank;
1517 const uint parent_petiole_index;
1518
1519 float carbohydrate_pool_molC = 0; // mol C
1520 float old_shoot_volume = 0;
1521
1522 float phyllochron_increase = 5;
1523 float phyllochron_recovery = phyllochron_increase;
1524
1525 float days_with_negative_carbon_balance = 0;
1526
1527 void breakDormancy();
1528 void makeDormant();
1529
1530 bool isdormant;
1531 uint dormancy_cycles = 0;
1532
1533 bool meristem_is_alive = true;
1534
1535 float phyllochron_counter = 0;
1536 float phyllochron_min = 6.f;
1537 float elongation_max = 0.25;
1538
1539 float curvature_perturbation = 0;
1540 float yaw_perturbation = 0;
1541
1542 float gravitropic_curvature = 0;
1543
1544 const float internode_length_max_shoot_initial;
1545
1546 uint internode_tube_objID = 4294967294;
1547
1548 std::vector<std::vector<helios::vec3>> shoot_internode_vertices; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1549 std::vector<std::vector<float>> shoot_internode_radii; // first index is phytomer within shoot, second index is segment within phytomer internode tube
1550
1551 bool build_context_geometry_internode = true;
1552
1553 // map of node number (key) to IDs of shoot children (value)
1554 std::map<int, std::vector<int>> childIDs;
1555
1556 ShootParameters shoot_parameters;
1557
1558 std::string shoot_type_label;
1559
1560 float phyllochron_instantaneous;
1561 float elongation_rate_instantaneous;
1562
1563 std::vector<std::shared_ptr<Phytomer>> phytomers;
1564
1565 PlantArchitecture *plantarchitecture_ptr;
1566
1567 helios::Context *context_ptr;
1568};
1569
1571
1572 PlantInstance(const helios::vec3 &a_base_position, float a_current_age, const std::string &a_plant_name, helios::Context *a_context_ptr) :
1573 base_position(a_base_position), current_age(a_current_age), plant_name(a_plant_name), context_ptr(a_context_ptr) {
1574 }
1575 std::vector<std::shared_ptr<Shoot>> shoot_tree;
1576 helios::vec3 base_position;
1577 float current_age;
1578 float time_since_dormancy = 0;
1579 helios::Context *context_ptr;
1580 std::string plant_name;
1581 std::pair<std::string, float> epicormic_shoot_probability_perlength_per_day; //.first is the epicormic shoot label string, .second is the probability
1582
1583 // Phenological thresholds
1584 float dd_to_dormancy_break = 0;
1585 float dd_to_flower_initiation = 0;
1586 float dd_to_flower_opening = 0;
1587 float dd_to_fruit_set = 0;
1588 float dd_to_fruit_maturity = 0;
1589 float dd_to_dormancy = 0;
1590 float max_leaf_lifespan = 1e6;
1591 bool is_evergreen = false;
1592
1593 float max_age = 999;
1594
1595 CarbohydrateParameters carb_parameters;
1596
1597 // --- Per-plant Attraction Points --- //
1598
1601
1603 std::vector<helios::vec3> attraction_points;
1604
1606 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
1607
1610
1612 float attraction_weight = 0.6f;
1613
1616};
1617
1619public:
1621
1624 explicit PlantArchitecture(helios::Context *context_ptr);
1625
1628
1630 static int selfTest(int argc, char **argv);
1631
1633
1636 void optionalOutputObjectData(const std::string &object_data_label);
1637
1639
1642 void optionalOutputObjectData(const std::vector<std::string> &object_data_labels);
1643
1644 // ********* Methods for Building Plants from Existing Library ********* //
1645
1647
1650 void loadPlantModelFromLibrary(const std::string &plant_label);
1651
1653 [[nodiscard]] std::vector<std::string> getAvailablePlantModels() const;
1654
1656
1661 uint buildPlantInstanceFromLibrary(const helios::vec3 &base_position, float age);
1662
1664
1672 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);
1673
1675
1682 std::vector<uint> buildPlantCanopyFromLibrary(const helios::vec3 &canopy_center_position, const helios::vec2 &canopy_extent_xy, uint plant_count, float age);
1683
1685
1689 ShootParameters getCurrentShootParameters(const std::string &shoot_type_label);
1690
1692
1695 std::map<std::string, ShootParameters> getCurrentShootParameters();
1696
1698
1702 std::map<std::string, PhytomerParameters> getCurrentPhytomerParameters();
1703
1705
1710 void updateCurrentShootParameters(const std::string &shoot_type_label, const ShootParameters &params);
1711
1713
1717 void updateCurrentShootParameters(const std::map<std::string, ShootParameters> &params);
1718
1719 // ********* Methods for Building Custom Plant Geometry from Scratch ********* //
1720
1722
1727 uint addPlantInstance(const helios::vec3 &base_position, float current_age);
1728
1730
1737 uint duplicatePlantInstance(uint plantID, const helios::vec3 &base_position, const AxisRotation &base_rotation, float current_age);
1738
1740
1743 void deletePlantInstance(uint plantID);
1744
1746
1749 void deletePlantInstance(const std::vector<uint> &plantIDs);
1750
1752
1764 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,
1765 float max_leaf_lifespan = 1e6, bool is_evergreen = false);
1766
1768
1772 void setPlantCarbohydrateModelParameters(uint plantID, const CarbohydrateParameters &carb_parameters);
1773
1775
1779 void setPlantCarbohydrateModelParameters(const std::vector<uint> &plantIDs, const CarbohydrateParameters &carb_parameters);
1780
1786 void disablePlantPhenology(uint plantID);
1787
1789
1792 void advanceTime(float time_step_days);
1793
1795
1799 void advanceTime(int time_step_years, float time_step_days);
1800
1802
1806 void advanceTime(uint plantID, float time_step_days);
1807
1809
1813 void advanceTime(const std::vector<uint> &plantIDs, float time_step_days);
1814
1816
1824
1826
1834
1835 // -- plant building methods -- //
1836
1838
1842 void defineShootType(const std::string &shoot_type_label, const ShootParameters &shoot_params);
1843
1845
1857 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,
1858 float radius_taper, const std::string &shoot_type_label);
1859
1861
1874 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,
1875 float radius_taper, const std::string &shoot_type_label);
1876
1878
1893 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,
1894 float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label, uint petiole_index = 0);
1895
1897
1911 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,
1912 float internode_length_scale_factor_fraction, float leaf_scale_factor_fraction, float radius_taper, const std::string &shoot_type_label);
1913
1915
1925 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);
1926
1928
1934 void enableEpicormicChildShoots(uint plantID, const std::string &epicormic_shoot_type_label, float epicormic_probability_perlength_perday);
1935
1938
1941
1944
1946
1949 void enableGroundClipping(float ground_height = 0.f);
1950
1951 // -- collision detection methods -- //
1952
1954
1960 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);
1961
1964
1966
1972 void setSoftCollisionAvoidanceParameters(float view_half_angle_deg, float look_ahead_distance, int sample_count, float inertia_weight);
1973
1975
1978 void setStaticObstacles(const std::vector<uint> &target_UUIDs);
1979
1981
1984 [[nodiscard]] CollisionDetection *getCollisionDetection() const;
1985
1987
1994 void setCollisionRelevantOrgans(bool include_internodes, bool include_leaves, bool include_petioles, bool include_flowers, bool include_fruit);
1995
1997
2009 void enableSolidObstacleAvoidance(const std::vector<uint> &obstacle_UUIDs, float avoidance_distance = 0.5f, bool enable_fruit_adjustment = false, bool enable_obstacle_pruning = false);
2010
2012
2016 void setGeometryUpdateScheduling(int update_frequency = 3, bool force_update_on_collision = true);
2017
2018 // -- attraction points methods -- //
2019
2021
2027 void enableAttractionPoints(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);
2028
2031
2033
2036 void updateAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2037
2039
2042 void appendAttractionPoints(const std::vector<helios::vec3> &attraction_points);
2043
2045
2051 void setAttractionParameters(float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2052
2054
2061 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);
2062
2064
2067 void disableAttractionPoints(uint plantID);
2068
2070
2074 void updateAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2075
2077
2081 void appendAttractionPoints(uint plantID, const std::vector<helios::vec3> &attraction_points);
2082
2084
2091 void setAttractionParameters(uint plantID, float view_half_angle_deg, float look_ahead_distance, float attraction_weight, float obstacle_reduction_factor = 0.75f);
2092
2107 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;
2108
2110 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;
2111
2112 // -- methods for modifying the current plant state -- //
2113
2118 void initializeCarbohydratePool(float carbohydrate_concentration_molC_m3) const;
2119
2126 void initializePlantCarbohydratePool(uint plantID, float carbohydrate_concentration_molC_m3);
2127
2136 void initializeShootCarbohydratePool(uint plantID, uint shootID, float carbohydrate_concentration_molC_m3);
2137
2146 void setPhytomerLeafScale(uint plantID, uint shootID, uint node_number, float leaf_scale_factor_fraction);
2147
2153 void setPlantBasePosition(uint plantID, const helios::vec3 &base_position);
2154
2167 void setPlantLeafElevationAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination) const;
2168
2181 void setPlantLeafElevationAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination) const;
2182
2195 void setPlantLeafAzimuthAngleDistribution(uint plantID, float eccentricity, float ellipse_rotation_degrees) const;
2196
2209 void setPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, float eccentricity, float ellipse_rotation_degrees) const;
2210
2225 void setPlantLeafAngleDistribution(uint plantID, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2226
2241 void setPlantLeafAngleDistribution(const std::vector<uint> &plantIDs, float Beta_mu_inclination, float Beta_nu_inclination, float eccentricity, float ellipse_rotation_degrees) const;
2242
2244 void setPlantAge(uint plantID, float current_age);
2245
2251 void harvestPlant(uint plantID);
2252
2259 void removeShootLeaves(uint plantID, uint shootID);
2260
2267 void removeShootVegetativeBuds(uint plantID, uint shootID);
2268
2274 void removePlantLeaves(uint plantID);
2275
2281 void makePlantDormant(uint plantID);
2282
2288 void breakPlantDormancy(uint plantID);
2289
2297 void pruneBranch(uint plantID, uint shootID, uint node_index);
2298
2299 // -- methods for querying information about the plant -- //
2300
2307 [[nodiscard]] std::string getPlantName(uint plantID) const;
2308
2315 [[nodiscard]] float getPlantAge(uint plantID) const;
2316
2324 [[nodiscard]] uint getShootNodeCount(uint plantID, uint shootID) const;
2325
2333 [[nodiscard]] float getShootTaper(uint plantID, uint shootID) const;
2334
2341 [[nodiscard]] helios::vec3 getPlantBasePosition(uint plantID) const;
2342
2349 [[nodiscard]] std::vector<helios::vec3> getPlantBasePosition(const std::vector<uint> &plantIDs) const;
2350
2352
2356 [[nodiscard]] float sumPlantLeafArea(uint plantID) const;
2357
2359
2363 [[nodiscard]] float getPlantStemHeight(uint plantID) const;
2364
2366
2370 [[nodiscard]] float getPlantHeight(uint plantID) const;
2371
2373
2379 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2380
2382
2388 [[nodiscard]] std::vector<float> getPlantLeafInclinationAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2389
2391
2397 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(uint plantID, uint Nbins, bool normalize = true) const;
2398
2400
2406 [[nodiscard]] std::vector<float> getPlantLeafAzimuthAngleDistribution(const std::vector<uint> &plantIDs, uint Nbins, bool normalize = true) const;
2407
2409
2413 [[nodiscard]] uint getPlantLeafCount(uint plantID) const;
2414
2416
2420 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(uint plantID) const;
2421
2423
2427 [[nodiscard]] std::vector<helios::vec3> getPlantLeafBases(const std::vector<uint> &plantIDs) const;
2428
2430
2434 [[nodiscard]] bool isPlantDormant(uint plantID) const;
2435
2437
2441 void writePlantMeshVertices(uint plantID, const std::string &filename) const;
2442
2444
2447 [[nodiscard]] std::vector<uint> getAllPlantIDs() const;
2448
2450
2454 [[nodiscard]] std::vector<uint> getAllPlantObjectIDs(uint plantID) const;
2455
2457
2461 [[nodiscard]] std::vector<uint> getAllPlantUUIDs(uint plantID) const;
2462
2464
2468 [[nodiscard]] std::vector<uint> getPlantInternodeObjectIDs(uint plantID) const;
2469
2471
2475 [[nodiscard]] std::vector<uint> getPlantPetioleObjectIDs(uint plantID) const;
2476
2478
2482 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(uint plantID) const;
2483
2485
2489 [[nodiscard]] std::vector<uint> getPlantLeafObjectIDs(const std::vector<uint> &plantIDs) const;
2490
2492
2496 [[nodiscard]] std::vector<uint> getPlantPeduncleObjectIDs(uint plantID) const;
2497
2499
2503 [[nodiscard]] std::vector<uint> getPlantFlowerObjectIDs(uint plantID) const;
2504
2506
2510 [[nodiscard]] std::vector<uint> getPlantFruitObjectIDs(uint plantID) const;
2511
2513
2520 [[nodiscard]] std::vector<uint> getPlantCollisionRelevantObjectIDs(uint plantID) const;
2521
2523
2526 [[nodiscard]] std::vector<uint> getAllUUIDs() const;
2527
2529
2532 [[nodiscard]] std::vector<uint> getAllLeafUUIDs() const;
2533
2535
2538 [[nodiscard]] std::vector<uint> getAllInternodeUUIDs() const;
2539
2541
2544 [[nodiscard]] std::vector<uint> getAllPetioleUUIDs() const;
2545
2547
2550 [[nodiscard]] std::vector<uint> getAllPeduncleUUIDs() const;
2551
2553
2556 [[nodiscard]] std::vector<uint> getAllFlowerUUIDs() const;
2557
2559
2562 [[nodiscard]] std::vector<uint> getAllFruitUUIDs() const;
2563
2565
2568 [[nodiscard]] std::vector<uint> getAllObjectIDs() const;
2569
2570 // -- carbohydrate model -- //
2571
2576
2581
2582 // -- manual plant generation from input string -- //
2583
2590 [[nodiscard]] std::string getPlantString(uint plantID) const;
2591
2599 uint generatePlantFromString(const std::string &generation_string, const PhytomerParameters &phytomer_parameters);
2600
2609 uint generatePlantFromString(const std::string &generation_string, const std::map<std::string, PhytomerParameters> &phytomer_parameters);
2610
2619 void writePlantStructureXML(uint plantID, const std::string &filename) const;
2620
2622
2633 void writeQSMCylinderFile(uint plantID, const std::string &filename) const;
2634
2646 std::vector<uint> readPlantStructureXML(const std::string &filename, bool quiet = false);
2647
2649 void disableMessages();
2650
2652 void enableMessages();
2653
2654 friend struct Phytomer;
2655 friend struct Shoot;
2656
2657private:
2659 void clearBVHCache() const;
2660
2662 void rebuildBVHForTimestep();
2663
2665
2675 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);
2676
2677protected:
2678 helios::Context *context_ptr;
2679
2680 std::minstd_rand0 *generator = nullptr;
2681
2682 uint plant_count = 0;
2683
2684 std::string current_plant_model;
2685
2686 // Function pointer maps for plant model registration
2687 std::map<std::string, std::function<void()>> shoot_initializers;
2688 std::map<std::string, std::function<uint(const helios::vec3 &)>> plant_builders;
2689
2690 std::map<uint, PlantInstance> plant_instances;
2691
2692 [[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;
2693
2694 std::map<std::string, ShootParameters> shoot_types;
2695
2696 // 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)
2697 // std::map<uint(*)(helios::Context* context_ptr, LeafPrototype* prototype_parameters, int compound_leaf_index),std::vector<std::vector<uint>> > unique_leaf_prototype_objIDs;
2698 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
2699
2700 // Key is the prototype function pointer; value index is the unique flower prototype
2701 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
2702 // Key is the prototype function pointer; value index is the unique flower prototype
2703 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions, bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
2704 // Key is the prototype function pointer; value index is the unique fruit prototype
2705 std::map<uint (*)(helios::Context *context_ptr, uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
2706
2707 bool build_context_geometry_internode = true;
2708 bool build_context_geometry_petiole = true;
2709 bool build_context_geometry_peduncle = true;
2710
2711 float ground_clipping_height = -99999;
2712
2713 void validateShootTypes(ShootParameters &shoot_parameters) const;
2714
2716 void registerPlantModel(const std::string &name, std::function<void()> shoot_init, std::function<uint(const helios::vec3 &)> plant_build);
2717
2719 void initializePlantModelRegistrations();
2720
2721 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);
2722
2723 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);
2724
2725 void parseInternodeArgument(const std::string &internode_argument, float &internode_radius, float &internode_length, PhytomerParameters &phytomer_parameters);
2726
2727 void parsePetioleArgument(const std::string &petiole_argument, PhytomerParameters &phytomer_parameters);
2728
2729 void parseLeafArgument(const std::string &leaf_argument, PhytomerParameters &phytomer_parameters);
2730
2731 void initializeDefaultShoots(const std::string &plant_label);
2732
2733 [[nodiscard]] bool detectGroundCollision(uint objID);
2734
2735 [[nodiscard]] bool detectGroundCollision(const std::vector<uint> &objID) const;
2736
2737 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;
2738
2739 static float interpolateTube(const std::vector<float> &P, float frac);
2740
2741 static helios::vec3 interpolateTube(const std::vector<helios::vec3> &P, float frac);
2742
2744 std::map<std::string, bool> output_object_data;
2745
2746 // --- Plant Growth --- //
2747
2748 void incrementPhytomerInternodeGirth(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
2749 void incrementPhytomerInternodeGirth_carb(uint plantID, uint shootID, uint node_number, float dt, bool update_context_geometry);
2750
2751 void pruneGroundCollisions(uint plantID);
2752
2753 void pruneSolidBoundaryCollisions();
2754
2755 // --- Carbohydrate Model --- //
2756
2757 void accumulateShootPhotosynthesis() const;
2758
2759 void subtractShootMaintenanceCarbon(float dt) const;
2760 void subtractShootGrowthCarbon();
2761
2762 void checkCarbonPool_abortOrgans(float dt);
2763 void checkCarbonPool_adjustPhyllochron(float dt);
2764 void checkCarbonPool_transferCarbon(float dt);
2765
2766 bool carbon_model_enabled = false;
2767
2768 // --- Collision Detection --- //
2769
2771 CollisionDetection *collision_detection_ptr = nullptr;
2772
2774 bool owns_collision_detection = false;
2775
2777 bool collision_detection_enabled = false;
2778
2780 std::vector<uint> collision_target_UUIDs;
2781
2783 std::vector<uint> collision_target_object_IDs;
2784
2786 float collision_cone_half_angle_rad = 80.f * M_PI / 180.f;
2787
2789 float collision_cone_height = 0.1f;
2790
2792 int collision_sample_count = 256;
2793
2795 float collision_inertia_weight = 0.4f;
2796
2798 int geometry_update_frequency = 3;
2799
2801 bool force_update_on_collision = true;
2802
2804 bool collision_include_internodes = false;
2805 bool collision_include_leaves = true;
2806 bool collision_include_petioles = false;
2807 bool collision_include_flowers = false;
2808 bool collision_include_fruit = false;
2809
2811 bool petiole_collision_detection_enabled = false;
2812
2814 bool fruit_collision_detection_enabled = false;
2815
2817 int geometry_update_counter = 0;
2818
2820 mutable bool collision_avoidance_applied = false;
2821
2823 bool spatial_filtering_enabled = false;
2824
2826 float spatial_max_distance = 5.0f;
2827
2829 mutable bool bvh_cached_for_current_growth = false;
2830 mutable std::vector<uint> cached_target_geometry;
2831 mutable std::vector<uint> cached_filtered_geometry;
2832
2834 bool solid_obstacle_avoidance_enabled = false;
2835 std::vector<uint> solid_obstacle_UUIDs;
2836 float solid_obstacle_avoidance_distance = 0.5f;
2837 float solid_obstacle_minimum_distance = 0.05f;
2838 bool solid_obstacle_fruit_adjustment_enabled = false;
2839 bool solid_obstacle_pruning_enabled = false;
2840
2841 // --- Attraction Points --- //
2842
2844 bool attraction_points_enabled = false;
2845
2847 std::vector<helios::vec3> attraction_points;
2848
2850 float attraction_cone_half_angle_rad = 80.f * M_PI / 180.f;
2851
2853 float attraction_cone_height = 0.1f;
2854
2856 float attraction_weight = 0.6f;
2857
2859 float attraction_obstacle_reduction_factor = 0.5f;
2860
2862 bool printmessages = true;
2863
2864 // --- Plant Library --- //
2865
2866 void initializeAlmondTreeShoots();
2867
2868 uint buildAlmondTree(const helios::vec3 &base_position);
2869
2870 void initializeAppleTreeShoots();
2871
2872 uint buildAppleTree(const helios::vec3 &base_position);
2873
2874 void initializeAsparagusShoots();
2875
2876 uint buildAsparagusPlant(const helios::vec3 &base_position);
2877
2878 void initializeBindweedShoots();
2879
2880 uint buildBindweedPlant(const helios::vec3 &base_position);
2881
2882 void initializeBeanShoots();
2883
2884 uint buildBeanPlant(const helios::vec3 &base_position);
2885
2886 void initializeCapsicumShoots();
2887
2888 uint buildCapsicumPlant(const helios::vec3 &base_position);
2889
2890 void initializeCheeseweedShoots();
2891
2892 uint buildCheeseweedPlant(const helios::vec3 &base_position);
2893
2894 void initializeCowpeaShoots();
2895
2896 uint buildCowpeaPlant(const helios::vec3 &base_position);
2897
2898 void initializeGrapevineVSPShoots();
2899
2900 uint buildGrapevineVSP(const helios::vec3 &base_position);
2901
2902 void initializeGrapevineWyeShoots();
2903
2904 uint buildGrapevineWye(const helios::vec3 &base_position);
2905
2906 void initializeGroundCherryWeedShoots();
2907
2908 uint buildGroundCherryWeedPlant(const helios::vec3 &base_position);
2909
2910 void initializeMaizeShoots();
2911
2912 uint buildMaizePlant(const helios::vec3 &base_position);
2913
2914 void initializeOliveTreeShoots();
2915
2916 uint buildOliveTree(const helios::vec3 &base_position);
2917
2918 void initializePistachioTreeShoots();
2919
2920 uint buildPistachioTree(const helios::vec3 &base_position);
2921
2922 void initializePuncturevineShoots();
2923
2924 uint buildPuncturevinePlant(const helios::vec3 &base_position);
2925
2926 void initializeEasternRedbudShoots();
2927
2928 uint buildEasternRedbudPlant(const helios::vec3 &base_position);
2929
2930 void initializeRiceShoots();
2931
2932 uint buildRicePlant(const helios::vec3 &base_position);
2933
2934 void initializeButterLettuceShoots();
2935
2936 uint buildButterLettucePlant(const helios::vec3 &base_position);
2937
2938 void initializeSoybeanShoots();
2939
2940 uint buildSoybeanPlant(const helios::vec3 &base_position);
2941
2942 void initializeSorghumShoots();
2943
2944 uint buildSorghumPlant(const helios::vec3 &base_position);
2945
2946 void initializeStrawberryShoots();
2947
2948 uint buildStrawberryPlant(const helios::vec3 &base_position);
2949
2950 void initializeSugarbeetShoots();
2951
2952 uint buildSugarbeetPlant(const helios::vec3 &base_position);
2953
2954 void initializeTomatoShoots();
2955
2956 uint buildTomatoPlant(const helios::vec3 &base_position);
2957
2958 void initializeCherryTomatoShoots();
2959
2960 uint buildCherryTomatoPlant(const helios::vec3 &base_position);
2961
2962 void initializeWalnutTreeShoots();
2963
2964 uint buildWalnutTree(const helios::vec3 &base_position);
2965
2966 void initializeWheatShoots();
2967
2968 uint buildWheatPlant(const helios::vec3 &base_position);
2969};
2970
2971#include "Assets.h"
2972
2973#endif // PLANT_ARCHITECTURE