854 std::cout <<
"Loading plant architecture XML file: " << filename <<
"..." << std::flush;
857 std::string fn = filename;
859 if (ext !=
".xml" && ext !=
".XML") {
863 std::vector<uint> plantIDs;
866 pugi::xml_document xmldoc;
870 std::string resolved_filename = resolved_path.string();
873 pugi::xml_parse_result load_result = xmldoc.load_file(resolved_filename.c_str());
877 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): Could not parse " + std::string(filename) +
":\nError description: " + load_result.description());
880 pugi::xml_node helios = xmldoc.child(
"helios");
883 std::string node_string;
885 if (helios.empty()) {
887 std::cout <<
"failed." << std::endl;
889 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): XML file must have tag '<helios> ... </helios>' bounding all other tags.");
892 size_t phytomer_count = 0;
894 std::map<int, int> shoot_ID_mapping;
896 for (pugi::xml_node plant = helios.child(
"plant_instance"); plant; plant = plant.next_sibling(
"plant_instance")) {
898 int plantID = std::stoi(plant.attribute(
"ID").value());
901 node_string =
"base_position";
902 vec3 base_position =
parse_xml_tag_vec3(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
905 node_string =
"plant_age";
906 float plant_age =
parse_xml_tag_float(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
909 plantIDs.push_back(plantID);
911 int current_shoot_ID;
913 for (pugi::xml_node shoot = plant.child(
"shoot"); shoot; shoot = shoot.next_sibling(
"shoot")) {
915 int shootID = std::stoi(shoot.attribute(
"ID").value());
916 bool base_shoot =
true;
919 node_string =
"shoot_type_label";
920 std::string shoot_type_label =
parse_xml_tag_string(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
923 node_string =
"parent_shoot_ID";
924 int parent_shoot_ID =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
927 node_string =
"parent_node_index";
928 int parent_node_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
931 node_string =
"parent_petiole_index";
932 int parent_petiole_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
935 node_string =
"base_rotation";
936 vec3 base_rot =
parse_xml_tag_vec3(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
939 for (pugi::xml_node phytomer = shoot.child(
"phytomer"); phytomer; phytomer = phytomer.next_sibling(
"phytomer")) {
941 pugi::xml_node internode = phytomer.child(
"internode");
944 node_string =
"internode_length";
945 float internode_length =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
948 node_string =
"internode_radius";
949 float internode_radius =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
952 node_string =
"internode_pitch";
953 float internode_pitch =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
956 node_string =
"internode_phyllotactic_angle";
957 float internode_phyllotactic_angle =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
961 float internode_length_max = internode_length;
962 node_string =
"internode_length_max";
963 if (internode.child(node_string.c_str())) {
964 internode_length_max =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
968 uint internode_length_segments = 1;
969 node_string =
"internode_length_segments";
970 if (internode.child(node_string.c_str())) {
971 internode_length_segments = (
uint)
parse_xml_tag_int(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
975 std::vector<float> curvature_perturbations;
976 node_string =
"curvature_perturbations";
977 if (internode.child(node_string.c_str())) {
978 std::string perturbs_str = internode.child_value(node_string.c_str());
979 std::istringstream perturbs_stream(perturbs_str);
980 std::string pert_str;
981 while (std::getline(perturbs_stream, pert_str,
';')) {
982 curvature_perturbations.push_back(std::stof(pert_str));
987 std::vector<float> yaw_perturbations;
988 node_string =
"yaw_perturbations";
989 if (internode.child(node_string.c_str())) {
990 std::string yaw_perturbs_str = internode.child_value(node_string.c_str());
991 std::istringstream yaw_stream(yaw_perturbs_str);
993 while (std::getline(yaw_stream, yaw_str,
';')) {
994 yaw_perturbations.push_back(std::stof(yaw_str));
999 std::vector<vec3> internode_vertices;
1000 node_string =
"internode_vertices";
1001 if (internode.child(node_string.c_str())) {
1002 std::string vertices_str = internode.child_value(node_string.c_str());
1003 std::istringstream verts_stream(vertices_str);
1004 std::string vertex_str;
1005 while (std::getline(verts_stream, vertex_str,
';')) {
1006 std::istringstream vertex_coords(vertex_str);
1008 if (vertex_coords >> x >> y >> z) {
1009 internode_vertices.push_back(
make_vec3(x, y, z));
1015 std::vector<float> internode_radii;
1016 node_string =
"internode_radii";
1017 if (internode.child(node_string.c_str())) {
1018 std::string radii_str = internode.child_value(node_string.c_str());
1019 std::istringstream radii_stream(radii_str);
1020 std::string radius_str;
1021 while (std::getline(radii_stream, radius_str,
';')) {
1022 float radius = std::stof(radius_str);
1023 internode_radii.push_back(radius);
1027 float petiole_length;
1028 float petiole_radius;
1029 float petiole_pitch;
1030 float petiole_curvature;
1031 float current_leaf_scale_factor_value;
1032 float leaflet_scale;
1033 float leaflet_offset;
1034 std::vector<float> petiole_lengths;
1035 std::vector<float> petiole_radii_values;
1036 std::vector<float> petiole_pitches;
1037 std::vector<float> petiole_curvatures;
1038 std::vector<vec3> petiole_base_positions;
1039 std::vector<float> current_leaf_scale_factors;
1040 std::vector<float> petiole_tapers;
1041 std::vector<uint> petiole_length_segments_all;
1042 std::vector<uint> petiole_radial_subdivisions_all;
1043 std::vector<std::vector<vec3>> saved_leaf_bases_all_petioles;
1044 std::vector<std::vector<float>> leaf_scale;
1045 std::vector<std::vector<float>> leaf_pitch;
1046 std::vector<std::vector<float>> leaf_yaw;
1047 std::vector<std::vector<float>> leaf_roll;
1050 struct FloralBudData {
1055 float current_fruit_scale_factor;
1056 std::vector<vec3> inflorescence_bases_saved;
1057 std::vector<vec3> flower_positions;
1058 std::vector<AxisRotation> flower_rotations;
1059 std::vector<float> flower_base_scales;
1061 float flower_offset = -1;
1063 float peduncle_length = -1;
1064 float peduncle_radius = -1;
1065 float peduncle_pitch = 0;
1066 float peduncle_roll = 0;
1067 float peduncle_curvature = 0;
1069 std::vector<std::vector<FloralBudData>> floral_bud_data;
1070 for (pugi::xml_node petiole = internode.child(
"petiole"); petiole; petiole = petiole.next_sibling(
"petiole")) {
1073 node_string =
"petiole_length";
1074 petiole_length =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1077 node_string =
"petiole_radius";
1078 petiole_radius =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1081 node_string =
"petiole_pitch";
1082 petiole_pitch =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1085 node_string =
"petiole_curvature";
1086 petiole_curvature =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1090 node_string =
"petiole_base_position";
1091 if (petiole.child(node_string.c_str())) {
1092 petiole_base_pos =
parse_xml_tag_vec3(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1096 node_string =
"current_leaf_scale_factor";
1097 current_leaf_scale_factor_value =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1100 node_string =
"petiole_taper";
1101 float petiole_taper =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1104 node_string =
"petiole_length_segments";
1105 uint length_segments = (
uint)
parse_xml_tag_int(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1108 node_string =
"petiole_radial_subdivisions";
1109 uint radial_subdivisions = (
uint)
parse_xml_tag_int(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1112 node_string =
"leaflet_scale";
1113 leaflet_scale =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1116 node_string =
"leaflet_offset";
1117 if (petiole.child(node_string.c_str())) {
1118 leaflet_offset =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1120 leaflet_offset = 0.f;
1124 petiole_lengths.push_back(petiole_length);
1125 petiole_radii_values.push_back(petiole_radius);
1126 petiole_pitches.push_back(petiole_pitch);
1127 petiole_curvatures.push_back(petiole_curvature);
1128 current_leaf_scale_factors.push_back(current_leaf_scale_factor_value);
1129 petiole_base_positions.push_back(petiole_base_pos);
1130 petiole_tapers.push_back(petiole_taper);
1131 petiole_length_segments_all.push_back(length_segments);
1132 petiole_radial_subdivisions_all.push_back(radial_subdivisions);
1134 leaf_scale.resize(leaf_scale.size() + 1);
1135 leaf_pitch.resize(leaf_pitch.size() + 1);
1136 leaf_yaw.resize(leaf_yaw.size() + 1);
1137 leaf_roll.resize(leaf_roll.size() + 1);
1138 floral_bud_data.resize(floral_bud_data.size() + 1);
1140 std::vector<vec3> saved_leaf_bases;
1142 for (pugi::xml_node leaf = petiole.child(
"leaf"); leaf; leaf = leaf.next_sibling(
"leaf")) {
1145 node_string =
"leaf_scale";
1146 leaf_scale.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1149 node_string =
"leaf_base";
1150 if (leaf.child(node_string.c_str())) {
1151 std::string base_str = leaf.child_value(node_string.c_str());
1152 std::istringstream base_stream(base_str);
1154 if (base_stream >> x >> y >> z) {
1155 saved_leaf_bases.push_back(
make_vec3(x, y, z));
1160 node_string =
"leaf_pitch";
1161 leaf_pitch.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1164 node_string =
"leaf_yaw";
1165 leaf_yaw.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1168 node_string =
"leaf_roll";
1169 leaf_roll.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1173 saved_leaf_bases_all_petioles.push_back(saved_leaf_bases);
1176 for (pugi::xml_node floral_bud = petiole.child(
"floral_bud"); floral_bud; floral_bud = floral_bud.next_sibling(
"floral_bud")) {
1178 FloralBudData fbud_data;
1181 node_string =
"bud_state";
1182 fbud_data.bud_state =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1185 node_string =
"parent_index";
1186 fbud_data.parent_index =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1189 node_string =
"bud_index";
1190 fbud_data.bud_index =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1193 node_string =
"is_terminal";
1194 int is_terminal =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1195 fbud_data.is_terminal = (is_terminal != 0);
1198 node_string =
"current_fruit_scale_factor";
1199 fbud_data.current_fruit_scale_factor =
parse_xml_tag_float(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1202 pugi::xml_node peduncle = floral_bud.child(
"peduncle");
1204 node_string =
"length";
1205 if (peduncle.child(node_string.c_str())) {
1206 fbud_data.peduncle_length =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1208 node_string =
"radius";
1209 if (peduncle.child(node_string.c_str())) {
1210 fbud_data.peduncle_radius =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1212 node_string =
"pitch";
1213 if (peduncle.child(node_string.c_str())) {
1214 fbud_data.peduncle_pitch =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1216 node_string =
"roll";
1217 if (peduncle.child(node_string.c_str())) {
1218 fbud_data.peduncle_roll =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1220 node_string =
"curvature";
1221 if (peduncle.child(node_string.c_str())) {
1222 fbud_data.peduncle_curvature =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1227 pugi::xml_node inflorescence = floral_bud.child(
"inflorescence");
1228 if (inflorescence) {
1230 node_string =
"flower_offset";
1231 if (inflorescence.child(node_string.c_str())) {
1232 fbud_data.flower_offset =
parse_xml_tag_float(inflorescence.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1236 for (pugi::xml_node flower = inflorescence.child(
"flower"); flower; flower = flower.next_sibling(
"flower")) {
1238 pugi::xml_node base_node = flower.child(
"inflorescence_base");
1240 vec3 base =
parse_xml_tag_vec3(base_node,
"inflorescence_base",
"PlantArchitecture::readPlantStructureXML");
1241 fbud_data.inflorescence_bases_saved.push_back(base);
1246 pugi::xml_node pitch_node = flower.child(
"flower_pitch");
1247 pugi::xml_node yaw_node = flower.child(
"flower_yaw");
1248 pugi::xml_node roll_node = flower.child(
"flower_roll");
1249 pugi::xml_node azimuth_node = flower.child(
"flower_azimuth");
1252 rotation.pitch =
parse_xml_tag_float(pitch_node,
"flower_pitch",
"PlantArchitecture::readPlantStructureXML");
1258 rotation.yaw =
parse_xml_tag_float(yaw_node,
"flower_yaw",
"PlantArchitecture::readPlantStructureXML");
1264 rotation.roll =
parse_xml_tag_float(roll_node,
"flower_roll",
"PlantArchitecture::readPlantStructureXML");
1270 rotation.azimuth =
parse_xml_tag_float(azimuth_node,
"flower_azimuth",
"PlantArchitecture::readPlantStructureXML");
1272 rotation.azimuth = 0;
1275 fbud_data.flower_rotations.push_back(rotation);
1278 pugi::xml_node scale_node = flower.child(
"flower_base_scale");
1280 float base_scale =
parse_xml_tag_float(scale_node,
"flower_base_scale",
"PlantArchitecture::readPlantStructureXML");
1281 fbud_data.flower_base_scales.push_back(base_scale);
1284 fbud_data.flower_base_scales.push_back(-1.0f);
1289 floral_bud_data.back().push_back(fbud_data);
1293 if (shoot_types.find(shoot_type_label) == shoot_types.end()) {
1294 helios_runtime_error(
"ERROR (PlantArchitecture::readPlantStructureXML): Shoot type " + shoot_type_label +
" not found in shoot types.");
1319 if (parent_shoot_ID < 0) {
1320 current_shoot_ID =
addBaseStemShoot(plantID, 1, base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, shoot_type_label);
1321 shoot_ID_mapping[shootID] = current_shoot_ID;
1323 current_shoot_ID =
addChildShoot(plantID, shoot_ID_mapping.at(parent_shoot_ID), parent_node_index, 1, base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, shoot_type_label, parent_petiole_index);
1324 shoot_ID_mapping[shootID] = current_shoot_ID;
1333 auto phytomer_ptr = plant_instances.at(plantID).shoot_tree.at(current_shoot_ID)->phytomers.back();
1336 phytomer_ptr->internode_pitch =
deg2rad(internode_pitch);
1337 phytomer_ptr->internode_phyllotactic_angle =
deg2rad(internode_phyllotactic_angle);
1340 auto shoot_ptr = plant_instances.at(plantID).shoot_tree.at(current_shoot_ID);
1341 uint phytomer_index_in_shoot = shoot_ptr->phytomers.size() - 1;
1344 auto recomputeInternodeOrientationVectors_local = [
this, plantID](std::shared_ptr<Phytomer> phytomer_ptr,
uint phytomer_index_in_shoot,
float internode_pitch_rad,
float internode_phyllotactic_angle_rad,
1355 if (phytomer_index_in_shoot > 0) {
1356 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1357 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1358 parent_petiole_axis = prev_phytomer->getPetioleAxisVector(0.f, 0);
1359 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1360 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1361 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1362 uint parent_petiole_index = phytomer_ptr->parent_shoot_ptr->parent_petiole_index;
1363 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1364 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1365 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1366 parent_petiole_axis = parent_phytomer->getPetioleAxisVector(0.f, parent_petiole_index);
1369 helios::vec3 petiole_rotation_axis =
cross(parent_internode_axis, parent_petiole_axis);
1370 if (petiole_rotation_axis.
magnitude() < 1e-6f) {
1371 petiole_rotation_axis =
make_vec3(1, 0, 0);
1378 if (phytomer_index_in_shoot == 0) {
1379 AxisRotation shoot_base_rotation = phytomer_ptr->parent_shoot_ptr->base_rotation;
1380 if (internode_pitch_rad != 0.f) {
1383 if (shoot_base_rotation.roll != 0.f) {
1387 if (shoot_base_rotation.pitch != 0.f) {
1388 helios::vec3 base_pitch_axis = -1.0f *
cross(parent_internode_axis, parent_petiole_axis);
1392 if (shoot_base_rotation.yaw != 0.f) {
1397 if (internode_pitch_rad != 0.f) {
1403 if (internode_axis ==
make_vec3(0, 0, 1)) {
1404 shoot_bending_axis =
make_vec3(0, 1, 0);
1409 out_internode_axis_initial = internode_axis;
1410 out_petiole_rotation_axis = petiole_rotation_axis;
1411 out_shoot_bending_axis = shoot_bending_axis;
1415 if (internode_length_segments > 0) {
1418 if (phytomer_index_in_shoot == 0) {
1420 if (shoot_ptr->parent_shoot_ID < 0) {
1426 int parent_shoot_id = shoot_ptr->parent_shoot_ID;
1427 uint parent_node_index = shoot_ptr->parent_node_index;
1428 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1431 internode_base = parent_shoot->shoot_internode_vertices.at(parent_node_index).back();
1435 internode_base = shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot - 1].back();
1443 recomputeInternodeOrientationVectors_local(phytomer_ptr, phytomer_index_in_shoot,
deg2rad(internode_pitch),
deg2rad(internode_phyllotactic_angle), internode_axis_initial, petiole_rotation_axis, shoot_bending_axis);
1446 std::vector<helios::vec3> reconstructed_vertices(internode_length_segments + 1);
1447 std::vector<float> reconstructed_radii(internode_length_segments + 1);
1449 reconstructed_vertices[0] = internode_base;
1450 reconstructed_radii[0] = internode_radius;
1452 float dr = internode_length / float(internode_length_segments);
1453 float dr_max = internode_length_max / float(internode_length_segments);
1457 for (
int i = 1; i <= internode_length_segments; i++) {
1459 if (phytomer_index_in_shoot > 0 && !curvature_perturbations.empty()) {
1461 float current_curvature_fact = 0.5f - internode_axis.
z / 2.0f;
1462 if (internode_axis.
z < 0) {
1463 current_curvature_fact *= 2.0f;
1467 float gravitropic_curvature = shoot_ptr->gravitropic_curvature;
1470 float curvature_angle =
deg2rad(gravitropic_curvature * current_curvature_fact * dr_max + curvature_perturbations[i - 1]);
1474 if (!yaw_perturbations.empty() && (i - 1) < yaw_perturbations.size()) {
1475 float yaw_angle =
deg2rad(yaw_perturbations[i - 1]);
1483 reconstructed_vertices[i] = reconstructed_vertices[i - 1] + dr * internode_axis;
1484 reconstructed_radii[i] = internode_radius;
1488 shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot] = reconstructed_vertices;
1489 shoot_ptr->shoot_internode_radii[phytomer_index_in_shoot] = reconstructed_radii;
1490 }
else if (!internode_vertices.empty() && !internode_radii.empty()) {
1492 shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot] = internode_vertices;
1493 shoot_ptr->shoot_internode_radii[phytomer_index_in_shoot] = internode_radii;
1497 auto recomputePetioleOrientationVectors = [
this, plantID](std::shared_ptr<Phytomer> phytomer_ptr,
uint petiole_index,
uint phytomer_index_in_shoot,
float petiole_pitch_rad,
float internode_phyllotactic_angle_rad,
1509 if (phytomer_index_in_shoot > 0) {
1511 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1512 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1513 parent_petiole_axis = prev_phytomer->getPetioleAxisVector(0.f, 0);
1514 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1516 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1517 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1518 uint parent_petiole_index = phytomer_ptr->parent_shoot_ptr->parent_petiole_index;
1520 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1521 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1522 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1523 parent_petiole_axis = parent_phytomer->getPetioleAxisVector(0.f, parent_petiole_index);
1527 helios::vec3 petiole_rotation_axis =
cross(parent_internode_axis, parent_petiole_axis);
1528 if (petiole_rotation_axis.
magnitude() < 1e-6f) {
1529 petiole_rotation_axis =
make_vec3(1, 0, 0);
1536 float internode_pitch_rad = phytomer_ptr->internode_pitch;
1539 if (phytomer_index_in_shoot == 0) {
1543 if (internode_pitch_rad != 0.f) {
1548 AxisRotation shoot_base_rotation = phytomer_ptr->parent_shoot_ptr->base_rotation;
1550 if (shoot_base_rotation.roll != 0.f) {
1555 if (shoot_base_rotation.pitch != 0.f) {
1556 helios::vec3 base_pitch_axis = -1.0f *
cross(parent_internode_axis, parent_petiole_axis);
1561 if (shoot_base_rotation.yaw != 0.f) {
1567 if (internode_pitch_rad != 0.f) {
1580 if (phytomer_index_in_shoot != 0 && std::abs(internode_phyllotactic_angle_rad) > 0) {
1586 if (petiole_index > 0) {
1587 uint petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
1588 float budrot = float(petiole_index) * 2.0f * float(
M_PI) / float(petioles_per_internode);
1594 out_petiole_axis_initial = petiole_axis;
1595 out_petiole_rotation_axis = petiole_rotation_axis;
1599 auto recomputePeduncleOrientationVectors = [
this, plantID](std::shared_ptr<Phytomer> phytomer_ptr,
uint petiole_index,
uint phytomer_index_in_shoot,
float peduncle_pitch_rad,
float peduncle_roll_rad,
const AxisRotation &base_rotation,
1600 vec3 &out_peduncle_axis_initial,
vec3 &out_peduncle_rotation_axis) ->
void {
1602 vec3 peduncle_axis = phytomer_ptr->getAxisVector(1.f, phytomer_ptr->getInternodeNodePositions());
1610 if (phytomer_index_in_shoot > 0) {
1612 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1613 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1614 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1616 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1617 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1619 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1620 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1621 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1625 vec3 current_petiole_axis;
1626 if (phytomer_ptr->petiole_vertices.empty()) {
1627 current_petiole_axis = parent_internode_axis;
1630 current_petiole_axis = phytomer_ptr->getPetioleAxisVector(0.f, petiole_index);
1633 vec3 inflorescence_bending_axis_actual =
cross(parent_internode_axis, current_petiole_axis);
1634 if (inflorescence_bending_axis_actual.
magnitude() < 0.001f) {
1635 inflorescence_bending_axis_actual =
make_vec3(1, 0, 0);
1637 inflorescence_bending_axis_actual.
normalize();
1640 if (peduncle_pitch_rad != 0.f || base_rotation.pitch != 0.f) {
1645 vec3 internode_axis = phytomer_ptr->getAxisVector(1.f, phytomer_ptr->getInternodeNodePositions());
1646 vec3 parent_petiole_base_axis = phytomer_ptr->petiole_vertices.empty() ? internode_axis : phytomer_ptr->getPetioleAxisVector(0.f, petiole_index);
1648 float parent_petiole_azimuth = -std::atan2(parent_petiole_base_axis.
y, parent_petiole_base_axis.
x);
1649 float current_peduncle_azimuth = -std::atan2(peduncle_axis.
y, peduncle_axis.
x);
1650 float azimuthal_rotation = current_peduncle_azimuth - parent_petiole_azimuth;
1656 out_peduncle_axis_initial = peduncle_axis;
1657 out_peduncle_rotation_axis = inflorescence_bending_axis_actual;
1661 for (
size_t p = 0; p < petiole_lengths.size(); p++) {
1662 if (p < phytomer_ptr->petiole_length.size()) {
1665 phytomer_ptr->petiole_length.at(p) = petiole_lengths[p];
1666 phytomer_ptr->petiole_pitch.at(p) =
deg2rad(petiole_pitches[p]);
1667 phytomer_ptr->petiole_curvature.at(p) = petiole_curvatures[p];
1668 phytomer_ptr->petiole_taper.at(p) = petiole_tapers[p];
1669 phytomer_ptr->current_leaf_scale_factor.at(p) = current_leaf_scale_factors[p];
1672 phytomer_ptr->phytomer_parameters.petiole.length_segments = petiole_length_segments_all[p];
1673 phytomer_ptr->phytomer_parameters.petiole.radial_subdivisions = petiole_radial_subdivisions_all[p];
1676 uint Ndiv_petiole_length = std::max(
uint(1), petiole_length_segments_all[p]);
1677 uint Ndiv_petiole_radius = std::max(
uint(3), petiole_radial_subdivisions_all[p]);
1679 phytomer_ptr->petiole_vertices.at(p).resize(Ndiv_petiole_length + 1);
1680 phytomer_ptr->petiole_radii.at(p).resize(Ndiv_petiole_length + 1);
1684 phytomer_ptr->petiole_vertices.at(p).at(0) = shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot].back();
1685 phytomer_ptr->petiole_radii.at(p).at(0) = petiole_radii_values[p];
1688 float dr_petiole = petiole_lengths[p] / float(Ndiv_petiole_length);
1691 vec3 recomputed_axis;
1692 vec3 recomputed_rotation_axis;
1694 recomputePetioleOrientationVectors(phytomer_ptr,
1696 phytomer_index_in_shoot,
1697 phytomer_ptr->petiole_pitch.at(p),
1698 phytomer_ptr->internode_phyllotactic_angle,
1699 recomputed_axis, recomputed_rotation_axis);
1704 vec3 petiole_axis_actual = recomputed_axis;
1705 vec3 petiole_rotation_axis_actual = recomputed_rotation_axis;
1708 for (
int j = 1; j <= Ndiv_petiole_length; j++) {
1710 if (fabs(petiole_curvatures[p]) > 0) {
1715 phytomer_ptr->petiole_vertices.at(p).at(j) = phytomer_ptr->petiole_vertices.at(p).at(j - 1) + dr_petiole * petiole_axis_actual;
1718 phytomer_ptr->petiole_radii.at(p).at(j) = current_leaf_scale_factors[p] * petiole_radii_values[p] * (1.0f - petiole_tapers[p] / float(Ndiv_petiole_length) * float(j));
1722 context_ptr->
deleteObject(phytomer_ptr->petiole_objIDs[p]);
1723 std::vector<RGBcolor> petiole_colors(phytomer_ptr->petiole_radii[p].size(), phytomer_ptr->phytomer_parameters.petiole.color);
1724 phytomer_ptr->petiole_objIDs[p] =
makeTubeFromCones(Ndiv_petiole_radius, phytomer_ptr->petiole_vertices[p], phytomer_ptr->petiole_radii[p], petiole_colors, context_ptr);
1727 if (!phytomer_ptr->petiole_objIDs[p].empty()) {
1732 if (p < saved_leaf_bases_all_petioles.size() && !saved_leaf_bases_all_petioles[p].empty()) {
1733 for (
size_t leaf = 0; leaf < phytomer_ptr->leaf_bases[p].size(); leaf++) {
1734 if (leaf < saved_leaf_bases_all_petioles[p].size()) {
1735 phytomer_ptr->leaf_bases[p][leaf] = saved_leaf_bases_all_petioles[p][leaf];
1746 std::vector<std::vector<std::map<std::string, int>>> saved_leaf_data_int;
1747 std::vector<std::vector<std::map<std::string, uint>>> saved_leaf_data_uint;
1748 std::vector<std::vector<std::map<std::string, float>>> saved_leaf_data_float;
1749 std::vector<std::vector<std::map<std::string, double>>> saved_leaf_data_double;
1750 std::vector<std::vector<std::map<std::string, vec2>>> saved_leaf_data_vec2;
1751 std::vector<std::vector<std::map<std::string, vec3>>> saved_leaf_data_vec3;
1752 std::vector<std::vector<std::map<std::string, vec4>>> saved_leaf_data_vec4;
1753 std::vector<std::vector<std::map<std::string, int2>>> saved_leaf_data_int2;
1754 std::vector<std::vector<std::map<std::string, int3>>> saved_leaf_data_int3;
1755 std::vector<std::vector<std::map<std::string, int4>>> saved_leaf_data_int4;
1756 std::vector<std::vector<std::map<std::string, std::string>>> saved_leaf_data_string;
1758 saved_leaf_data_int.resize(phytomer_ptr->leaf_objIDs.size());
1759 saved_leaf_data_uint.resize(phytomer_ptr->leaf_objIDs.size());
1760 saved_leaf_data_float.resize(phytomer_ptr->leaf_objIDs.size());
1761 saved_leaf_data_double.resize(phytomer_ptr->leaf_objIDs.size());
1762 saved_leaf_data_vec2.resize(phytomer_ptr->leaf_objIDs.size());
1763 saved_leaf_data_vec3.resize(phytomer_ptr->leaf_objIDs.size());
1764 saved_leaf_data_vec4.resize(phytomer_ptr->leaf_objIDs.size());
1765 saved_leaf_data_int2.resize(phytomer_ptr->leaf_objIDs.size());
1766 saved_leaf_data_int3.resize(phytomer_ptr->leaf_objIDs.size());
1767 saved_leaf_data_int4.resize(phytomer_ptr->leaf_objIDs.size());
1768 saved_leaf_data_string.resize(phytomer_ptr->leaf_objIDs.size());
1770 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
1771 saved_leaf_data_int[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1772 saved_leaf_data_uint[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1773 saved_leaf_data_float[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1774 saved_leaf_data_double[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1775 saved_leaf_data_vec2[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1776 saved_leaf_data_vec3[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1777 saved_leaf_data_vec4[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1778 saved_leaf_data_int2[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1779 saved_leaf_data_int3[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1780 saved_leaf_data_int4[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1781 saved_leaf_data_string[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1783 for (
int leaf = 0; leaf < phytomer_ptr->leaf_objIDs[petiole].size(); leaf++) {
1784 uint objID = phytomer_ptr->leaf_objIDs[petiole][leaf];
1786 if (!UUIDs.empty()) {
1788 std::vector<std::string> data_int = context_ptr->
listPrimitiveData(UUIDs.front());
1789 for (
const auto &label: data_int) {
1794 saved_leaf_data_int[petiole][leaf][label] = value;
1798 saved_leaf_data_uint[petiole][leaf][label] = value;
1802 saved_leaf_data_float[petiole][leaf][label] = value;
1806 saved_leaf_data_double[petiole][leaf][label] = value;
1810 saved_leaf_data_vec2[petiole][leaf][label] = value;
1814 saved_leaf_data_vec3[petiole][leaf][label] = value;
1818 saved_leaf_data_vec4[petiole][leaf][label] = value;
1822 saved_leaf_data_int2[petiole][leaf][label] = value;
1826 saved_leaf_data_int3[petiole][leaf][label] = value;
1830 saved_leaf_data_int4[petiole][leaf][label] = value;
1834 saved_leaf_data_string[petiole][leaf][label] = value;
1842 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
1843 for (
uint objID: phytomer_ptr->leaf_objIDs[petiole]) {
1846 phytomer_ptr->leaf_objIDs[petiole].clear();
1847 phytomer_ptr->leaf_bases[petiole].clear();
1848 phytomer_ptr->leaf_rotation[petiole].clear();
1852 assert(leaf_scale.size() == leaf_pitch.size());
1853 float leaflet_offset_val = 0.f;
1856 if (leaf_scale.size() > 0) {
1858 int max_leaves_per_petiole = 0;
1859 for (
size_t i = 0; i < leaf_scale.size(); i++) {
1860 max_leaves_per_petiole = std::max(max_leaves_per_petiole, (
int) leaf_scale[i].size());
1862 int leaves_per_petiole = max_leaves_per_petiole;
1863 assert(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier != 0);
1865 if (phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes > 0 &&
1866 this->unique_leaf_prototype_objIDs.find(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier) == this->unique_leaf_prototype_objIDs.end()) {
1867 this->unique_leaf_prototype_objIDs[phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier].resize(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes);
1868 for (
int prototype = 0; prototype < phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes; prototype++) {
1869 for (
int leaf = 0; leaf < leaves_per_petiole; leaf++) {
1870 float ind_from_tip = float(leaf) - float(leaves_per_petiole - 1) / 2.f;
1871 uint objID_leaf = phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function(context_ptr, &phytomer_ptr->phytomer_parameters.leaf.prototype, ind_from_tip);
1872 if (phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function == GenericLeafPrototype) {
1875 this->unique_leaf_prototype_objIDs.at(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier).at(prototype).push_back(objID_leaf);
1877 context_ptr->
setPrimitiveColor(petiolule_UUIDs, phytomer_ptr->phytomer_parameters.petiole.color);
1884 for (
int petiole = 0; petiole < leaf_scale.size(); petiole++) {
1885 int leaves_per_petiole = leaf_scale[petiole].size();
1888 vec3 petiole_tip_axis = phytomer_ptr->getPetioleAxisVector(1.f, petiole);
1889 vec3 internode_axis = phytomer_ptr->getInternodeAxisVector(1.f);
1892 phytomer_ptr->leaf_objIDs[petiole].resize(leaves_per_petiole);
1893 phytomer_ptr->leaf_bases[petiole].resize(leaves_per_petiole);
1894 phytomer_ptr->leaf_rotation[petiole].resize(leaves_per_petiole);
1896 for (
int leaf = 0; leaf < leaves_per_petiole; leaf++) {
1897 float ind_from_tip = float(leaf) - float(leaves_per_petiole - 1) / 2.f;
1901 if (phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes > 0) {
1902 int prototype = context_ptr->
randu(0, phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes - 1);
1903 uint uid = phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier;
1904 assert(this->unique_leaf_prototype_objIDs.find(uid) != this->unique_leaf_prototype_objIDs.end());
1905 assert(this->unique_leaf_prototype_objIDs.at(uid).size() > prototype);
1906 assert(this->unique_leaf_prototype_objIDs.at(uid).at(prototype).size() > leaf);
1907 objID_leaf = context_ptr->
copyObject(this->unique_leaf_prototype_objIDs.at(uid).at(prototype).at(leaf));
1909 objID_leaf = phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function(context_ptr, &phytomer_ptr->phytomer_parameters.leaf.prototype, ind_from_tip);
1913 vec3 leaf_scale_vec = leaf_scale[petiole][leaf] *
make_vec3(1, 1, 1);
1914 context_ptr->
scaleObject(objID_leaf, leaf_scale_vec);
1917 float compound_rotation = 0;
1918 if (leaves_per_petiole > 1) {
1919 if (leaf ==
float(leaves_per_petiole - 1) / 2.f) {
1920 compound_rotation = 0;
1921 }
else if (leaf <
float(leaves_per_petiole - 1) / 2.f) {
1922 compound_rotation = -0.5 *
M_PI;
1924 compound_rotation = 0.5 *
M_PI;
1929 float saved_pitch =
deg2rad(leaf_pitch[petiole][leaf]);
1930 float saved_yaw =
deg2rad(leaf_yaw[petiole][leaf]);
1931 float saved_roll =
deg2rad(leaf_roll[petiole][leaf]);
1935 if (leaves_per_petiole == 1) {
1937 roll_rot = (
acos_safe(internode_axis.
z) - saved_roll) * sign;
1938 }
else if (ind_from_tip != 0) {
1939 roll_rot = (
asin_safe(petiole_tip_axis.
z) + saved_roll) * compound_rotation / std::fabs(compound_rotation);
1941 phytomer_ptr->leaf_rotation[petiole][leaf].roll = saved_roll;
1945 phytomer_ptr->leaf_rotation[petiole][leaf].pitch = saved_pitch;
1946 float pitch_rot = saved_pitch;
1947 if (ind_from_tip == 0) {
1950 context_ptr->
rotateObject(objID_leaf, -pitch_rot,
"y");
1953 if (ind_from_tip != 0) {
1954 phytomer_ptr->leaf_rotation[petiole][leaf].yaw = saved_yaw;
1957 phytomer_ptr->leaf_rotation[petiole][leaf].yaw = 0;
1961 context_ptr->
rotateObject(objID_leaf, -std::atan2(petiole_tip_axis.
y, petiole_tip_axis.
x) + compound_rotation,
"z");
1964 vec3 leaf_base = phytomer_ptr->petiole_vertices[petiole].back();
1966 int leaves_per_petiole = phytomer_ptr->phytomer_parameters.leaf.leaves_per_petiole.val();
1967 float leaflet_offset_val = clampOffset(leaves_per_petiole, phytomer_ptr->phytomer_parameters.leaf.leaflet_offset.val());
1969 if (leaves_per_petiole > 1 && leaflet_offset_val > 0) {
1971 int ind_from_tip = leaf - int(floor(
float(leaves_per_petiole - 1) / 2.f));
1972 if (ind_from_tip != 0) {
1973 float offset = (fabs(ind_from_tip) - 0.5f) * leaflet_offset_val * phytomer_ptr->phytomer_parameters.petiole.length.val();
1974 leaf_base = PlantArchitecture::interpolateTube(phytomer_ptr->petiole_vertices[petiole], 1.f - offset / phytomer_ptr->phytomer_parameters.petiole.length.val());
1979 phytomer_ptr->leaf_objIDs[petiole][leaf] = objID_leaf;
1980 phytomer_ptr->leaf_bases[petiole][leaf] = leaf_base;
1985 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
1986 for (
int leaf = 0; leaf < phytomer_ptr->leaf_objIDs[petiole].size(); leaf++) {
1987 uint objID = phytomer_ptr->leaf_objIDs[petiole][leaf];
1990 for (
const auto &pair: saved_leaf_data_int[petiole][leaf]) {
1993 for (
const auto &pair: saved_leaf_data_uint[petiole][leaf]) {
1996 for (
const auto &pair: saved_leaf_data_float[petiole][leaf]) {
1999 for (
const auto &pair: saved_leaf_data_double[petiole][leaf]) {
2002 for (
const auto &pair: saved_leaf_data_vec2[petiole][leaf]) {
2005 for (
const auto &pair: saved_leaf_data_vec3[petiole][leaf]) {
2008 for (
const auto &pair: saved_leaf_data_vec4[petiole][leaf]) {
2011 for (
const auto &pair: saved_leaf_data_int2[petiole][leaf]) {
2014 for (
const auto &pair: saved_leaf_data_int3[petiole][leaf]) {
2017 for (
const auto &pair: saved_leaf_data_int4[petiole][leaf]) {
2020 for (
const auto &pair: saved_leaf_data_string[petiole][leaf]) {
2027 if (!floral_bud_data.empty() && floral_bud_data.size() <= phytomer_ptr->petiole_length.size()) {
2029 phytomer_ptr->floral_buds.resize(floral_bud_data.size());
2031 for (
size_t petiole = 0; petiole < floral_bud_data.size(); petiole++) {
2032 phytomer_ptr->floral_buds.at(petiole).resize(floral_bud_data.at(petiole).size());
2034 for (
size_t bud = 0; bud < floral_bud_data.at(petiole).size(); bud++) {
2035 const FloralBudData &fbud_data = floral_bud_data.at(petiole).at(bud);
2038 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2040 fbud.parent_index = fbud_data.parent_index;
2041 fbud.bud_index = fbud_data.bud_index;
2042 fbud.isterminal = fbud_data.is_terminal;
2043 fbud.current_fruit_scale_factor = fbud_data.current_fruit_scale_factor;
2048 if (fbud.isterminal) {
2050 fbud.base_position = phytomer_ptr->parent_shoot_ptr->shoot_internode_vertices.back().back();
2053 if (petiole < phytomer_ptr->petiole_vertices.size()) {
2054 fbud.base_position = phytomer_ptr->petiole_vertices.at(petiole).front();
2056 helios_runtime_error(
"ERROR (PlantArchitecture::readPlantStructureXML): Floral bud parent_index " + std::to_string(fbud.parent_index) +
" exceeds number of petioles.");
2062 uint Nbuds = floral_bud_data.at(petiole).size();
2063 if (fbud.isterminal) {
2065 float pitch_adjustment = (Nbuds > 1) ?
deg2rad(30.f) : 0.f;
2066 float yaw_adjustment =
static_cast<float>(fbud.bud_index) * 2.f *
M_PI /
float(Nbuds);
2067 fbud.base_rotation = make_AxisRotation(pitch_adjustment, yaw_adjustment, 0.f);
2070 float pitch_adjustment =
static_cast<float>(fbud.bud_index) * 0.1f *
M_PI /
float(Nbuds);
2071 float yaw_adjustment = -0.25f *
M_PI +
static_cast<float>(fbud.bud_index) * 0.5f *
M_PI /
float(Nbuds);
2072 fbud.base_rotation = make_AxisRotation(pitch_adjustment, yaw_adjustment, 0.f);
2078 BudState desired_state =
static_cast<BudState
>(fbud_data.bud_state);
2080 if (desired_state != BUD_DORMANT && desired_state != BUD_DEAD && desired_state != BUD_ACTIVE) {
2082 if (fbud_data.peduncle_length >= 0) {
2083 phytomer_ptr->phytomer_parameters.peduncle.length = fbud_data.peduncle_length;
2085 if (fbud_data.peduncle_radius >= 0) {
2086 phytomer_ptr->phytomer_parameters.peduncle.radius = fbud_data.peduncle_radius;
2088 if (fbud_data.peduncle_pitch != 0) {
2089 phytomer_ptr->phytomer_parameters.peduncle.pitch = fbud_data.peduncle_pitch;
2091 if (fbud_data.peduncle_roll != 0) {
2092 phytomer_ptr->phytomer_parameters.peduncle.roll = fbud_data.peduncle_roll;
2094 if (fbud_data.peduncle_curvature != 0) {
2095 phytomer_ptr->phytomer_parameters.peduncle.curvature = fbud_data.peduncle_curvature;
2099 if (fbud_data.flower_offset >= 0) {
2100 phytomer_ptr->phytomer_parameters.inflorescence.flower_offset = fbud_data.flower_offset;
2103 if ((desired_state == BUD_FLOWER_CLOSED || desired_state == BUD_FLOWER_OPEN) && phytomer_ptr->phytomer_parameters.inflorescence.flower_prototype_function !=
nullptr) {
2104 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2108 fbud.inflorescence_objIDs.clear();
2109 fbud.inflorescence_bases.clear();
2110 fbud.inflorescence_rotation.clear();
2112 if (phytomer_ptr->build_context_geometry_peduncle) {
2114 fbud.peduncle_objIDs.clear();
2118 fbud.state = desired_state;
2119 fbud.time_counter = 0;
2122 if (fbud_data.peduncle_length > 0 && fbud_data.peduncle_radius > 0) {
2125 uint Ndiv_peduncle_length = std::max(
uint(1), phytomer_ptr->phytomer_parameters.peduncle.length_segments);
2126 uint Ndiv_peduncle_radius = std::max(
uint(3), phytomer_ptr->phytomer_parameters.peduncle.radial_subdivisions);
2129 std::vector<vec3> peduncle_vertices_computed(Ndiv_peduncle_length + 1);
2130 std::vector<float> peduncle_radii_computed(Ndiv_peduncle_length + 1);
2133 peduncle_vertices_computed.at(0) = fbud.base_position;
2134 peduncle_radii_computed.at(0) = fbud_data.peduncle_radius;
2136 float dr_peduncle = fbud_data.peduncle_length / float(Ndiv_peduncle_length);
2139 vec3 peduncle_axis_computed;
2140 vec3 peduncle_rotation_axis_computed;
2142 recomputePeduncleOrientationVectors(phytomer_ptr, fbud.parent_index, phytomer_index_in_shoot,
deg2rad(fbud_data.peduncle_pitch),
deg2rad(fbud_data.peduncle_roll), fbud.base_rotation, peduncle_axis_computed,
2143 peduncle_rotation_axis_computed);
2146 vec3 peduncle_axis_actual = peduncle_axis_computed;
2149 for (
int i = 1; i <= Ndiv_peduncle_length; i++) {
2150 if (fabs(fbud_data.peduncle_curvature) > 0) {
2151 float curvature_value = fbud_data.peduncle_curvature;
2155 float axis_magnitude = horizontal_bending_axis.
magnitude();
2157 if (axis_magnitude > 0.001f) {
2158 horizontal_bending_axis = horizontal_bending_axis / axis_magnitude;
2160 float theta_curvature =
deg2rad(curvature_value * dr_peduncle);
2161 float theta_from_target = (curvature_value > 0) ? std::acos(std::min(1.0f, std::max(-1.0f, peduncle_axis_actual.
z))) : std::acos(std::min(1.0f, std::max(-1.0f, -peduncle_axis_actual.
z)));
2163 if (fabs(theta_curvature) >= theta_from_target) {
2164 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2170 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2174 peduncle_vertices_computed.at(i) = peduncle_vertices_computed.at(i - 1) + dr_peduncle * peduncle_axis_actual;
2175 peduncle_radii_computed.at(i) = fbud_data.peduncle_radius;
2179 if (petiole < phytomer_ptr->peduncle_vertices.size()) {
2180 if (phytomer_ptr->peduncle_vertices.at(petiole).size() <= bud) {
2181 phytomer_ptr->peduncle_vertices.at(petiole).resize(bud + 1);
2182 phytomer_ptr->peduncle_radii.at(petiole).resize(bud + 1);
2184 phytomer_ptr->peduncle_vertices.at(petiole).at(bud) = peduncle_vertices_computed;
2185 phytomer_ptr->peduncle_radii.at(petiole).at(bud) = peduncle_radii_computed;
2189 if (phytomer_ptr->build_context_geometry_peduncle) {
2190 std::vector<RGBcolor> colors(peduncle_vertices_computed.size(), phytomer_ptr->phytomer_parameters.peduncle.color);
2191 fbud.peduncle_objIDs.push_back(context_ptr->
addTubeObject(Ndiv_peduncle_radius, peduncle_vertices_computed, peduncle_radii_computed, colors));
2197 if (!fbud_data.flower_rotations.empty()) {
2200 ensureInflorescencePrototypesInitialized(phytomer_ptr->phytomer_parameters);
2202 for (
size_t i = 0; i < fbud_data.flower_rotations.size(); i++) {
2204 vec3 flower_base_saved = (i < fbud_data.inflorescence_bases_saved.size()) ? fbud_data.inflorescence_bases_saved.at(i) :
make_vec3(0, 0, 0);
2205 float saved_pitch =
deg2rad(fbud_data.flower_rotations.at(i).pitch);
2206 float saved_yaw =
deg2rad(fbud_data.flower_rotations.at(i).yaw);
2207 float saved_roll =
deg2rad(fbud_data.flower_rotations.at(i).roll);
2208 float saved_azimuth =
deg2rad(fbud_data.flower_rotations.at(i).azimuth);
2211 int flowers_per_peduncle = fbud_data.flower_rotations.size();
2212 int petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
2215 float flower_offset_clamped = clampOffset(flowers_per_peduncle, fbud_data.flower_offset);
2216 float ind_from_tip_computed = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2219 vec3 flower_base_computed = phytomer_ptr->peduncle_vertices.at(petiole).at(bud).back();
2222 if (flowers_per_peduncle > 1 && flower_offset_clamped > 0) {
2223 if (ind_from_tip_computed != 0) {
2224 float offset_computed = (ind_from_tip_computed - 0.5f) * flower_offset_clamped * fbud_data.peduncle_length;
2225 float frac_computed = 1.0f;
2226 if (fbud_data.peduncle_length > 0) {
2227 frac_computed = 1.f - offset_computed / fbud_data.peduncle_length;
2229 flower_base_computed = interpolateTube(phytomer_ptr->peduncle_vertices.at(petiole).at(bud), frac_computed);
2234 float flower_offset_val = fbud_data.flower_offset;
2235 if (flowers_per_peduncle > 2) {
2236 float denom = 0.5f * float(flowers_per_peduncle) - 1.f;
2237 if (flower_offset_val * denom > 1.f) {
2238 flower_offset_val = 1.f / denom;
2242 float ind_from_tip = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2244 if (flowers_per_peduncle > 1 && flower_offset_val > 0 && ind_from_tip != 0) {
2245 float offset = (ind_from_tip - 0.5f) * flower_offset_val * fbud_data.peduncle_length;
2246 if (fbud_data.peduncle_length > 0) {
2247 frac = 1.f - offset / fbud_data.peduncle_length;
2250 vec3 recalculated_peduncle_axis = phytomer_ptr->getAxisVector(frac, phytomer_ptr->peduncle_vertices.at(petiole).at(bud));
2254 if (i < fbud_data.flower_base_scales.size() && fbud_data.flower_base_scales.at(i) >= 0) {
2255 scale_factor = fbud_data.flower_base_scales.at(i);
2257 scale_factor = phytomer_ptr->phytomer_parameters.inflorescence.flower_prototype_scale.val();
2261 bool is_open_flower = (desired_state == BUD_FLOWER_OPEN);
2264 phytomer_ptr->createInflorescenceGeometry(fbud, flower_base_computed, recalculated_peduncle_axis, saved_pitch, saved_roll, saved_azimuth, saved_yaw, scale_factor, is_open_flower);
2268 }
else if (desired_state == BUD_FRUITING && phytomer_ptr->phytomer_parameters.inflorescence.fruit_prototype_function !=
nullptr) {
2269 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2273 fbud.inflorescence_objIDs.clear();
2274 fbud.inflorescence_bases.clear();
2275 fbud.inflorescence_rotation.clear();
2277 if (phytomer_ptr->build_context_geometry_peduncle) {
2279 fbud.peduncle_objIDs.clear();
2282 fbud.state = desired_state;
2283 fbud.time_counter = 0;
2286 if (fbud_data.peduncle_length > 0 && fbud_data.peduncle_radius > 0) {
2289 uint Ndiv_peduncle_length = std::max(
uint(1), phytomer_ptr->phytomer_parameters.peduncle.length_segments);
2290 uint Ndiv_peduncle_radius = std::max(
uint(3), phytomer_ptr->phytomer_parameters.peduncle.radial_subdivisions);
2293 std::vector<vec3> peduncle_vertices_computed(Ndiv_peduncle_length + 1);
2294 std::vector<float> peduncle_radii_computed(Ndiv_peduncle_length + 1);
2297 peduncle_vertices_computed.at(0) = fbud.base_position;
2298 peduncle_radii_computed.at(0) = fbud_data.peduncle_radius;
2300 float dr_peduncle = fbud_data.peduncle_length / float(Ndiv_peduncle_length);
2303 vec3 peduncle_axis_computed;
2304 vec3 peduncle_rotation_axis_computed;
2306 recomputePeduncleOrientationVectors(phytomer_ptr, fbud.parent_index, phytomer_index_in_shoot,
deg2rad(fbud_data.peduncle_pitch),
deg2rad(fbud_data.peduncle_roll), fbud.base_rotation, peduncle_axis_computed,
2307 peduncle_rotation_axis_computed);
2310 vec3 peduncle_axis_actual = peduncle_axis_computed;
2313 for (
int i = 1; i <= Ndiv_peduncle_length; i++) {
2314 if (fabs(fbud_data.peduncle_curvature) > 0) {
2315 float curvature_value = fbud_data.peduncle_curvature;
2319 float axis_magnitude = horizontal_bending_axis.
magnitude();
2321 if (axis_magnitude > 0.001f) {
2322 horizontal_bending_axis = horizontal_bending_axis / axis_magnitude;
2324 float theta_curvature =
deg2rad(curvature_value * dr_peduncle);
2325 float theta_from_target = (curvature_value > 0) ? std::acos(std::min(1.0f, std::max(-1.0f, peduncle_axis_actual.
z))) : std::acos(std::min(1.0f, std::max(-1.0f, -peduncle_axis_actual.
z)));
2327 if (fabs(theta_curvature) >= theta_from_target) {
2328 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2334 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2338 peduncle_vertices_computed.at(i) = peduncle_vertices_computed.at(i - 1) + dr_peduncle * peduncle_axis_actual;
2339 peduncle_radii_computed.at(i) = fbud_data.peduncle_radius;
2343 if (petiole < phytomer_ptr->peduncle_vertices.size()) {
2344 if (phytomer_ptr->peduncle_vertices.at(petiole).size() <= bud) {
2345 phytomer_ptr->peduncle_vertices.at(petiole).resize(bud + 1);
2346 phytomer_ptr->peduncle_radii.at(petiole).resize(bud + 1);
2348 phytomer_ptr->peduncle_vertices.at(petiole).at(bud) = peduncle_vertices_computed;
2349 phytomer_ptr->peduncle_radii.at(petiole).at(bud) = peduncle_radii_computed;
2353 if (phytomer_ptr->build_context_geometry_peduncle) {
2354 std::vector<RGBcolor> colors(peduncle_vertices_computed.size(), phytomer_ptr->phytomer_parameters.peduncle.color);
2355 fbud.peduncle_objIDs.push_back(context_ptr->
addTubeObject(Ndiv_peduncle_radius, peduncle_vertices_computed, peduncle_radii_computed, colors));
2361 if (!fbud_data.flower_rotations.empty()) {
2364 ensureInflorescencePrototypesInitialized(phytomer_ptr->phytomer_parameters);
2366 for (
size_t i = 0; i < fbud_data.flower_rotations.size(); i++) {
2368 vec3 fruit_base_saved = (i < fbud_data.inflorescence_bases_saved.size()) ? fbud_data.inflorescence_bases_saved.at(i) :
make_vec3(0, 0, 0);
2369 float saved_pitch =
deg2rad(fbud_data.flower_rotations.at(i).pitch);
2370 float saved_yaw =
deg2rad(fbud_data.flower_rotations.at(i).yaw);
2371 float saved_roll =
deg2rad(fbud_data.flower_rotations.at(i).roll);
2372 float saved_azimuth =
deg2rad(fbud_data.flower_rotations.at(i).azimuth);
2375 int flowers_per_peduncle = fbud_data.flower_rotations.size();
2376 int petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
2379 float flower_offset_clamped = clampOffset(flowers_per_peduncle, fbud_data.flower_offset);
2380 float ind_from_tip_computed = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2383 vec3 fruit_base_computed = phytomer_ptr->peduncle_vertices.at(petiole).at(bud).back();
2386 if (flowers_per_peduncle > 1 && flower_offset_clamped > 0) {
2387 if (ind_from_tip_computed != 0) {
2388 float offset_computed = (ind_from_tip_computed - 0.5f) * flower_offset_clamped * fbud_data.peduncle_length;
2389 float frac_computed = 1.0f;
2390 if (fbud_data.peduncle_length > 0) {
2391 frac_computed = 1.f - offset_computed / fbud_data.peduncle_length;
2393 fruit_base_computed = interpolateTube(phytomer_ptr->peduncle_vertices.at(petiole).at(bud), frac_computed);
2401 float flower_offset_val = fbud_data.flower_offset;
2402 if (flowers_per_peduncle > 2) {
2403 float denom = 0.5f * float(flowers_per_peduncle) - 1.f;
2404 if (flower_offset_val * denom > 1.f) {
2405 flower_offset_val = 1.f / denom;
2409 float ind_from_tip = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2411 if (flowers_per_peduncle > 1 && flower_offset_val > 0 && ind_from_tip != 0) {
2412 float offset = (ind_from_tip - 0.5f) * flower_offset_val * fbud_data.peduncle_length;
2413 if (fbud_data.peduncle_length > 0) {
2414 frac = 1.f - offset / fbud_data.peduncle_length;
2417 vec3 recalculated_peduncle_axis = phytomer_ptr->getAxisVector(frac, phytomer_ptr->peduncle_vertices.at(petiole).at(bud));
2420 float base_fruit_scale;
2421 if (i < fbud_data.flower_base_scales.size() && fbud_data.flower_base_scales.at(i) >= 0) {
2422 base_fruit_scale = fbud_data.flower_base_scales.at(i);
2424 base_fruit_scale = phytomer_ptr->phytomer_parameters.inflorescence.fruit_prototype_scale.val();
2426 float scale_factor = base_fruit_scale * fbud_data.current_fruit_scale_factor;
2429 phytomer_ptr->createInflorescenceGeometry(fbud, fruit_base_computed, recalculated_peduncle_axis, saved_pitch, saved_roll, saved_azimuth, saved_yaw, scale_factor,
false);
2435 fbud.state = desired_state;
2449 std::cout <<
"done." << std::endl;