571 struct InternodeParameters {
586 std::string image_texture;
588 uint length_segments;
590 uint radial_subdivisions;
592 InternodeParameters &operator=(
const InternodeParameters &a) {
594 this->pitch = a.pitch;
595 if (a.pitch.distribution !=
"constant")
596 this->pitch.resample();
597 this->phyllotactic_angle = a.phyllotactic_angle;
598 if (a.phyllotactic_angle.distribution !=
"constant")
599 this->phyllotactic_angle.resample();
600 this->radius_initial = a.radius_initial;
601 if (a.radius_initial.distribution !=
"constant")
602 this->radius_initial.resample();
603 this->max_vegetative_buds_per_petiole = a.max_vegetative_buds_per_petiole;
604 if (a.max_vegetative_buds_per_petiole.distribution !=
"constant")
605 this->max_vegetative_buds_per_petiole.resample();
606 this->max_floral_buds_per_petiole = a.max_floral_buds_per_petiole;
607 if (a.max_floral_buds_per_petiole.distribution !=
"constant")
608 this->max_floral_buds_per_petiole.resample();
609 this->color = a.color;
610 this->image_texture = a.image_texture;
611 this->length_segments = a.length_segments;
612 this->radial_subdivisions = a.radial_subdivisions;
618 struct PetioleParameters {
621 uint petioles_per_internode;
635 uint length_segments;
637 uint radial_subdivisions;
639 PetioleParameters &operator=(
const PetioleParameters &a) {
641 this->petioles_per_internode = a.petioles_per_internode;
642 this->pitch = a.pitch;
643 if (a.pitch.distribution !=
"constant")
644 this->pitch.resample();
645 this->radius = a.radius;
646 if (a.radius.distribution !=
"constant")
647 this->radius.resample();
648 this->length = a.length;
649 if (a.length.distribution !=
"constant")
650 this->length.resample();
651 this->curvature = a.curvature;
652 if (a.curvature.distribution !=
"constant")
653 this->curvature.resample();
654 this->taper = a.taper;
655 if (a.taper.distribution !=
"constant")
656 this->taper.resample();
657 this->color = a.color;
658 this->length_segments = a.length_segments;
659 this->radial_subdivisions = a.radial_subdivisions;
665 struct LeafParameters {
683 LeafParameters &operator=(
const LeafParameters &a) {
685 this->leaves_per_petiole = a.leaves_per_petiole;
686 if (a.leaves_per_petiole.distribution !=
"constant")
687 this->leaves_per_petiole.resample();
688 this->pitch = a.pitch;
689 if (a.pitch.distribution !=
"constant")
690 this->pitch.resample();
692 if (a.yaw.distribution !=
"constant")
693 this->yaw.resample();
695 if (a.roll.distribution !=
"constant")
696 this->roll.resample();
697 this->leaflet_offset = a.leaflet_offset;
698 if (a.leaflet_offset.distribution !=
"constant")
699 this->leaflet_offset.resample();
700 this->leaflet_scale = a.leaflet_scale;
701 if (a.leaflet_scale.distribution !=
"constant")
702 this->leaflet_scale.resample();
703 this->prototype_scale = a.prototype_scale;
704 if (a.prototype_scale.distribution !=
"constant")
705 this->prototype_scale.resample();
706 this->prototype.duplicate(a.prototype);
712 struct PeduncleParameters {
726 uint length_segments;
728 uint radial_subdivisions;
730 PeduncleParameters &operator=(
const PeduncleParameters &a) {
732 this->length = a.length;
733 if (a.length.distribution !=
"constant")
734 this->length.resample();
735 this->radius = a.radius;
736 if (a.radius.distribution !=
"constant")
737 this->radius.resample();
738 this->pitch = a.pitch;
739 if (a.pitch.distribution !=
"constant")
740 this->pitch.resample();
742 if (a.roll.distribution !=
"constant")
743 this->roll.resample();
744 this->curvature = a.curvature;
745 if (a.curvature.distribution !=
"constant")
746 this->curvature.resample();
747 this->color = a.color;
748 this->length_segments = a.length_segments;
749 this->radial_subdivisions = a.radial_subdivisions;
755 struct InflorescenceParameters {
776 uint unique_prototypes;
778 InflorescenceParameters &operator=(
const InflorescenceParameters &a) {
780 this->flowers_per_peduncle = a.flowers_per_peduncle;
781 this->flowers_per_peduncle.resample();
782 this->flower_offset = a.flower_offset;
783 this->flower_offset.resample();
784 this->pitch = a.pitch;
785 this->pitch.resample();
787 this->roll.resample();
788 this->flower_prototype_scale = a.flower_prototype_scale;
789 this->flower_prototype_scale.resample();
790 this->flower_prototype_function = a.flower_prototype_function;
791 this->fruit_prototype_scale = a.fruit_prototype_scale;
792 this->fruit_prototype_scale.resample();
793 this->fruit_prototype_function = a.fruit_prototype_function;
794 this->fruit_gravity_factor_fraction = a.fruit_gravity_factor_fraction;
795 this->fruit_gravity_factor_fraction.resample();
796 this->unique_prototypes = a.unique_prototypes;
1027 float internode_radius,
float internode_length_max,
float internode_length_scale_factor_fraction,
float leaf_scale_factor_fraction,
uint rank,
PlantArchitecture *plantarchitecture_ptr,
helios::Context *context_ptr);
1139 [[nodiscard]]
bool hasLeaf()
const;
1392 helios::vec3 calculatePetioleCollisionAvoidanceDirection(
const helios::vec3 &petiole_base_origin,
const helios::vec3 &proposed_petiole_axis,
bool &collision_detection_active)
const;
1401 helios::vec3 calculateFruitCollisionAvoidanceDirection(
const helios::vec3 &fruit_base_origin,
const helios::vec3 &proposed_fruit_axis,
bool &collision_detection_active)
const;
1409 std::vector<std::vector<helios::vec3>> leaf_bases;
1410 std::vector<std::vector<std::vector<helios::vec3>>> peduncle_vertices;
1411 std::vector<std::vector<std::vector<float>>> peduncle_radii;
1412 std::vector<std::vector<float>> peduncle_length;
1413 std::vector<std::vector<float>> peduncle_radius;
1414 std::vector<std::vector<float>> peduncle_pitch;
1415 std::vector<std::vector<float>> peduncle_curvature;
1416 float internode_pitch, internode_phyllotactic_angle;
1418 std::vector<std::vector<float>> petiole_radii;
1419 std::vector<float> petiole_length;
1420 std::vector<float> petiole_pitch;
1421 std::vector<float> petiole_curvature;
1422 std::vector<float> petiole_taper;
1423 std::vector<helios::vec3> petiole_axis_initial;
1424 std::vector<helios::vec3> petiole_rotation_axis;
1425 std::vector<std::vector<float>> leaf_size_max;
1426 std::vector<std::vector<AxisRotation>> leaf_rotation;
1428 std::vector<helios::RGBcolor> internode_colors;
1429 std::vector<helios::RGBcolor> petiole_colors;
1431 std::vector<std::vector<uint>> petiole_objIDs;
1432 std::vector<std::vector<uint>> leaf_objIDs;
1441 uint parent_shoot_ID;
1442 Shoot *parent_shoot_ptr;
1446 bool isdormant =
false;
1448 float current_internode_scale_factor = 1;
1449 std::vector<float> current_leaf_scale_factor;
1451 float old_phytomer_volume = 0;
1453 float downstream_leaf_area = 0;
1455 std::vector<std::vector<VegetativeBud>> axillary_vegetative_buds;
1456 std::vector<std::vector<FloralBud>> floral_buds;
1458 float internode_radius_initial;
1459 float internode_radius_max;
1460 float internode_length_max;
1465 bool build_context_geometry_petiole =
true;
1466 bool build_context_geometry_peduncle =
true;
1475 void updateInflorescence(
FloralBud &fbud);
1493 void createInflorescenceGeometry(
FloralBud &fbud,
const helios::vec3 &fruit_base,
const helios::vec3 &peduncle_axis,
float pitch,
float roll,
float azimuth,
float yaw_compound,
float scale_factor,
bool is_open_flower);
1500 [[nodiscard]]
float calculatePhytomerConstructionCosts()
const;
1509 [[nodiscard]]
float calculateFlowerConstructionCosts(
const FloralBud &fbud)
const;
1516 [[nodiscard]]
float calculateFruitConstructionCosts(
const FloralBud &fbud)
const;
1518 friend struct Shoot;
1845 static int selfTest(
int argc,
char **argv);
1913 const std::map<std::string, float> &build_parameters = {});
1961 [[nodiscard]] std::vector<std::string>
listShootTypeLabels(
const std::string &plant_model_name);
2032 void setPlantPhenologicalThresholds(
uint plantID,
float time_to_dormancy_break,
float time_to_flower_initiation,
float time_to_flower_opening,
float time_to_fruit_set,
float time_to_fruit_maturity,
float time_to_dormancy,
2033 float max_leaf_lifespan = 1e6,
bool is_evergreen =
false);
2067 void advanceTime(
int time_step_years,
float time_step_days);
2081 void advanceTime(
const std::vector<uint> &plantIDs,
float time_step_days);
2134 uint addBaseStemShoot(
uint plantID,
uint current_node_number,
const AxisRotation &base_rotation,
float internode_radius,
float internode_length_max,
float internode_length_scale_factor_fraction,
float leaf_scale_factor_fraction,
2135 float radius_taper,
const std::string &shoot_type_label);
2151 uint appendShoot(
uint plantID,
int parent_shoot_ID,
uint current_node_number,
const AxisRotation &base_rotation,
float internode_radius,
float internode_length_max,
float internode_length_scale_factor_fraction,
float leaf_scale_factor_fraction,
2152 float radius_taper,
const std::string &shoot_type_label);
2170 uint addChildShoot(
uint plantID,
int parent_shoot_ID,
uint parent_node_index,
uint current_node_number,
const AxisRotation &shoot_base_rotation,
float internode_radius,
float internode_length_max,
float internode_length_scale_factor_fraction,
2171 float leaf_scale_factor_fraction,
float radius_taper,
const std::string &shoot_type_label,
uint petiole_index = 0);
2188 uint addEpicormicShoot(
uint plantID,
int parent_shoot_ID,
float parent_position_fraction,
uint current_node_number,
float zenith_perturbation_degrees,
float internode_radius,
float internode_length_max,
2189 float internode_length_scale_factor_fraction,
float leaf_scale_factor_fraction,
float radius_taper,
const std::string &shoot_type_label);
2202 int appendPhytomerToShoot(
uint plantID,
uint shootID,
const PhytomerParameters &phytomer_parameters,
float internode_radius,
float internode_length_max,
float internode_length_scale_factor_fraction,
float leaf_scale_factor_fraction);
2237 void enableSoftCollisionAvoidance(
const std::vector<uint> &target_object_UUIDs = {},
const std::vector<uint> &target_object_IDs = {},
bool enable_petiole_collision =
false,
bool enable_fruit_collision =
false);
2271 void setCollisionRelevantOrgans(
bool include_internodes,
bool include_leaves,
bool include_petioles,
bool include_flowers,
bool include_fruit);
2286 void enableSolidObstacleAvoidance(
const std::vector<uint> &obstacle_UUIDs,
float avoidance_distance = 0.5f,
bool enable_fruit_adjustment =
false,
bool enable_obstacle_pruning =
false);
2304 void enableAttractionPoints(
const std::vector<helios::vec3> &attraction_points,
float view_half_angle_deg = 45.0f,
float look_ahead_distance = 0.1f,
float attraction_weight = 0.6f);
2328 void setAttractionParameters(
float view_half_angle_deg,
float look_ahead_distance,
float attraction_weight,
float obstacle_reduction_factor = 0.75f);
2338 void enableAttractionPoints(
uint plantID,
const std::vector<helios::vec3> &attraction_points,
float view_half_angle_deg = 80.0f,
float look_ahead_distance = 0.1f,
float attraction_weight = 0.6f);
2368 void setAttractionParameters(
uint plantID,
float view_half_angle_deg,
float look_ahead_distance,
float attraction_weight,
float obstacle_reduction_factor = 0.75f);
2502 void setPlantLeafAngleDistribution(
uint plantID,
float Beta_mu_inclination,
float Beta_nu_inclination,
float eccentricity,
float ellipse_rotation_degrees)
const;
2518 void setPlantLeafAngleDistribution(
const std::vector<uint> &plantIDs,
float Beta_mu_inclination,
float Beta_nu_inclination,
float eccentricity,
float ellipse_rotation_degrees)
const;
2657 [[nodiscard]] std::vector<helios::vec3>
getPlantBasePosition(
const std::vector<uint> &plantIDs)
const;
2735 [[nodiscard]] std::vector<helios::vec3>
getPlantLeafBases(
const std::vector<uint> &plantIDs)
const;
2777 [[nodiscard]] std::vector<uint>
getAllPlantUUIDs(
uint plantID,
bool include_hidden =
false)
const;
2867 [[nodiscard]] std::vector<uint>
getAllUUIDs()
const;
3020 uint generatePlantFromString(
const std::string &generation_string,
const std::map<std::string, PhytomerParameters> &phytomer_parameters);
3141 friend struct Shoot;
3155 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;
3158 void clearBVHCache()
const;
3161 void rebuildBVHForTimestep();
3174 void setPlantAttractionPoints(
uint plantID,
const std::vector<helios::vec3> &attraction_points,
float view_half_angle_deg = 80.0f,
float look_ahead_distance = 0.1f,
float attraction_weight = 0.6f,
float obstacle_reduction_factor = 0.75f);
3183 void ensureInflorescencePrototypesInitialized(
const PhytomerParameters ¶ms,
const std::string &plant_name);
3188 std::minstd_rand0 *generator =
nullptr;
3190 uint plant_count = 0;
3192 std::string current_plant_model;
3194 std::function<void(
float,
const std::string&)> progress_callback;
3199 volatile int *cancel_flag =
nullptr;
3202 std::map<std::string, float> current_build_parameters;
3205 std::map<std::string, std::function<void()>> shoot_initializers;
3206 std::map<std::string, std::function<
uint(
const helios::vec3 &)>> plant_builders;
3207 std::map<std::string, std::string> plant_type_map;
3209 std::map<uint, PlantInstance> plant_instances;
3211 [[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;
3213 std::map<std::string, ShootParameters> shoot_types;
3217 std::map<uint, std::vector<std::vector<uint>>> unique_leaf_prototype_objIDs;
3220 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions,
bool flower_is_open), std::vector<uint>> unique_open_flower_prototype_objIDs;
3222 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions,
bool flower_is_open), std::vector<uint>> unique_closed_flower_prototype_objIDs;
3224 std::map<
uint (*)(
helios::Context *context_ptr,
uint subdivisions), std::vector<uint>> unique_fruit_prototype_objIDs;
3227 [[nodiscard]] std::vector<uint> getAllPrototypeObjectIDs()
const;
3230 void deleteAllPrototypes();
3232 bool build_context_geometry_internode =
true;
3233 bool build_context_geometry_petiole =
true;
3234 bool build_context_geometry_peduncle =
true;
3236 float ground_clipping_height = -99999;
3238 void validateShootTypes(
ShootParameters &shoot_parameters,
const std::map<std::string, ShootParameters> &shoot_types_ref)
const;
3247 void registerPlantModel(
const std::string &name, std::function<
void()> shoot_init, std::function<
uint(
const helios::vec3 &)> plant_build,
const std::string &plant_type =
"herbaceous");
3250 void initializePlantModelRegistrations();
3252 void parseStringShoot(
const std::string &LString_shoot,
uint plantID,
int parentID,
uint parent_node,
const std::map<std::string, PhytomerParameters> &phytomer_parameters,
ShootParameters &shoot_parameters);
3254 void parseShootArgument(
const std::string &shoot_argument,
const std::map<std::string, PhytomerParameters> &phytomer_parameters,
ShootParameters &shoot_parameters,
AxisRotation &base_rotation, std::string &phytomer_label);
3256 void parseInternodeArgument(
const std::string &internode_argument,
float &internode_radius,
float &internode_length,
PhytomerParameters &phytomer_parameters);
3258 void parsePetioleArgument(
const std::string &petiole_argument,
PhytomerParameters &phytomer_parameters);
3260 void parseLeafArgument(
const std::string &leaf_argument,
PhytomerParameters &phytomer_parameters);
3262 void initializeDefaultShoots(
const std::string &plant_label);
3264 [[nodiscard]]
bool detectGroundCollision(
uint objID);
3266 [[nodiscard]]
bool detectGroundCollision(
const std::vector<uint> &objID)
const;
3268 void setPlantLeafAngleDistribution_private(
const std::vector<uint> &plantIDs,
float Beta_mu_inclination,
float Beta_nu_inclination,
float eccentricity_azimuth,
float ellipse_rotation_azimuth_degrees,
bool set_elevation,
bool set_azimuth)
const;
3270 static float interpolateTube(
const std::vector<float> &P,
float frac);
3275 std::map<std::string, bool> output_object_data;
3279 void incrementPhytomerInternodeGirth(
uint plantID,
uint shootID,
uint node_number,
float dt,
bool update_context_geometry);
3280 void incrementPhytomerInternodeGirth_carb(
uint plantID,
uint shootID,
uint node_number,
float dt,
bool update_context_geometry);
3282 void pruneGroundCollisions(
uint plantID);
3284 void pruneSolidBoundaryCollisions();
3288 void accumulateShootPhotosynthesis()
const;
3290 void subtractShootMaintenanceCarbon(
float dt)
const;
3292 void subtractShootGrowthCarbon();
3294 void checkCarbonPool_abortOrgans(
float dt);
3295 void checkCarbonPool_adjustPhyllochron(
float dt);
3296 void checkCarbonPool_transferCarbon(
float dt);
3298 bool carbon_model_enabled =
false;
3302 void accumulateLeafNitrogen(
float dt);
3303 void remobilizeNitrogen(
float dt);
3304 void updateNitrogenStressFactor();
3305 void removeFruitNitrogen();
3307 bool nitrogen_model_enabled =
false;
3315 bool owns_collision_detection =
false;
3318 bool collision_detection_enabled =
false;
3321 std::vector<uint> collision_target_UUIDs;
3324 std::vector<uint> collision_target_object_IDs;
3327 float collision_cone_half_angle_rad = 80.f *
M_PI / 180.f;
3330 float collision_cone_height = 0.1f;
3333 int collision_sample_count = 256;
3336 float collision_inertia_weight = 0.4f;
3339 int geometry_update_frequency = 3;
3342 bool force_update_on_collision =
true;
3345 bool collision_include_internodes =
false;
3346 bool collision_include_leaves =
true;
3347 bool collision_include_petioles =
false;
3348 bool collision_include_flowers =
false;
3349 bool collision_include_fruit =
false;
3352 bool petiole_collision_detection_enabled =
false;
3355 bool fruit_collision_detection_enabled =
false;
3358 int geometry_update_counter = 0;
3361 mutable bool collision_avoidance_applied =
false;
3364 bool spatial_filtering_enabled =
false;
3367 float spatial_max_distance = 5.0f;
3370 mutable bool bvh_cached_for_current_growth =
false;
3371 mutable std::vector<uint> cached_target_geometry;
3372 mutable std::vector<uint> cached_filtered_geometry;
3375 bool solid_obstacle_avoidance_enabled =
false;
3376 std::vector<uint> solid_obstacle_UUIDs;
3377 float solid_obstacle_avoidance_distance = 0.5f;
3378 float solid_obstacle_minimum_distance = 0.05f;
3379 bool solid_obstacle_fruit_adjustment_enabled =
false;
3380 bool solid_obstacle_pruning_enabled =
false;
3385 bool attraction_points_enabled =
false;
3388 std::vector<helios::vec3> attraction_points;
3391 float attraction_cone_half_angle_rad = 80.f *
M_PI / 180.f;
3394 float attraction_cone_height = 0.1f;
3397 float attraction_weight = 0.6f;
3400 float attraction_obstacle_reduction_factor = 0.5f;
3403 bool printmessages =
true;
3412 void initializeAlmondTreeShoots();
3416 void initializeAlmondTreeAldrichShoots();
3420 void initializeAlmondTreeWoodColonyShoots();
3424 void initializeAppleTreeShoots();
3428 void initializeAppleFruitingWallShoots();
3432 void initializeAsparagusShoots();
3436 void initializeBindweedShoots();
3440 void initializeBeanShoots();
3444 void initializeBougainvilleaShoots();
3448 void initializeCapsicumShoots();
3452 void initializeCheeseweedShoots();
3456 void initializeCowpeaShoots();
3460 void initializeGrapevineVSPShoots();
3464 void initializeGrapevineWyeShoots();
3468 void initializeGroundCherryWeedShoots();
3472 void initializeMaizeShoots();
3476 void initializeOliveTreeShoots();
3480 void initializePistachioTreeShoots();
3484 void initializePuncturevineShoots();
3488 void initializeEasternRedbudShoots();
3492 void initializeRiceShoots();
3496 void initializeButterLettuceShoots();
3500 void initializeSoybeanShoots();
3504 void initializeSorghumShoots();
3508 void initializeStrawberryShoots();
3512 void initializeSugarbeetShoots();
3516 void initializeTomatoShoots();
3520 void initializeCherryTomatoShoots();
3524 void initializeWalnutTreeShoots();
3528 void initializeWheatShoots();