883 std::cout <<
"Loading plant architecture XML file: " << filename <<
"..." << std::flush;
886 std::string fn = filename;
888 if (ext !=
".xml" && ext !=
".XML") {
892 std::vector<uint> plantIDs;
895 pugi::xml_document xmldoc;
899 std::string resolved_filename = resolved_path.string();
902 pugi::xml_parse_result load_result = xmldoc.load_file(resolved_filename.c_str());
906 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): Could not parse " + std::string(filename) +
":\nError description: " + load_result.description());
909 pugi::xml_node helios = xmldoc.child(
"helios");
912 std::string node_string;
914 if (helios.empty()) {
916 std::cout <<
"failed." << std::endl;
918 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): XML file must have tag '<helios> ... </helios>' bounding all other tags.");
921 size_t phytomer_count = 0;
923 std::map<int, int> shoot_ID_mapping;
925 for (pugi::xml_node plant = helios.child(
"plant_instance"); plant; plant = plant.next_sibling(
"plant_instance")) {
927 int plantID = std::stoi(plant.attribute(
"ID").value());
930 node_string =
"base_position";
931 vec3 base_position =
parse_xml_tag_vec3(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
934 node_string =
"plant_age";
935 float plant_age =
parse_xml_tag_float(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
938 plantIDs.push_back(plantID);
940 int current_shoot_ID;
942 for (pugi::xml_node shoot = plant.child(
"shoot"); shoot; shoot = shoot.next_sibling(
"shoot")) {
944 int shootID = std::stoi(shoot.attribute(
"ID").value());
945 bool base_shoot =
true;
948 node_string =
"shoot_type_label";
949 std::string shoot_type_label =
parse_xml_tag_string(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
952 node_string =
"parent_shoot_ID";
953 int parent_shoot_ID =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
956 node_string =
"parent_node_index";
957 int parent_node_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
960 node_string =
"parent_petiole_index";
961 int parent_petiole_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
964 node_string =
"base_rotation";
965 vec3 base_rot =
parse_xml_tag_vec3(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
968 for (pugi::xml_node phytomer = shoot.child(
"phytomer"); phytomer; phytomer = phytomer.next_sibling(
"phytomer")) {
970 pugi::xml_node internode = phytomer.child(
"internode");
973 node_string =
"internode_length";
974 float internode_length =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
977 node_string =
"internode_radius";
978 float internode_radius =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
981 node_string =
"internode_pitch";
982 float internode_pitch =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
985 node_string =
"internode_phyllotactic_angle";
986 float internode_phyllotactic_angle =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
990 float internode_length_max = internode_length;
991 node_string =
"internode_length_max";
992 if (internode.child(node_string.c_str())) {
993 internode_length_max =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
997 uint internode_length_segments = 1;
998 node_string =
"internode_length_segments";
999 if (internode.child(node_string.c_str())) {
1000 internode_length_segments = (
uint)
parse_xml_tag_int(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1004 std::vector<float> curvature_perturbations;
1005 node_string =
"curvature_perturbations";
1006 if (internode.child(node_string.c_str())) {
1007 std::string perturbs_str = internode.child_value(node_string.c_str());
1008 std::istringstream perturbs_stream(perturbs_str);
1009 std::string pert_str;
1010 while (std::getline(perturbs_stream, pert_str,
';')) {
1011 curvature_perturbations.push_back(std::stof(pert_str));
1016 std::vector<float> yaw_perturbations;
1017 node_string =
"yaw_perturbations";
1018 if (internode.child(node_string.c_str())) {
1019 std::string yaw_perturbs_str = internode.child_value(node_string.c_str());
1020 std::istringstream yaw_stream(yaw_perturbs_str);
1021 std::string yaw_str;
1022 while (std::getline(yaw_stream, yaw_str,
';')) {
1023 yaw_perturbations.push_back(std::stof(yaw_str));
1028 std::vector<vec3> internode_vertices;
1029 node_string =
"internode_vertices";
1030 if (internode.child(node_string.c_str())) {
1031 std::string vertices_str = internode.child_value(node_string.c_str());
1032 std::istringstream verts_stream(vertices_str);
1033 std::string vertex_str;
1034 while (std::getline(verts_stream, vertex_str,
';')) {
1035 std::istringstream vertex_coords(vertex_str);
1037 if (vertex_coords >> x >> y >> z) {
1038 internode_vertices.push_back(
make_vec3(x, y, z));
1044 std::vector<float> internode_radii;
1045 node_string =
"internode_radii";
1046 if (internode.child(node_string.c_str())) {
1047 std::string radii_str = internode.child_value(node_string.c_str());
1048 std::istringstream radii_stream(radii_str);
1049 std::string radius_str;
1050 while (std::getline(radii_stream, radius_str,
';')) {
1051 float radius = std::stof(radius_str);
1052 internode_radii.push_back(radius);
1056 float petiole_length;
1057 float petiole_radius;
1058 float petiole_pitch;
1059 float petiole_curvature;
1060 float current_leaf_scale_factor_value;
1061 float leaflet_scale;
1062 float leaflet_offset;
1063 std::vector<float> petiole_lengths;
1064 std::vector<float> petiole_radii_values;
1065 std::vector<float> petiole_pitches;
1066 std::vector<float> petiole_curvatures;
1067 std::vector<vec3> petiole_base_positions;
1068 std::vector<float> current_leaf_scale_factors;
1069 std::vector<float> petiole_tapers;
1070 std::vector<uint> petiole_length_segments_all;
1071 std::vector<uint> petiole_radial_subdivisions_all;
1072 std::vector<std::vector<vec3>> saved_leaf_bases_all_petioles;
1073 std::vector<std::vector<float>> leaf_scale;
1074 std::vector<std::vector<float>> leaf_pitch;
1075 std::vector<std::vector<float>> leaf_yaw;
1076 std::vector<std::vector<float>> leaf_roll;
1079 struct FloralBudData {
1084 float current_fruit_scale_factor;
1085 std::vector<vec3> inflorescence_bases_saved;
1086 std::vector<vec3> flower_positions;
1087 std::vector<AxisRotation> flower_rotations;
1088 std::vector<float> flower_base_scales;
1090 float flower_offset = -1;
1092 float peduncle_length = -1;
1093 float peduncle_radius = -1;
1094 float peduncle_pitch = 0;
1095 float peduncle_roll = 0;
1096 float peduncle_curvature = 0;
1098 std::vector<std::vector<FloralBudData>> floral_bud_data;
1099 for (pugi::xml_node petiole = internode.child(
"petiole"); petiole; petiole = petiole.next_sibling(
"petiole")) {
1102 node_string =
"petiole_length";
1103 petiole_length =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1106 node_string =
"petiole_radius";
1107 petiole_radius =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1110 node_string =
"petiole_pitch";
1111 petiole_pitch =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1114 node_string =
"petiole_curvature";
1115 petiole_curvature =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1119 node_string =
"petiole_base_position";
1120 if (petiole.child(node_string.c_str())) {
1121 petiole_base_pos =
parse_xml_tag_vec3(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1125 node_string =
"current_leaf_scale_factor";
1126 current_leaf_scale_factor_value =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1129 node_string =
"petiole_taper";
1130 float petiole_taper =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1133 node_string =
"petiole_length_segments";
1134 uint length_segments = (
uint)
parse_xml_tag_int(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1137 node_string =
"petiole_radial_subdivisions";
1138 uint radial_subdivisions = (
uint)
parse_xml_tag_int(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1141 node_string =
"leaflet_scale";
1142 leaflet_scale =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1145 node_string =
"leaflet_offset";
1146 if (petiole.child(node_string.c_str())) {
1147 leaflet_offset =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1149 leaflet_offset = 0.f;
1153 petiole_lengths.push_back(petiole_length);
1154 petiole_radii_values.push_back(petiole_radius);
1155 petiole_pitches.push_back(petiole_pitch);
1156 petiole_curvatures.push_back(petiole_curvature);
1157 current_leaf_scale_factors.push_back(current_leaf_scale_factor_value);
1158 petiole_base_positions.push_back(petiole_base_pos);
1159 petiole_tapers.push_back(petiole_taper);
1160 petiole_length_segments_all.push_back(length_segments);
1161 petiole_radial_subdivisions_all.push_back(radial_subdivisions);
1163 leaf_scale.resize(leaf_scale.size() + 1);
1164 leaf_pitch.resize(leaf_pitch.size() + 1);
1165 leaf_yaw.resize(leaf_yaw.size() + 1);
1166 leaf_roll.resize(leaf_roll.size() + 1);
1167 floral_bud_data.resize(floral_bud_data.size() + 1);
1169 std::vector<vec3> saved_leaf_bases;
1171 for (pugi::xml_node leaf = petiole.child(
"leaf"); leaf; leaf = leaf.next_sibling(
"leaf")) {
1174 node_string =
"leaf_scale";
1175 leaf_scale.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1178 node_string =
"leaf_base";
1179 if (leaf.child(node_string.c_str())) {
1180 std::string base_str = leaf.child_value(node_string.c_str());
1181 std::istringstream base_stream(base_str);
1183 if (base_stream >> x >> y >> z) {
1184 saved_leaf_bases.push_back(
make_vec3(x, y, z));
1189 node_string =
"leaf_pitch";
1190 leaf_pitch.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1193 node_string =
"leaf_yaw";
1194 leaf_yaw.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1197 node_string =
"leaf_roll";
1198 leaf_roll.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
1202 saved_leaf_bases_all_petioles.push_back(saved_leaf_bases);
1205 for (pugi::xml_node floral_bud = petiole.child(
"floral_bud"); floral_bud; floral_bud = floral_bud.next_sibling(
"floral_bud")) {
1207 FloralBudData fbud_data;
1210 node_string =
"bud_state";
1211 fbud_data.bud_state =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1214 node_string =
"parent_index";
1215 fbud_data.parent_index =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1218 node_string =
"bud_index";
1219 fbud_data.bud_index =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1222 node_string =
"is_terminal";
1223 int is_terminal =
parse_xml_tag_int(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1224 fbud_data.is_terminal = (is_terminal != 0);
1227 node_string =
"current_fruit_scale_factor";
1228 fbud_data.current_fruit_scale_factor =
parse_xml_tag_float(floral_bud.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1231 pugi::xml_node peduncle = floral_bud.child(
"peduncle");
1233 node_string =
"length";
1234 if (peduncle.child(node_string.c_str())) {
1235 fbud_data.peduncle_length =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1237 node_string =
"radius";
1238 if (peduncle.child(node_string.c_str())) {
1239 fbud_data.peduncle_radius =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1241 node_string =
"pitch";
1242 if (peduncle.child(node_string.c_str())) {
1243 fbud_data.peduncle_pitch =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1245 node_string =
"roll";
1246 if (peduncle.child(node_string.c_str())) {
1247 fbud_data.peduncle_roll =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1249 node_string =
"curvature";
1250 if (peduncle.child(node_string.c_str())) {
1251 fbud_data.peduncle_curvature =
parse_xml_tag_float(peduncle.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1256 pugi::xml_node inflorescence = floral_bud.child(
"inflorescence");
1257 if (inflorescence) {
1259 node_string =
"flower_offset";
1260 if (inflorescence.child(node_string.c_str())) {
1261 fbud_data.flower_offset =
parse_xml_tag_float(inflorescence.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
1265 for (pugi::xml_node flower = inflorescence.child(
"flower"); flower; flower = flower.next_sibling(
"flower")) {
1267 pugi::xml_node base_node = flower.child(
"inflorescence_base");
1269 vec3 base =
parse_xml_tag_vec3(base_node,
"inflorescence_base",
"PlantArchitecture::readPlantStructureXML");
1270 fbud_data.inflorescence_bases_saved.push_back(base);
1275 pugi::xml_node pitch_node = flower.child(
"flower_pitch");
1276 pugi::xml_node yaw_node = flower.child(
"flower_yaw");
1277 pugi::xml_node roll_node = flower.child(
"flower_roll");
1278 pugi::xml_node azimuth_node = flower.child(
"flower_azimuth");
1281 rotation.pitch =
parse_xml_tag_float(pitch_node,
"flower_pitch",
"PlantArchitecture::readPlantStructureXML");
1287 rotation.yaw =
parse_xml_tag_float(yaw_node,
"flower_yaw",
"PlantArchitecture::readPlantStructureXML");
1293 rotation.roll =
parse_xml_tag_float(roll_node,
"flower_roll",
"PlantArchitecture::readPlantStructureXML");
1299 rotation.azimuth =
parse_xml_tag_float(azimuth_node,
"flower_azimuth",
"PlantArchitecture::readPlantStructureXML");
1301 rotation.azimuth = 0;
1304 fbud_data.flower_rotations.push_back(rotation);
1307 pugi::xml_node scale_node = flower.child(
"flower_base_scale");
1309 float base_scale =
parse_xml_tag_float(scale_node,
"flower_base_scale",
"PlantArchitecture::readPlantStructureXML");
1310 fbud_data.flower_base_scales.push_back(base_scale);
1313 fbud_data.flower_base_scales.push_back(-1.0f);
1318 floral_bud_data.back().push_back(fbud_data);
1322 if (shoot_types.find(shoot_type_label) == shoot_types.end()) {
1323 helios_runtime_error(
"ERROR (PlantArchitecture::readPlantStructureXML): Shoot type " + shoot_type_label +
" not found in shoot types.");
1348 if (parent_shoot_ID < 0) {
1349 current_shoot_ID =
addBaseStemShoot(plantID, 1, base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, shoot_type_label);
1350 shoot_ID_mapping[shootID] = current_shoot_ID;
1352 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);
1353 shoot_ID_mapping[shootID] = current_shoot_ID;
1362 auto phytomer_ptr = plant_instances.at(plantID).shoot_tree.at(current_shoot_ID)->phytomers.back();
1365 phytomer_ptr->internode_pitch =
deg2rad(internode_pitch);
1366 phytomer_ptr->internode_phyllotactic_angle =
deg2rad(internode_phyllotactic_angle);
1369 auto shoot_ptr = plant_instances.at(plantID).shoot_tree.at(current_shoot_ID);
1370 uint phytomer_index_in_shoot = shoot_ptr->phytomers.size() - 1;
1373 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,
1384 if (phytomer_index_in_shoot > 0) {
1385 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1386 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1387 parent_petiole_axis = prev_phytomer->getPetioleAxisVector(0.f, 0);
1388 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1389 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1390 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1391 uint parent_petiole_index = phytomer_ptr->parent_shoot_ptr->parent_petiole_index;
1392 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1393 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1394 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1395 parent_petiole_axis = parent_phytomer->getPetioleAxisVector(0.f, parent_petiole_index);
1398 helios::vec3 petiole_rotation_axis =
cross(parent_internode_axis, parent_petiole_axis);
1399 if (petiole_rotation_axis.
magnitude() < 1e-6f) {
1400 petiole_rotation_axis =
make_vec3(1, 0, 0);
1407 if (phytomer_index_in_shoot == 0) {
1408 AxisRotation shoot_base_rotation = phytomer_ptr->parent_shoot_ptr->base_rotation;
1409 if (internode_pitch_rad != 0.f) {
1412 if (shoot_base_rotation.roll != 0.f) {
1416 if (shoot_base_rotation.pitch != 0.f) {
1417 helios::vec3 base_pitch_axis = -1.0f *
cross(parent_internode_axis, parent_petiole_axis);
1421 if (shoot_base_rotation.yaw != 0.f) {
1426 if (internode_pitch_rad != 0.f) {
1432 if (internode_axis ==
make_vec3(0, 0, 1)) {
1433 shoot_bending_axis =
make_vec3(0, 1, 0);
1438 out_internode_axis_initial = internode_axis;
1439 out_petiole_rotation_axis = petiole_rotation_axis;
1440 out_shoot_bending_axis = shoot_bending_axis;
1444 if (internode_length_segments > 0) {
1447 if (phytomer_index_in_shoot == 0) {
1449 if (shoot_ptr->parent_shoot_ID < 0) {
1451 internode_base = plant_instances.at(plantID).base_position;
1455 int parent_shoot_id = shoot_ptr->parent_shoot_ID;
1456 uint parent_node_index = shoot_ptr->parent_node_index;
1457 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1460 internode_base = parent_shoot->shoot_internode_vertices.at(parent_node_index).back();
1464 internode_base = shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot - 1].back();
1472 recomputeInternodeOrientationVectors_local(phytomer_ptr, phytomer_index_in_shoot,
deg2rad(internode_pitch),
deg2rad(internode_phyllotactic_angle), internode_axis_initial, petiole_rotation_axis, shoot_bending_axis);
1475 std::vector<helios::vec3> reconstructed_vertices(internode_length_segments + 1);
1476 std::vector<float> reconstructed_radii(internode_length_segments + 1);
1478 reconstructed_vertices[0] = internode_base;
1479 reconstructed_radii[0] = internode_radius;
1481 float dr = internode_length / float(internode_length_segments);
1482 float dr_max = internode_length_max / float(internode_length_segments);
1486 for (
int i = 1; i <= internode_length_segments; i++) {
1488 if (phytomer_index_in_shoot > 0 && !curvature_perturbations.empty()) {
1490 float current_curvature_fact = 0.5f - internode_axis.
z / 2.0f;
1491 if (internode_axis.
z < 0) {
1492 current_curvature_fact *= 2.0f;
1496 float gravitropic_curvature = shoot_ptr->gravitropic_curvature;
1499 float curvature_angle =
deg2rad(gravitropic_curvature * current_curvature_fact * dr_max + curvature_perturbations[i - 1]);
1503 if (!yaw_perturbations.empty() && (i - 1) < yaw_perturbations.size()) {
1504 float yaw_angle =
deg2rad(yaw_perturbations[i - 1]);
1512 reconstructed_vertices[i] = reconstructed_vertices[i - 1] + dr * internode_axis;
1513 reconstructed_radii[i] = internode_radius;
1517 shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot] = reconstructed_vertices;
1518 shoot_ptr->shoot_internode_radii[phytomer_index_in_shoot] = reconstructed_radii;
1519 }
else if (!internode_vertices.empty() && !internode_radii.empty()) {
1521 shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot] = internode_vertices;
1522 shoot_ptr->shoot_internode_radii[phytomer_index_in_shoot] = internode_radii;
1526 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,
1538 if (phytomer_index_in_shoot > 0) {
1540 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1541 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1542 parent_petiole_axis = prev_phytomer->getPetioleAxisVector(0.f, 0);
1543 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1545 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1546 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1547 uint parent_petiole_index = phytomer_ptr->parent_shoot_ptr->parent_petiole_index;
1549 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1550 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1551 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1552 parent_petiole_axis = parent_phytomer->getPetioleAxisVector(0.f, parent_petiole_index);
1556 helios::vec3 petiole_rotation_axis =
cross(parent_internode_axis, parent_petiole_axis);
1557 if (petiole_rotation_axis.
magnitude() < 1e-6f) {
1558 petiole_rotation_axis =
make_vec3(1, 0, 0);
1565 float internode_pitch_rad = phytomer_ptr->internode_pitch;
1568 if (phytomer_index_in_shoot == 0) {
1572 if (internode_pitch_rad != 0.f) {
1577 AxisRotation shoot_base_rotation = phytomer_ptr->parent_shoot_ptr->base_rotation;
1579 if (shoot_base_rotation.roll != 0.f) {
1584 if (shoot_base_rotation.pitch != 0.f) {
1585 helios::vec3 base_pitch_axis = -1.0f *
cross(parent_internode_axis, parent_petiole_axis);
1590 if (shoot_base_rotation.yaw != 0.f) {
1596 if (internode_pitch_rad != 0.f) {
1609 if (phytomer_index_in_shoot != 0 && std::abs(internode_phyllotactic_angle_rad) > 0) {
1615 if (petiole_index > 0) {
1616 uint petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
1617 float budrot = float(petiole_index) * 2.0f * float(
M_PI) / float(petioles_per_internode);
1623 out_petiole_axis_initial = petiole_axis;
1624 out_petiole_rotation_axis = petiole_rotation_axis;
1628 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,
1629 vec3 &out_peduncle_axis_initial,
vec3 &out_peduncle_rotation_axis) ->
void {
1631 vec3 peduncle_axis = phytomer_ptr->getAxisVector(1.f, phytomer_ptr->getInternodeNodePositions());
1639 if (phytomer_index_in_shoot > 0) {
1641 auto prev_phytomer = phytomer_ptr->parent_shoot_ptr->phytomers.at(phytomer_index_in_shoot - 1);
1642 parent_internode_axis = prev_phytomer->getInternodeAxisVector(1.0f);
1643 }
else if (phytomer_ptr->parent_shoot_ptr->parent_shoot_ID >= 0) {
1645 int parent_shoot_id = phytomer_ptr->parent_shoot_ptr->parent_shoot_ID;
1646 uint parent_node_index = phytomer_ptr->parent_shoot_ptr->parent_node_index;
1648 auto &parent_shoot = plant_instances.at(plantID).shoot_tree.at(parent_shoot_id);
1649 auto parent_phytomer = parent_shoot->phytomers.at(parent_node_index);
1650 parent_internode_axis = parent_phytomer->getInternodeAxisVector(1.0f);
1654 vec3 current_petiole_axis;
1655 if (phytomer_ptr->petiole_vertices.empty()) {
1656 current_petiole_axis = parent_internode_axis;
1659 current_petiole_axis = phytomer_ptr->getPetioleAxisVector(0.f, petiole_index);
1662 vec3 inflorescence_bending_axis_actual =
cross(parent_internode_axis, current_petiole_axis);
1663 if (inflorescence_bending_axis_actual.
magnitude() < 0.001f) {
1664 inflorescence_bending_axis_actual =
make_vec3(1, 0, 0);
1666 inflorescence_bending_axis_actual.
normalize();
1669 if (peduncle_pitch_rad != 0.f || base_rotation.pitch != 0.f) {
1674 vec3 internode_axis = phytomer_ptr->getAxisVector(1.f, phytomer_ptr->getInternodeNodePositions());
1675 vec3 parent_petiole_base_axis = phytomer_ptr->petiole_vertices.empty() ? internode_axis : phytomer_ptr->getPetioleAxisVector(0.f, petiole_index);
1677 float parent_petiole_azimuth = -std::atan2(parent_petiole_base_axis.
y, parent_petiole_base_axis.
x);
1678 float current_peduncle_azimuth = -std::atan2(peduncle_axis.
y, peduncle_axis.
x);
1679 float azimuthal_rotation = current_peduncle_azimuth - parent_petiole_azimuth;
1685 out_peduncle_axis_initial = peduncle_axis;
1686 out_peduncle_rotation_axis = inflorescence_bending_axis_actual;
1690 for (
size_t p = 0; p < petiole_lengths.size(); p++) {
1691 if (p < phytomer_ptr->petiole_length.size()) {
1694 phytomer_ptr->petiole_length.at(p) = petiole_lengths[p];
1695 phytomer_ptr->petiole_pitch.at(p) =
deg2rad(petiole_pitches[p]);
1696 phytomer_ptr->petiole_curvature.at(p) = petiole_curvatures[p];
1697 phytomer_ptr->petiole_taper.at(p) = petiole_tapers[p];
1698 phytomer_ptr->current_leaf_scale_factor.at(p) = current_leaf_scale_factors[p];
1701 phytomer_ptr->phytomer_parameters.petiole.length_segments = petiole_length_segments_all[p];
1702 phytomer_ptr->phytomer_parameters.petiole.radial_subdivisions = petiole_radial_subdivisions_all[p];
1705 uint Ndiv_petiole_length = std::max(
uint(1), petiole_length_segments_all[p]);
1706 uint Ndiv_petiole_radius = std::max(
uint(3), petiole_radial_subdivisions_all[p]);
1708 phytomer_ptr->petiole_vertices.at(p).resize(Ndiv_petiole_length + 1);
1709 phytomer_ptr->petiole_radii.at(p).resize(Ndiv_petiole_length + 1);
1713 phytomer_ptr->petiole_vertices.at(p).at(0) = shoot_ptr->shoot_internode_vertices[phytomer_index_in_shoot].back();
1714 phytomer_ptr->petiole_radii.at(p).at(0) = petiole_radii_values[p];
1717 float dr_petiole = petiole_lengths[p] / float(Ndiv_petiole_length);
1720 vec3 recomputed_axis;
1721 vec3 recomputed_rotation_axis;
1723 recomputePetioleOrientationVectors(phytomer_ptr,
1725 phytomer_index_in_shoot,
1726 phytomer_ptr->petiole_pitch.at(p),
1727 phytomer_ptr->internode_phyllotactic_angle,
1728 recomputed_axis, recomputed_rotation_axis);
1733 vec3 petiole_axis_actual = recomputed_axis;
1734 vec3 petiole_rotation_axis_actual = recomputed_rotation_axis;
1737 for (
int j = 1; j <= Ndiv_petiole_length; j++) {
1739 if (fabs(petiole_curvatures[p]) > 0) {
1744 phytomer_ptr->petiole_vertices.at(p).at(j) = phytomer_ptr->petiole_vertices.at(p).at(j - 1) + dr_petiole * petiole_axis_actual;
1747 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));
1751 context_ptr->
deleteObject(phytomer_ptr->petiole_objIDs[p]);
1752 std::vector<RGBcolor> petiole_colors(phytomer_ptr->petiole_radii[p].size(), phytomer_ptr->phytomer_parameters.petiole.color);
1753 phytomer_ptr->petiole_objIDs[p] =
makeTubeFromCones(Ndiv_petiole_radius, phytomer_ptr->petiole_vertices[p], phytomer_ptr->petiole_radii[p], petiole_colors, context_ptr);
1756 if (!phytomer_ptr->petiole_objIDs[p].empty()) {
1758 std::string petiole_material_name = plant_instances.at(plantID).plant_name +
"_" + shoot_type_label +
"_petiole";
1759 renameAutoMaterial(context_ptr, phytomer_ptr->petiole_objIDs[p], petiole_material_name);
1763 if (p < saved_leaf_bases_all_petioles.size() && !saved_leaf_bases_all_petioles[p].empty()) {
1764 for (
size_t leaf = 0; leaf < phytomer_ptr->leaf_bases[p].size(); leaf++) {
1765 if (leaf < saved_leaf_bases_all_petioles[p].size()) {
1766 phytomer_ptr->leaf_bases[p][leaf] = saved_leaf_bases_all_petioles[p][leaf];
1777 std::vector<std::vector<std::map<std::string, int>>> saved_leaf_data_int;
1778 std::vector<std::vector<std::map<std::string, uint>>> saved_leaf_data_uint;
1779 std::vector<std::vector<std::map<std::string, float>>> saved_leaf_data_float;
1780 std::vector<std::vector<std::map<std::string, double>>> saved_leaf_data_double;
1781 std::vector<std::vector<std::map<std::string, vec2>>> saved_leaf_data_vec2;
1782 std::vector<std::vector<std::map<std::string, vec3>>> saved_leaf_data_vec3;
1783 std::vector<std::vector<std::map<std::string, vec4>>> saved_leaf_data_vec4;
1784 std::vector<std::vector<std::map<std::string, int2>>> saved_leaf_data_int2;
1785 std::vector<std::vector<std::map<std::string, int3>>> saved_leaf_data_int3;
1786 std::vector<std::vector<std::map<std::string, int4>>> saved_leaf_data_int4;
1787 std::vector<std::vector<std::map<std::string, std::string>>> saved_leaf_data_string;
1789 saved_leaf_data_int.resize(phytomer_ptr->leaf_objIDs.size());
1790 saved_leaf_data_uint.resize(phytomer_ptr->leaf_objIDs.size());
1791 saved_leaf_data_float.resize(phytomer_ptr->leaf_objIDs.size());
1792 saved_leaf_data_double.resize(phytomer_ptr->leaf_objIDs.size());
1793 saved_leaf_data_vec2.resize(phytomer_ptr->leaf_objIDs.size());
1794 saved_leaf_data_vec3.resize(phytomer_ptr->leaf_objIDs.size());
1795 saved_leaf_data_vec4.resize(phytomer_ptr->leaf_objIDs.size());
1796 saved_leaf_data_int2.resize(phytomer_ptr->leaf_objIDs.size());
1797 saved_leaf_data_int3.resize(phytomer_ptr->leaf_objIDs.size());
1798 saved_leaf_data_int4.resize(phytomer_ptr->leaf_objIDs.size());
1799 saved_leaf_data_string.resize(phytomer_ptr->leaf_objIDs.size());
1801 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
1802 saved_leaf_data_int[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1803 saved_leaf_data_uint[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1804 saved_leaf_data_float[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1805 saved_leaf_data_double[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1806 saved_leaf_data_vec2[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1807 saved_leaf_data_vec3[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1808 saved_leaf_data_vec4[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1809 saved_leaf_data_int2[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1810 saved_leaf_data_int3[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1811 saved_leaf_data_int4[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1812 saved_leaf_data_string[petiole].resize(phytomer_ptr->leaf_objIDs[petiole].size());
1814 for (
int leaf = 0; leaf < phytomer_ptr->leaf_objIDs[petiole].size(); leaf++) {
1815 uint objID = phytomer_ptr->leaf_objIDs[petiole][leaf];
1817 if (!UUIDs.empty()) {
1819 std::vector<std::string> data_int = context_ptr->
listPrimitiveData(UUIDs.front());
1820 for (
const auto &label: data_int) {
1825 saved_leaf_data_int[petiole][leaf][label] = value;
1829 saved_leaf_data_uint[petiole][leaf][label] = value;
1833 saved_leaf_data_float[petiole][leaf][label] = value;
1837 saved_leaf_data_double[petiole][leaf][label] = value;
1841 saved_leaf_data_vec2[petiole][leaf][label] = value;
1845 saved_leaf_data_vec3[petiole][leaf][label] = value;
1849 saved_leaf_data_vec4[petiole][leaf][label] = value;
1853 saved_leaf_data_int2[petiole][leaf][label] = value;
1857 saved_leaf_data_int3[petiole][leaf][label] = value;
1861 saved_leaf_data_int4[petiole][leaf][label] = value;
1865 saved_leaf_data_string[petiole][leaf][label] = value;
1873 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
1874 for (
uint objID: phytomer_ptr->leaf_objIDs[petiole]) {
1877 phytomer_ptr->leaf_objIDs[petiole].clear();
1878 phytomer_ptr->leaf_bases[petiole].clear();
1879 phytomer_ptr->leaf_rotation[petiole].clear();
1883 assert(leaf_scale.size() == leaf_pitch.size());
1884 float leaflet_offset_val = 0.f;
1887 if (leaf_scale.size() > 0) {
1889 int max_leaves_per_petiole = 0;
1890 for (
size_t i = 0; i < leaf_scale.size(); i++) {
1891 max_leaves_per_petiole = std::max(max_leaves_per_petiole, (
int) leaf_scale[i].size());
1893 int leaves_per_petiole = max_leaves_per_petiole;
1894 assert(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier != 0);
1896 if (phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes > 0 &&
1897 this->unique_leaf_prototype_objIDs.find(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier) == this->unique_leaf_prototype_objIDs.end()) {
1898 this->unique_leaf_prototype_objIDs[phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier].resize(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes);
1899 for (
int prototype = 0; prototype < phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes; prototype++) {
1900 for (
int leaf = 0; leaf < leaves_per_petiole; leaf++) {
1901 float ind_from_tip = float(leaf) - float(leaves_per_petiole - 1) / 2.f;
1902 uint objID_leaf = phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function(context_ptr, &phytomer_ptr->phytomer_parameters.leaf.prototype, ind_from_tip);
1903 if (phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function == GenericLeafPrototype) {
1906 this->unique_leaf_prototype_objIDs.at(phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier).at(prototype).push_back(objID_leaf);
1907 std::string leaf_material_name = plant_instances.at(plantID).plant_name +
"_" + shoot_type_label +
"_leaf";
1908 renameAutoMaterial(context_ptr, objID_leaf, leaf_material_name);
1910 context_ptr->
setPrimitiveColor(petiolule_UUIDs, phytomer_ptr->phytomer_parameters.petiole.color);
1917 for (
int petiole = 0; petiole < leaf_scale.size(); petiole++) {
1918 int leaves_per_petiole = leaf_scale[petiole].size();
1921 vec3 petiole_tip_axis = phytomer_ptr->getPetioleAxisVector(1.f, petiole);
1922 vec3 internode_axis = phytomer_ptr->getInternodeAxisVector(1.f);
1925 phytomer_ptr->leaf_objIDs[petiole].resize(leaves_per_petiole);
1926 phytomer_ptr->leaf_bases[petiole].resize(leaves_per_petiole);
1927 phytomer_ptr->leaf_rotation[petiole].resize(leaves_per_petiole);
1929 for (
int leaf = 0; leaf < leaves_per_petiole; leaf++) {
1930 float ind_from_tip = float(leaf) - float(leaves_per_petiole - 1) / 2.f;
1934 if (phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes > 0) {
1935 int prototype = context_ptr->
randu(0, phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototypes - 1);
1936 uint uid = phytomer_ptr->phytomer_parameters.leaf.prototype.unique_prototype_identifier;
1937 assert(this->unique_leaf_prototype_objIDs.find(uid) != this->unique_leaf_prototype_objIDs.end());
1938 assert(this->unique_leaf_prototype_objIDs.at(uid).size() > prototype);
1939 assert(this->unique_leaf_prototype_objIDs.at(uid).at(prototype).size() > leaf);
1940 objID_leaf = context_ptr->
copyObject(this->unique_leaf_prototype_objIDs.at(uid).at(prototype).at(leaf));
1942 objID_leaf = phytomer_ptr->phytomer_parameters.leaf.prototype.prototype_function(context_ptr, &phytomer_ptr->phytomer_parameters.leaf.prototype, ind_from_tip);
1946 vec3 leaf_scale_vec = leaf_scale[petiole][leaf] *
make_vec3(1, 1, 1);
1947 context_ptr->
scaleObject(objID_leaf, leaf_scale_vec);
1950 float compound_rotation = 0;
1951 if (leaves_per_petiole > 1) {
1952 if (leaf ==
float(leaves_per_petiole - 1) / 2.f) {
1953 compound_rotation = 0;
1954 }
else if (leaf <
float(leaves_per_petiole - 1) / 2.f) {
1955 compound_rotation = -0.5 *
M_PI;
1957 compound_rotation = 0.5 *
M_PI;
1962 float saved_pitch =
deg2rad(leaf_pitch[petiole][leaf]);
1963 float saved_yaw =
deg2rad(leaf_yaw[petiole][leaf]);
1964 float saved_roll =
deg2rad(leaf_roll[petiole][leaf]);
1968 if (leaves_per_petiole == 1) {
1970 roll_rot = (
acos_safe(internode_axis.
z) - saved_roll) * sign;
1971 }
else if (ind_from_tip != 0) {
1972 roll_rot = (
asin_safe(petiole_tip_axis.
z) + saved_roll) * compound_rotation / std::fabs(compound_rotation);
1974 phytomer_ptr->leaf_rotation[petiole][leaf].roll = saved_roll;
1978 phytomer_ptr->leaf_rotation[petiole][leaf].pitch = saved_pitch;
1979 float pitch_rot = saved_pitch;
1980 if (ind_from_tip == 0) {
1983 context_ptr->
rotateObject(objID_leaf, -pitch_rot,
"y");
1986 if (ind_from_tip != 0) {
1987 phytomer_ptr->leaf_rotation[petiole][leaf].yaw = saved_yaw;
1990 phytomer_ptr->leaf_rotation[petiole][leaf].yaw = 0;
1994 context_ptr->
rotateObject(objID_leaf, -std::atan2(petiole_tip_axis.
y, petiole_tip_axis.
x) + compound_rotation,
"z");
1997 vec3 leaf_base = phytomer_ptr->petiole_vertices[petiole].back();
1999 int leaves_per_petiole = phytomer_ptr->phytomer_parameters.leaf.leaves_per_petiole.val();
2000 float leaflet_offset_val = clampOffset(leaves_per_petiole, phytomer_ptr->phytomer_parameters.leaf.leaflet_offset.val());
2002 if (leaves_per_petiole > 1 && leaflet_offset_val > 0) {
2004 int ind_from_tip = leaf - int(floor(
float(leaves_per_petiole - 1) / 2.f));
2005 if (ind_from_tip != 0) {
2006 float offset = (fabs(ind_from_tip) - 0.5f) * leaflet_offset_val * phytomer_ptr->phytomer_parameters.petiole.length.val();
2007 leaf_base = PlantArchitecture::interpolateTube(phytomer_ptr->petiole_vertices[petiole], 1.f - offset / phytomer_ptr->phytomer_parameters.petiole.length.val());
2012 phytomer_ptr->leaf_objIDs[petiole][leaf] = objID_leaf;
2013 phytomer_ptr->leaf_bases[petiole][leaf] = leaf_base;
2018 for (
int petiole = 0; petiole < phytomer_ptr->leaf_objIDs.size(); petiole++) {
2019 for (
int leaf = 0; leaf < phytomer_ptr->leaf_objIDs[petiole].size(); leaf++) {
2020 uint objID = phytomer_ptr->leaf_objIDs[petiole][leaf];
2023 for (
const auto &pair: saved_leaf_data_int[petiole][leaf]) {
2026 for (
const auto &pair: saved_leaf_data_uint[petiole][leaf]) {
2029 for (
const auto &pair: saved_leaf_data_float[petiole][leaf]) {
2032 for (
const auto &pair: saved_leaf_data_double[petiole][leaf]) {
2035 for (
const auto &pair: saved_leaf_data_vec2[petiole][leaf]) {
2038 for (
const auto &pair: saved_leaf_data_vec3[petiole][leaf]) {
2041 for (
const auto &pair: saved_leaf_data_vec4[petiole][leaf]) {
2044 for (
const auto &pair: saved_leaf_data_int2[petiole][leaf]) {
2047 for (
const auto &pair: saved_leaf_data_int3[petiole][leaf]) {
2050 for (
const auto &pair: saved_leaf_data_int4[petiole][leaf]) {
2053 for (
const auto &pair: saved_leaf_data_string[petiole][leaf]) {
2060 if (!floral_bud_data.empty() && floral_bud_data.size() <= phytomer_ptr->petiole_length.size()) {
2062 phytomer_ptr->floral_buds.resize(floral_bud_data.size());
2064 for (
size_t petiole = 0; petiole < floral_bud_data.size(); petiole++) {
2065 phytomer_ptr->floral_buds.at(petiole).resize(floral_bud_data.at(petiole).size());
2067 for (
size_t bud = 0; bud < floral_bud_data.at(petiole).size(); bud++) {
2068 const FloralBudData &fbud_data = floral_bud_data.at(petiole).at(bud);
2071 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2073 fbud.parent_index = fbud_data.parent_index;
2074 fbud.bud_index = fbud_data.bud_index;
2075 fbud.isterminal = fbud_data.is_terminal;
2076 fbud.current_fruit_scale_factor = fbud_data.current_fruit_scale_factor;
2081 if (fbud.isterminal) {
2083 fbud.base_position = phytomer_ptr->parent_shoot_ptr->shoot_internode_vertices.back().back();
2086 if (petiole < phytomer_ptr->petiole_vertices.size()) {
2087 fbud.base_position = phytomer_ptr->petiole_vertices.at(petiole).front();
2089 helios_runtime_error(
"ERROR (PlantArchitecture::readPlantStructureXML): Floral bud parent_index " + std::to_string(fbud.parent_index) +
" exceeds number of petioles.");
2095 uint Nbuds = floral_bud_data.at(petiole).size();
2096 if (fbud.isterminal) {
2098 float pitch_adjustment = (Nbuds > 1) ?
deg2rad(30.f) : 0.f;
2099 float yaw_adjustment =
static_cast<float>(fbud.bud_index) * 2.f *
M_PI /
float(Nbuds);
2100 fbud.base_rotation = make_AxisRotation(pitch_adjustment, yaw_adjustment, 0.f);
2103 float pitch_adjustment =
static_cast<float>(fbud.bud_index) * 0.1f *
M_PI /
float(Nbuds);
2104 float yaw_adjustment = -0.25f *
M_PI +
static_cast<float>(fbud.bud_index) * 0.5f *
M_PI /
float(Nbuds);
2105 fbud.base_rotation = make_AxisRotation(pitch_adjustment, yaw_adjustment, 0.f);
2111 BudState desired_state =
static_cast<BudState
>(fbud_data.bud_state);
2113 if (desired_state != BUD_DORMANT && desired_state != BUD_DEAD && desired_state != BUD_ACTIVE) {
2115 if (fbud_data.peduncle_length >= 0) {
2116 phytomer_ptr->phytomer_parameters.peduncle.length = fbud_data.peduncle_length;
2118 if (fbud_data.peduncle_radius >= 0) {
2119 phytomer_ptr->phytomer_parameters.peduncle.radius = fbud_data.peduncle_radius;
2121 if (fbud_data.peduncle_pitch != 0) {
2122 phytomer_ptr->phytomer_parameters.peduncle.pitch = fbud_data.peduncle_pitch;
2124 if (fbud_data.peduncle_roll != 0) {
2125 phytomer_ptr->phytomer_parameters.peduncle.roll = fbud_data.peduncle_roll;
2127 if (fbud_data.peduncle_curvature != 0) {
2128 phytomer_ptr->phytomer_parameters.peduncle.curvature = fbud_data.peduncle_curvature;
2132 if (fbud_data.flower_offset >= 0) {
2133 phytomer_ptr->phytomer_parameters.inflorescence.flower_offset = fbud_data.flower_offset;
2136 if ((desired_state == BUD_FLOWER_CLOSED || desired_state == BUD_FLOWER_OPEN) && phytomer_ptr->phytomer_parameters.inflorescence.flower_prototype_function !=
nullptr) {
2137 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2141 fbud.inflorescence_objIDs.clear();
2142 fbud.inflorescence_bases.clear();
2143 fbud.inflorescence_rotation.clear();
2145 if (phytomer_ptr->build_context_geometry_peduncle) {
2147 fbud.peduncle_objIDs.clear();
2151 fbud.state = desired_state;
2152 fbud.time_counter = 0;
2155 if (fbud_data.peduncle_length > 0 && fbud_data.peduncle_radius > 0) {
2158 uint Ndiv_peduncle_length = std::max(
uint(1), phytomer_ptr->phytomer_parameters.peduncle.length_segments);
2159 uint Ndiv_peduncle_radius = std::max(
uint(3), phytomer_ptr->phytomer_parameters.peduncle.radial_subdivisions);
2162 std::vector<vec3> peduncle_vertices_computed(Ndiv_peduncle_length + 1);
2163 std::vector<float> peduncle_radii_computed(Ndiv_peduncle_length + 1);
2166 peduncle_vertices_computed.at(0) = fbud.base_position;
2167 peduncle_radii_computed.at(0) = fbud_data.peduncle_radius;
2169 float dr_peduncle = fbud_data.peduncle_length / float(Ndiv_peduncle_length);
2172 vec3 peduncle_axis_computed;
2173 vec3 peduncle_rotation_axis_computed;
2175 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,
2176 peduncle_rotation_axis_computed);
2179 vec3 peduncle_axis_actual = peduncle_axis_computed;
2182 for (
int i = 1; i <= Ndiv_peduncle_length; i++) {
2183 if (fabs(fbud_data.peduncle_curvature) > 0) {
2184 float curvature_value = fbud_data.peduncle_curvature;
2188 float axis_magnitude = horizontal_bending_axis.
magnitude();
2190 if (axis_magnitude > 0.001f) {
2191 horizontal_bending_axis = horizontal_bending_axis / axis_magnitude;
2193 float theta_curvature =
deg2rad(curvature_value * dr_peduncle);
2194 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)));
2196 if (fabs(theta_curvature) >= theta_from_target) {
2197 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2203 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2207 peduncle_vertices_computed.at(i) = peduncle_vertices_computed.at(i - 1) + dr_peduncle * peduncle_axis_actual;
2208 peduncle_radii_computed.at(i) = fbud_data.peduncle_radius;
2212 if (petiole < phytomer_ptr->peduncle_vertices.size()) {
2213 if (phytomer_ptr->peduncle_vertices.at(petiole).size() <= bud) {
2214 phytomer_ptr->peduncle_vertices.at(petiole).resize(bud + 1);
2215 phytomer_ptr->peduncle_radii.at(petiole).resize(bud + 1);
2217 phytomer_ptr->peduncle_vertices.at(petiole).at(bud) = peduncle_vertices_computed;
2218 phytomer_ptr->peduncle_radii.at(petiole).at(bud) = peduncle_radii_computed;
2222 if (phytomer_ptr->build_context_geometry_peduncle) {
2223 std::vector<RGBcolor> colors(peduncle_vertices_computed.size(), phytomer_ptr->phytomer_parameters.peduncle.color);
2224 fbud.peduncle_objIDs.push_back(context_ptr->
addTubeObject(Ndiv_peduncle_radius, peduncle_vertices_computed, peduncle_radii_computed, colors));
2226 std::string peduncle_material_name = plant_instances.at(plantID).plant_name +
"_" + shoot_type_label +
"_peduncle";
2227 renameAutoMaterial(context_ptr, fbud.peduncle_objIDs.back(), peduncle_material_name);
2232 if (!fbud_data.flower_rotations.empty()) {
2235 ensureInflorescencePrototypesInitialized(phytomer_ptr->phytomer_parameters, plant_instances.at(plantID).plant_name);
2237 for (
size_t i = 0; i < fbud_data.flower_rotations.size(); i++) {
2239 vec3 flower_base_saved = (i < fbud_data.inflorescence_bases_saved.size()) ? fbud_data.inflorescence_bases_saved.at(i) :
make_vec3(0, 0, 0);
2240 float saved_pitch =
deg2rad(fbud_data.flower_rotations.at(i).pitch);
2241 float saved_yaw =
deg2rad(fbud_data.flower_rotations.at(i).yaw);
2242 float saved_roll =
deg2rad(fbud_data.flower_rotations.at(i).roll);
2243 float saved_azimuth =
deg2rad(fbud_data.flower_rotations.at(i).azimuth);
2246 int flowers_per_peduncle = fbud_data.flower_rotations.size();
2247 int petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
2250 float flower_offset_clamped = clampOffset(flowers_per_peduncle, fbud_data.flower_offset);
2251 float ind_from_tip_computed = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2254 vec3 flower_base_computed = phytomer_ptr->peduncle_vertices.at(petiole).at(bud).back();
2257 if (flowers_per_peduncle > 1 && flower_offset_clamped > 0) {
2258 if (ind_from_tip_computed != 0) {
2259 float offset_computed = (ind_from_tip_computed - 0.5f) * flower_offset_clamped * fbud_data.peduncle_length;
2260 float frac_computed = 1.0f;
2261 if (fbud_data.peduncle_length > 0) {
2262 frac_computed = 1.f - offset_computed / fbud_data.peduncle_length;
2264 flower_base_computed = interpolateTube(phytomer_ptr->peduncle_vertices.at(petiole).at(bud), frac_computed);
2269 float flower_offset_val = fbud_data.flower_offset;
2270 if (flowers_per_peduncle > 2) {
2271 float denom = 0.5f * float(flowers_per_peduncle) - 1.f;
2272 if (flower_offset_val * denom > 1.f) {
2273 flower_offset_val = 1.f / denom;
2277 float ind_from_tip = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2279 if (flowers_per_peduncle > 1 && flower_offset_val > 0 && ind_from_tip != 0) {
2280 float offset = (ind_from_tip - 0.5f) * flower_offset_val * fbud_data.peduncle_length;
2281 if (fbud_data.peduncle_length > 0) {
2282 frac = 1.f - offset / fbud_data.peduncle_length;
2285 vec3 recalculated_peduncle_axis = phytomer_ptr->getAxisVector(frac, phytomer_ptr->peduncle_vertices.at(petiole).at(bud));
2289 if (i < fbud_data.flower_base_scales.size() && fbud_data.flower_base_scales.at(i) >= 0) {
2290 scale_factor = fbud_data.flower_base_scales.at(i);
2292 scale_factor = phytomer_ptr->phytomer_parameters.inflorescence.flower_prototype_scale.val();
2296 bool is_open_flower = (desired_state == BUD_FLOWER_OPEN);
2299 phytomer_ptr->createInflorescenceGeometry(fbud, flower_base_computed, recalculated_peduncle_axis, saved_pitch, saved_roll, saved_azimuth, saved_yaw, scale_factor, is_open_flower);
2303 }
else if (desired_state == BUD_FRUITING && phytomer_ptr->phytomer_parameters.inflorescence.fruit_prototype_function !=
nullptr) {
2304 FloralBud &fbud = phytomer_ptr->floral_buds.at(petiole).at(bud);
2308 fbud.inflorescence_objIDs.clear();
2309 fbud.inflorescence_bases.clear();
2310 fbud.inflorescence_rotation.clear();
2312 if (phytomer_ptr->build_context_geometry_peduncle) {
2314 fbud.peduncle_objIDs.clear();
2317 fbud.state = desired_state;
2318 fbud.time_counter = 0;
2321 if (fbud_data.peduncle_length > 0 && fbud_data.peduncle_radius > 0) {
2324 uint Ndiv_peduncle_length = std::max(
uint(1), phytomer_ptr->phytomer_parameters.peduncle.length_segments);
2325 uint Ndiv_peduncle_radius = std::max(
uint(3), phytomer_ptr->phytomer_parameters.peduncle.radial_subdivisions);
2328 std::vector<vec3> peduncle_vertices_computed(Ndiv_peduncle_length + 1);
2329 std::vector<float> peduncle_radii_computed(Ndiv_peduncle_length + 1);
2332 peduncle_vertices_computed.at(0) = fbud.base_position;
2333 peduncle_radii_computed.at(0) = fbud_data.peduncle_radius;
2335 float dr_peduncle = fbud_data.peduncle_length / float(Ndiv_peduncle_length);
2338 vec3 peduncle_axis_computed;
2339 vec3 peduncle_rotation_axis_computed;
2341 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,
2342 peduncle_rotation_axis_computed);
2345 vec3 peduncle_axis_actual = peduncle_axis_computed;
2348 for (
int i = 1; i <= Ndiv_peduncle_length; i++) {
2349 if (fabs(fbud_data.peduncle_curvature) > 0) {
2350 float curvature_value = fbud_data.peduncle_curvature;
2354 float axis_magnitude = horizontal_bending_axis.
magnitude();
2356 if (axis_magnitude > 0.001f) {
2357 horizontal_bending_axis = horizontal_bending_axis / axis_magnitude;
2359 float theta_curvature =
deg2rad(curvature_value * dr_peduncle);
2360 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)));
2362 if (fabs(theta_curvature) >= theta_from_target) {
2363 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2369 peduncle_axis_actual = (curvature_value > 0) ?
make_vec3(0, 0, 1) :
make_vec3(0, 0, -1);
2373 peduncle_vertices_computed.at(i) = peduncle_vertices_computed.at(i - 1) + dr_peduncle * peduncle_axis_actual;
2374 peduncle_radii_computed.at(i) = fbud_data.peduncle_radius;
2378 if (petiole < phytomer_ptr->peduncle_vertices.size()) {
2379 if (phytomer_ptr->peduncle_vertices.at(petiole).size() <= bud) {
2380 phytomer_ptr->peduncle_vertices.at(petiole).resize(bud + 1);
2381 phytomer_ptr->peduncle_radii.at(petiole).resize(bud + 1);
2383 phytomer_ptr->peduncle_vertices.at(petiole).at(bud) = peduncle_vertices_computed;
2384 phytomer_ptr->peduncle_radii.at(petiole).at(bud) = peduncle_radii_computed;
2388 if (phytomer_ptr->build_context_geometry_peduncle) {
2389 std::vector<RGBcolor> colors(peduncle_vertices_computed.size(), phytomer_ptr->phytomer_parameters.peduncle.color);
2390 fbud.peduncle_objIDs.push_back(context_ptr->
addTubeObject(Ndiv_peduncle_radius, peduncle_vertices_computed, peduncle_radii_computed, colors));
2392 std::string peduncle_material_name = plant_instances.at(plantID).plant_name +
"_" + shoot_type_label +
"_peduncle";
2393 renameAutoMaterial(context_ptr, fbud.peduncle_objIDs.back(), peduncle_material_name);
2398 if (!fbud_data.flower_rotations.empty()) {
2401 ensureInflorescencePrototypesInitialized(phytomer_ptr->phytomer_parameters, plant_instances.at(plantID).plant_name);
2403 for (
size_t i = 0; i < fbud_data.flower_rotations.size(); i++) {
2405 vec3 fruit_base_saved = (i < fbud_data.inflorescence_bases_saved.size()) ? fbud_data.inflorescence_bases_saved.at(i) :
make_vec3(0, 0, 0);
2406 float saved_pitch =
deg2rad(fbud_data.flower_rotations.at(i).pitch);
2407 float saved_yaw =
deg2rad(fbud_data.flower_rotations.at(i).yaw);
2408 float saved_roll =
deg2rad(fbud_data.flower_rotations.at(i).roll);
2409 float saved_azimuth =
deg2rad(fbud_data.flower_rotations.at(i).azimuth);
2412 int flowers_per_peduncle = fbud_data.flower_rotations.size();
2413 int petioles_per_internode = phytomer_ptr->phytomer_parameters.petiole.petioles_per_internode;
2416 float flower_offset_clamped = clampOffset(flowers_per_peduncle, fbud_data.flower_offset);
2417 float ind_from_tip_computed = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2420 vec3 fruit_base_computed = phytomer_ptr->peduncle_vertices.at(petiole).at(bud).back();
2423 if (flowers_per_peduncle > 1 && flower_offset_clamped > 0) {
2424 if (ind_from_tip_computed != 0) {
2425 float offset_computed = (ind_from_tip_computed - 0.5f) * flower_offset_clamped * fbud_data.peduncle_length;
2426 float frac_computed = 1.0f;
2427 if (fbud_data.peduncle_length > 0) {
2428 frac_computed = 1.f - offset_computed / fbud_data.peduncle_length;
2430 fruit_base_computed = interpolateTube(phytomer_ptr->peduncle_vertices.at(petiole).at(bud), frac_computed);
2438 float flower_offset_val = fbud_data.flower_offset;
2439 if (flowers_per_peduncle > 2) {
2440 float denom = 0.5f * float(flowers_per_peduncle) - 1.f;
2441 if (flower_offset_val * denom > 1.f) {
2442 flower_offset_val = 1.f / denom;
2446 float ind_from_tip = fabs(
float(i) -
float(flowers_per_peduncle - 1) /
float(petioles_per_internode));
2448 if (flowers_per_peduncle > 1 && flower_offset_val > 0 && ind_from_tip != 0) {
2449 float offset = (ind_from_tip - 0.5f) * flower_offset_val * fbud_data.peduncle_length;
2450 if (fbud_data.peduncle_length > 0) {
2451 frac = 1.f - offset / fbud_data.peduncle_length;
2454 vec3 recalculated_peduncle_axis = phytomer_ptr->getAxisVector(frac, phytomer_ptr->peduncle_vertices.at(petiole).at(bud));
2457 float base_fruit_scale;
2458 if (i < fbud_data.flower_base_scales.size() && fbud_data.flower_base_scales.at(i) >= 0) {
2459 base_fruit_scale = fbud_data.flower_base_scales.at(i);
2461 base_fruit_scale = phytomer_ptr->phytomer_parameters.inflorescence.fruit_prototype_scale.val();
2463 float scale_factor = base_fruit_scale * fbud_data.current_fruit_scale_factor;
2466 phytomer_ptr->createInflorescenceGeometry(fbud, fruit_base_computed, recalculated_peduncle_axis, saved_pitch, saved_roll, saved_azimuth, saved_yaw, scale_factor,
false);
2472 fbud.state = desired_state;
2486 std::cout <<
"done." << std::endl;