572 struct InternodeParameters {
587 std::string image_texture;
589 uint length_segments;
591 uint radial_subdivisions;
593 InternodeParameters &operator=(
const InternodeParameters &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;
619 struct PetioleParameters {
622 uint petioles_per_internode;
636 uint length_segments;
638 uint radial_subdivisions;
640 PetioleParameters &operator=(
const PetioleParameters &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;
666 struct LeafParameters {
684 LeafParameters &operator=(
const LeafParameters &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();
693 if (a.yaw.distribution !=
"constant")
694 this->yaw.resample();
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);
713 struct PeduncleParameters {
727 uint length_segments;
729 uint radial_subdivisions;
731 PeduncleParameters &operator=(
const PeduncleParameters &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();
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;
756 struct InflorescenceParameters {
777 uint unique_prototypes;
779 InflorescenceParameters &operator=(
const InflorescenceParameters &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();
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;
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);
1140 [[nodiscard]]
bool hasLeaf()
const;
1393 helios::vec3 calculatePetioleCollisionAvoidanceDirection(
const helios::vec3 &petiole_base_origin,
const helios::vec3 &proposed_petiole_axis,
bool &collision_detection_active)
const;
1402 helios::vec3 calculateFruitCollisionAvoidanceDirection(
const helios::vec3 &fruit_base_origin,
const helios::vec3 &proposed_fruit_axis,
bool &collision_detection_active)
const;
1410 std::vector<std::vector<helios::vec3>> leaf_bases;
1411 std::vector<std::vector<std::vector<helios::vec3>>> peduncle_vertices;
1412 std::vector<std::vector<std::vector<float>>> peduncle_radii;
1413 std::vector<std::vector<float>> peduncle_length;
1414 std::vector<std::vector<float>> peduncle_radius;
1415 std::vector<std::vector<float>> peduncle_pitch;
1416 std::vector<std::vector<float>> peduncle_curvature;
1417 float internode_pitch, internode_phyllotactic_angle;
1419 std::vector<std::vector<float>> petiole_radii;
1420 std::vector<float> petiole_length;
1421 std::vector<float> petiole_pitch;
1422 std::vector<float> petiole_curvature;
1423 std::vector<float> petiole_taper;
1424 std::vector<helios::vec3> petiole_axis_initial;
1425 std::vector<helios::vec3> petiole_rotation_axis;
1426 std::vector<std::vector<float>> leaf_size_max;
1427 std::vector<std::vector<AxisRotation>> leaf_rotation;
1429 std::vector<helios::RGBcolor> internode_colors;
1430 std::vector<helios::RGBcolor> petiole_colors;
1432 std::vector<std::vector<uint>> petiole_objIDs;
1433 std::vector<std::vector<uint>> leaf_objIDs;
1442 uint parent_shoot_ID;
1443 Shoot *parent_shoot_ptr;
1447 bool isdormant =
false;
1449 float current_internode_scale_factor = 1;
1450 std::vector<float> current_leaf_scale_factor;
1452 float old_phytomer_volume = 0;
1454 float downstream_leaf_area = 0;
1456 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds;
1457 std::vector<std::vector<FloralBud>> floral_buds;
1459 float internode_radius_initial;
1460 float internode_radius_max;
1461 float internode_length_max;
1466 bool build_context_geometry_petiole =
true;
1467 bool build_context_geometry_peduncle =
true;
1476 void updateInflorescence(
FloralBud &fbud);
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);
1501 [[nodiscard]]
float calculatePhytomerConstructionCosts()
const;
1510 [[nodiscard]]
float calculateFlowerConstructionCosts(
const FloralBud &fbud)
const;
1517 [[nodiscard]]
float calculateFruitConstructionCosts(
const FloralBud &fbud)
const;
1519 friend struct Shoot;
1832 static int selfTest(
int argc,
char **argv);
1887 const std::map<std::string, float> &build_parameters = {});
1935 [[nodiscard]] std::vector<std::string>
listShootTypeLabels(
const std::string &plant_model_name);
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);
2041 void advanceTime(
int time_step_years,
float time_step_days);
2055 void advanceTime(
const std::vector<uint> &plantIDs,
float time_step_days);
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);
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);
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);
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);
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);
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);
2236 void setCollisionRelevantOrgans(
bool include_internodes,
bool include_leaves,
bool include_petioles,
bool include_flowers,
bool include_fruit);
2251 void enableSolidObstacleAvoidance(
const std::vector<uint> &obstacle_UUIDs,
float avoidance_distance = 0.5f,
bool enable_fruit_adjustment =
false,
bool enable_obstacle_pruning =
false);
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);
2293 void setAttractionParameters(
float view_half_angle_deg,
float look_ahead_distance,
float attraction_weight,
float obstacle_reduction_factor = 0.75f);
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);
2333 void setAttractionParameters(
uint plantID,
float view_half_angle_deg,
float look_ahead_distance,
float attraction_weight,
float obstacle_reduction_factor = 0.75f);
2467 void setPlantLeafAngleDistribution(
uint plantID,
float Beta_mu_inclination,
float Beta_nu_inclination,
float eccentricity,
float ellipse_rotation_degrees)
const;
2483 void setPlantLeafAngleDistribution(
const std::vector<uint> &plantIDs,
float Beta_mu_inclination,
float Beta_nu_inclination,
float eccentricity,
float ellipse_rotation_degrees)
const;
2599 [[nodiscard]] std::vector<helios::vec3>
getPlantBasePosition(
const std::vector<uint> &plantIDs)
const;
2677 [[nodiscard]] std::vector<helios::vec3>
getPlantLeafBases(
const std::vector<uint> &plantIDs)
const;
2719 [[nodiscard]] std::vector<uint>
getAllPlantUUIDs(
uint plantID,
bool include_hidden =
false)
const;
2809 [[nodiscard]] std::vector<uint>
getAllUUIDs()
const;
2962 uint generatePlantFromString(
const std::string &generation_string,
const std::map<std::string, PhytomerParameters> &phytomer_parameters);
3083 friend struct Shoot;
3097 float getParameterValue(
const std::map<std::string, float> &build_parameters,
const std::string ¶meter_name,
float default_value,
float min_value,
float max_value,
const std::string ¶meter_description)
const;
3100 void clearBVHCache()
const;
3103 void rebuildBVHForTimestep();
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);
3125 void ensureInflorescencePrototypesInitialized(
const PhytomerParameters ¶ms,
const std::string &plant_name);
3130 std::minstd_rand0 *generator =
nullptr;
3132 uint plant_count = 0;
3134 std::string current_plant_model;
3136 std::function<void(
float,
const std::string&)> progress_callback;
3139 std::map<std::string, float> current_build_parameters;
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;
3146 std::map<uint, PlantInstance> plant_instances;
3148 [[nodiscard]] std::string makeShootString(
const std::string ¤t_string,
const std::shared_ptr<Shoot> &shoot,
const std::vector<std::shared_ptr<Shoot>> &shoot_tree)
const;
3150 std::map<std::string, ShootParameters> shoot_types;
3154 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
3157 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions,
bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
3159 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions,
bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
3161 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
3164 [[nodiscard]] std::vector<uint> getAllPrototypeObjectIDs()
const;
3167 void deleteAllPrototypes();
3169 bool build_context_geometry_internode =
true;
3170 bool build_context_geometry_petiole =
true;
3171 bool build_context_geometry_peduncle =
true;
3173 float ground_clipping_height = -99999;
3175 void validateShootTypes(
ShootParameters &shoot_parameters,
const std::map<std::string, ShootParameters> &shoot_types_ref)
const;
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");
3181 void initializePlantModelRegistrations();
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);
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);
3187 void parseInternodeArgument(
const std::string &internode_argument,
float &internode_radius,
float &internode_length,
PhytomerParameters &phytomer_parameters);
3189 void parsePetioleArgument(
const std::string &petiole_argument,
PhytomerParameters &phytomer_parameters);
3191 void parseLeafArgument(
const std::string &leaf_argument,
PhytomerParameters &phytomer_parameters);
3193 void initializeDefaultShoots(
const std::string &plant_label);
3195 [[nodiscard]]
bool detectGroundCollision(
uint objID);
3197 [[nodiscard]]
bool detectGroundCollision(
const std::vector<uint> &objID)
const;
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;
3201 static float interpolateTube(
const std::vector<float> &P,
float frac);
3206 std::map<std::string, bool> output_object_data;
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);
3213 void pruneGroundCollisions(
uint plantID);
3215 void pruneSolidBoundaryCollisions();
3219 void accumulateShootPhotosynthesis()
const;
3221 void subtractShootMaintenanceCarbon(
float dt)
const;
3222 void subtractShootGrowthCarbon();
3224 void checkCarbonPool_abortOrgans(
float dt);
3225 void checkCarbonPool_adjustPhyllochron(
float dt);
3226 void checkCarbonPool_transferCarbon(
float dt);
3228 bool carbon_model_enabled =
false;
3232 void accumulateLeafNitrogen(
float dt);
3233 void remobilizeNitrogen(
float dt);
3234 void updateNitrogenStressFactor();
3235 void removeFruitNitrogen();
3237 bool nitrogen_model_enabled =
false;
3245 bool owns_collision_detection =
false;
3248 bool collision_detection_enabled =
false;
3251 std::vector<uint> collision_target_UUIDs;
3254 std::vector<uint> collision_target_object_IDs;
3257 float collision_cone_half_angle_rad = 80.f *
M_PI / 180.f;
3260 float collision_cone_height = 0.1f;
3263 int collision_sample_count = 256;
3266 float collision_inertia_weight = 0.4f;
3269 int geometry_update_frequency = 3;
3272 bool force_update_on_collision =
true;
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;
3282 bool petiole_collision_detection_enabled =
false;
3285 bool fruit_collision_detection_enabled =
false;
3288 int geometry_update_counter = 0;
3291 mutable bool collision_avoidance_applied =
false;
3294 bool spatial_filtering_enabled =
false;
3297 float spatial_max_distance = 5.0f;
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;
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;
3315 bool attraction_points_enabled =
false;
3318 std::vector<helios::vec3> attraction_points;
3321 float attraction_cone_half_angle_rad = 80.f *
M_PI / 180.f;
3324 float attraction_cone_height = 0.1f;
3327 float attraction_weight = 0.6f;
3330 float attraction_obstacle_reduction_factor = 0.5f;
3333 bool printmessages =
true;
3342 void initializeAlmondTreeShoots();
3346 void initializeAlmondTreeAldrichShoots();
3350 void initializeAlmondTreeWoodColonyShoots();
3354 void initializeAppleTreeShoots();
3358 void initializeAppleFruitingWallShoots();
3362 void initializeAsparagusShoots();
3366 void initializeBindweedShoots();
3370 void initializeBeanShoots();
3374 void initializeBougainvilleaShoots();
3378 void initializeCapsicumShoots();
3382 void initializeCheeseweedShoots();
3386 void initializeCowpeaShoots();
3390 void initializeGrapevineVSPShoots();
3394 void initializeGrapevineWyeShoots();
3398 void initializeGroundCherryWeedShoots();
3402 void initializeMaizeShoots();
3406 void initializeOliveTreeShoots();
3410 void initializePistachioTreeShoots();
3414 void initializePuncturevineShoots();
3418 void initializeEasternRedbudShoots();
3422 void initializeRiceShoots();
3426 void initializeButterLettuceShoots();
3430 void initializeSoybeanShoots();
3434 void initializeSorghumShoots();
3438 void initializeStrawberryShoots();
3442 void initializeSugarbeetShoots();
3446 void initializeTomatoShoots();
3450 void initializeCherryTomatoShoots();
3454 void initializeWalnutTreeShoots();
3458 void initializeWheatShoots();