18using namespace helios;
20std::string PlantArchitecture::makeShootString(
const std::string ¤t_string,
const std::shared_ptr<Shoot> &shoot,
const std::vector<std::shared_ptr<Shoot>> &shoot_tree)
const {
22 std::string outstring = current_string;
24 if (shoot->parent_shoot_ID != -1) {
28 outstring +=
"{" + std::to_string(
rad2deg(shoot->base_rotation.pitch)) +
"," + std::to_string(
rad2deg(shoot->base_rotation.yaw)) +
"," + std::to_string(
rad2deg(shoot->base_rotation.roll)) +
"," +
29 std::to_string(shoot->shoot_parameters.gravitropic_curvature.val() ) +
"," + shoot->shoot_type_label +
"}";
32 for (
auto &phytomer: shoot->phytomers) {
34 float length = phytomer->getInternodeLength();
35 float radius = phytomer->getInternodeRadius();
37 outstring +=
"Internode(" + std::to_string(length) +
"," + std::to_string(radius) +
"," + std::to_string(
rad2deg(phytomer->internode_pitch)) +
"," + std::to_string(
rad2deg(phytomer->internode_phyllotactic_angle)) +
")";
39 for (
uint petiole = 0; petiole < phytomer->petiole_length.size(); petiole++) {
41 outstring +=
"Petiole(" + std::to_string(phytomer->petiole_length.at(petiole)) +
"," + std::to_string(phytomer->petiole_radii.at(petiole).front()) +
"," + std::to_string(
rad2deg(phytomer->petiole_pitch.at(petiole))) +
")";
44 outstring +=
"Leaf(" + std::to_string(phytomer->leaf_size_max.at(petiole).front() * phytomer->current_leaf_scale_factor.at(petiole)) +
"," + std::to_string(
rad2deg(phytomer->leaf_rotation.at(petiole).front().pitch)) +
"," +
45 std::to_string(
rad2deg(phytomer->leaf_rotation.at(petiole).front().yaw)) +
"," + std::to_string(
rad2deg(phytomer->leaf_rotation.at(petiole).front().roll)) +
")";
47 if (shoot->childIDs.find(node_number) != shoot->childIDs.end()) {
48 for (
int childID: shoot->childIDs.at(node_number)) {
49 outstring = makeShootString(outstring, shoot_tree.at(childID), shoot_tree);
57 if (shoot->parent_shoot_ID != -1) {
66 auto plant_shoot_tree = &plant_instances.at(plantID).shoot_tree;
68 std::string out_string;
70 for (
auto &shoot: *plant_shoot_tree) {
71 out_string = makeShootString(out_string, shoot, *plant_shoot_tree);
77void PlantArchitecture::parseShootArgument(
const std::string &shoot_argument,
const std::map<std::string, PhytomerParameters> &phytomer_parameters,
ShootParameters &shoot_parameters,
AxisRotation &base_rotation, std::string &phytomer_label) {
86 size_t pos_shoot_start = 0;
88 std::string s_argument = shoot_argument;
90 pos_shoot_start = s_argument.find(
',');
91 if (pos_shoot_start == std::string::npos) {
92 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Shoot brackets '{}' does not have the correct number of values given.");
94 float insertion_angle = std::stof(s_argument.substr(0, pos_shoot_start));
95 s_argument.erase(0, pos_shoot_start + 1);
96 base_rotation.pitch =
deg2rad(insertion_angle);
98 pos_shoot_start = s_argument.find(
',');
99 if (pos_shoot_start == std::string::npos) {
100 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Shoot brackets '{}' does not have the correct number of values given.");
102 float shoot_yaw = std::stof(s_argument.substr(0, pos_shoot_start));
103 s_argument.erase(0, pos_shoot_start + 1);
104 base_rotation.yaw =
deg2rad(shoot_yaw);
106 pos_shoot_start = s_argument.find(
',');
107 if (pos_shoot_start == std::string::npos) {
108 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Shoot brackets '{}' does not have the correct number of values given.");
110 float shoot_roll = std::stof(s_argument.substr(0, pos_shoot_start));
111 s_argument.erase(0, pos_shoot_start + 1);
112 base_rotation.roll =
deg2rad(shoot_roll);
114 pos_shoot_start = s_argument.find(
',');
115 if (pos_shoot_start == std::string::npos) {
116 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Shoot brackets '{}' does not have the correct number of values given.");
118 float shoot_curvature = std::stof(s_argument.substr(0, pos_shoot_start));
119 s_argument.erase(0, pos_shoot_start + 1);
122 if (pos_shoot_start != std::string::npos) {
123 pos_shoot_start = s_argument.find(
',');
124 phytomer_label = s_argument.substr(0, pos_shoot_start);
125 s_argument.erase(0, pos_shoot_start + 1);
126 if (phytomer_parameters.find(phytomer_label) == phytomer_parameters.end()) {
127 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Phytomer parameters with label " + phytomer_label +
" was not provided to PlantArchitecture::generatePlantFromString().");
131 phytomer_label = phytomer_parameters.begin()->first;
136void PlantArchitecture::parseInternodeArgument(
const std::string &internode_argument,
float &internode_radius,
float &internode_length,
PhytomerParameters &phytomer_parameters) {
144 size_t pos_inode_start = 0;
146 std::string inode_argument = internode_argument;
148 pos_inode_start = inode_argument.find(
',');
149 if (pos_inode_start == std::string::npos) {
150 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Internode()' does not have the correct number of values given.");
152 internode_length = std::stof(inode_argument.substr(0, pos_inode_start));
153 inode_argument.erase(0, pos_inode_start + 1);
155 pos_inode_start = inode_argument.find(
',');
156 if (pos_inode_start == std::string::npos) {
157 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Internode()' does not have the correct number of values given.");
159 internode_radius = std::stof(inode_argument.substr(0, pos_inode_start));
160 inode_argument.erase(0, pos_inode_start + 1);
162 pos_inode_start = inode_argument.find(
',');
163 if (pos_inode_start == std::string::npos) {
164 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Internode()' does not have the correct number of values given.");
166 float internode_pitch = std::stof(inode_argument.substr(0, pos_inode_start));
167 inode_argument.erase(0, pos_inode_start + 1);
168 phytomer_parameters.
internode.pitch = internode_pitch;
170 pos_inode_start = inode_argument.find(
',');
171 if (pos_inode_start != std::string::npos) {
172 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Internode()' does not have the correct number of values given.");
174 float internode_phyllotaxis = std::stof(inode_argument.substr(0, pos_inode_start));
175 inode_argument.erase(0, pos_inode_start + 1);
176 phytomer_parameters.
internode.phyllotactic_angle = internode_phyllotaxis;
179void PlantArchitecture::parsePetioleArgument(
const std::string &petiole_argument,
PhytomerParameters &phytomer_parameters) {
186 if (petiole_argument.empty()) {
187 phytomer_parameters.
petiole.length = 0;
191 size_t pos_petiole_start = 0;
193 std::string pet_argument = petiole_argument;
195 pos_petiole_start = pet_argument.find(
',');
196 if (pos_petiole_start == std::string::npos) {
197 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Petiole()' does not have the correct number of values given.");
199 float petiole_length = std::stof(pet_argument.substr(0, pos_petiole_start));
200 pet_argument.erase(0, pos_petiole_start + 1);
201 phytomer_parameters.
petiole.length = petiole_length;
203 pos_petiole_start = pet_argument.find(
',');
204 if (pos_petiole_start == std::string::npos) {
205 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Petiole()' does not have the correct number of values given.");
207 float petiole_radius = std::stof(pet_argument.substr(0, pos_petiole_start));
208 pet_argument.erase(0, pos_petiole_start + 1);
209 phytomer_parameters.
petiole.radius = petiole_radius;
211 pos_petiole_start = pet_argument.find(
',');
212 if (pos_petiole_start != std::string::npos) {
213 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Petiole()' does not have the correct number of values given.");
215 float petiole_pitch = std::stof(pet_argument.substr(0, pos_petiole_start));
216 pet_argument.erase(0, pos_petiole_start + 1);
217 phytomer_parameters.
petiole.pitch = petiole_pitch;
220void PlantArchitecture::parseLeafArgument(
const std::string &leaf_argument,
PhytomerParameters &phytomer_parameters) {
228 if (leaf_argument.empty()) {
229 phytomer_parameters.
leaf.prototype_scale = 0;
233 size_t pos_leaf_start = 0;
235 std::string l_argument = leaf_argument;
237 pos_leaf_start = l_argument.find(
',');
238 if (pos_leaf_start == std::string::npos) {
239 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Leaf()' does not have the correct number of values given.");
241 float leaf_scale = std::stof(l_argument.substr(0, pos_leaf_start));
242 l_argument.erase(0, pos_leaf_start + 1);
243 phytomer_parameters.
leaf.prototype_scale = leaf_scale;
245 pos_leaf_start = l_argument.find(
',');
246 if (pos_leaf_start == std::string::npos) {
247 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Leaf()' does not have the correct number of values given.");
249 float leaf_pitch = std::stof(l_argument.substr(0, pos_leaf_start));
250 l_argument.erase(0, pos_leaf_start + 1);
251 phytomer_parameters.
leaf.pitch = leaf_pitch;
253 pos_leaf_start = l_argument.find(
',');
254 if (pos_leaf_start == std::string::npos) {
255 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Leaf()' does not have the correct number of values given.");
257 float leaf_yaw = std::stof(l_argument.substr(0, pos_leaf_start));
258 l_argument.erase(0, pos_leaf_start + 1);
259 phytomer_parameters.
leaf.yaw = leaf_yaw;
261 pos_leaf_start = l_argument.find(
',');
262 if (pos_leaf_start != std::string::npos) {
263 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): 'Leaf()' does not have the correct number of values given.");
265 float leaf_roll = std::stof(l_argument.substr(0, pos_leaf_start));
266 l_argument.erase(0, pos_leaf_start + 1);
267 phytomer_parameters.
leaf.roll = leaf_roll;
270size_t findShootClosingBracket(
const std::string &lstring) {
272 size_t pos_close = std::string::npos;
273 size_t pos_open = lstring.find_first_of(
'[', 0);
274 if (pos_open == std::string::npos) {
278 size_t pos = pos_open;
282 if (lstring[pos] ==
'[') {
284 }
else if (lstring[pos] ==
']') {
287 if (pos == lstring.length()) {
297void PlantArchitecture::parseStringShoot(
const std::string &LString_shoot,
uint plantID,
int parentID,
uint parent_node,
const std::map<std::string, PhytomerParameters> &phytomer_parameters,
ShootParameters &shoot_parameters) {
299 std::string lstring_tobeparsed = LString_shoot;
301 size_t pos_inode_start = 0;
302 std::string inode_delimiter =
"Internode(";
303 std::string petiole_delimiter =
"Petiole(";
304 std::string leaf_delimiter =
"Leaf(";
305 bool base_shoot =
true;
310 if (LString_shoot.front() !=
'{') {
311 helios_runtime_error(
"ERROR (PlantArchitecture::parseStringShoot): Shoot string is not formatted correctly. All shoots should start with a curly bracket containing two arguments {X,Y}.");
313 size_t pos_shoot_end = lstring_tobeparsed.find(
'}');
314 std::string shoot_argument = lstring_tobeparsed.substr(1, pos_shoot_end - 1);
315 std::string phytomer_label;
316 parseShootArgument(shoot_argument, phytomer_parameters, shoot_parameters, shoot_base_rotation, phytomer_label);
317 lstring_tobeparsed.erase(0, pos_shoot_end + 1);
319 uint shoot_node_count = 0;
320 while ((pos_inode_start = lstring_tobeparsed.find(inode_delimiter)) != std::string::npos) {
322 if (pos_inode_start != 0) {
323 helios_runtime_error(
"ERROR (PlantArchitecture::parseStringShoot): Shoot string is not formatted correctly.");
326 size_t pos_inode_end = lstring_tobeparsed.find(
')');
327 std::string inode_argument = lstring_tobeparsed.substr(pos_inode_start + inode_delimiter.length(), pos_inode_end - pos_inode_start - inode_delimiter.length());
328 float internode_radius = 0;
329 float internode_length = 0;
330 parseInternodeArgument(inode_argument, internode_radius, internode_length, shoot_parameters.
phytomer_parameters);
331 lstring_tobeparsed.erase(0, pos_inode_end + 1);
333 size_t pos_petiole_start = lstring_tobeparsed.find(petiole_delimiter);
334 size_t pos_petiole_end = lstring_tobeparsed.find(
')');
335 std::string petiole_argument;
336 if (pos_petiole_start == 0) {
337 petiole_argument = lstring_tobeparsed.substr(pos_petiole_start + petiole_delimiter.length(), pos_petiole_end - pos_petiole_start - petiole_delimiter.length());
339 petiole_argument =
"";
342 if (pos_petiole_start == 0) {
343 lstring_tobeparsed.erase(0, pos_petiole_end + 1);
346 size_t pos_leaf_start = lstring_tobeparsed.find(leaf_delimiter);
347 size_t pos_leaf_end = lstring_tobeparsed.find(
')');
348 std::string leaf_argument;
349 if (pos_leaf_start == 0) {
350 leaf_argument = lstring_tobeparsed.substr(pos_leaf_start + leaf_delimiter.length(), pos_leaf_end - pos_leaf_start - leaf_delimiter.length());
355 if (pos_leaf_start == 0) {
356 lstring_tobeparsed.erase(0, pos_leaf_end + 1);
368 baseID =
addBaseStemShoot(plantID, 1, shoot_base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, phytomer_label);
371 baseID =
addChildShoot(plantID, parentID, parent_node, 1, shoot_base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, phytomer_label, 0);
379 while (!lstring_tobeparsed.empty() && lstring_tobeparsed.substr(0, 1) ==
"[") {
380 size_t pos_shoot_bracket_end = findShootClosingBracket(lstring_tobeparsed);
381 if (pos_shoot_bracket_end == std::string::npos) {
382 helios_runtime_error(
"ERROR (PlantArchitecture::parseStringShoot): Shoot string is not formatted correctly. Shoots must be closed with a ']'.");
384 std::string lstring_child = lstring_tobeparsed.substr(1, pos_shoot_bracket_end - 1);
385 parseStringShoot(lstring_child, plantID, (
int) baseID, shoot_node_count, phytomer_parameters, shoot_parameters);
386 lstring_tobeparsed.erase(0, pos_shoot_bracket_end + 1);
394 std::map<std::string, PhytomerParameters> phytomer_parameters_map;
395 phytomer_parameters_map[
"default"] = phytomer_parameters;
402 if (generation_string.front() !=
'{') {
403 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): First character of string must be '{'");
413 if (phytomer_parameters.empty()) {
414 helios_runtime_error(
"ERROR (PlantArchitecture::generatePlantFromString): Phytomer parameters must be provided.");
421 size_t pos_first_child_shoot = generation_string.find(
'[');
423 if (pos_first_child_shoot == std::string::npos) {
424 pos_first_child_shoot = generation_string.length();
427 parseStringShoot(generation_string, plantID, -1, 0, phytomer_parameters, shoot_parameters);
435 if (plant_instances.find(plantID) == plant_instances.end()) {
436 helios_runtime_error(
"ERROR (PlantArchitecture::writePlantStructureXML): Plant ID " + std::to_string(plantID) +
" does not exist.");
439 std::string output_file = filename;
441 helios_runtime_error(
"ERROR (PlantArchitecture::writePlantStructureXML): Could not open file " + filename +
" for writing. Make sure the directory exists and is writable.");
443 helios_runtime_error(
"ERROR (PlantArchitecture::writePlantStructureXML): The output file given was a directory. This argument should be the path to a file not to a directory.");
446 std::ofstream output_xml(filename);
448 if (!output_xml.is_open()) {
449 helios_runtime_error(
"ERROR (PlantArchitecture::writePlantStructureXML): Could not open file " + filename +
" for writing. Make sure the directory exists and is writable.");
452 output_xml <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << std::endl;
453 output_xml <<
"<helios>" << std::endl;
454 output_xml <<
"\t<plant_instance ID=\"" << plantID <<
"\">" << std::endl;
456 output_xml <<
"\t\t<base_position> " << plant_instances.at(plantID).base_position.x <<
" " << plant_instances.at(plantID).base_position.y <<
" " << plant_instances.at(plantID).base_position.z <<
" </base_position>" << std::endl;
457 output_xml <<
"\t\t<plant_age> " << plant_instances.at(plantID).current_age <<
" </plant_age>" << std::endl;
459 for (
auto &shoot: plant_instances.at(plantID).shoot_tree) {
461 output_xml <<
"\t\t<shoot ID=\"" << shoot->ID <<
"\">" << std::endl;
462 output_xml <<
"\t\t\t<shoot_type_label> " << shoot->shoot_type_label <<
" </shoot_type_label>" << std::endl;
463 output_xml <<
"\t\t\t<parent_shoot_ID> " << shoot->parent_shoot_ID <<
" </parent_shoot_ID>" << std::endl;
464 output_xml <<
"\t\t\t<parent_node_index> " << shoot->parent_node_index <<
" </parent_node_index>" << std::endl;
465 output_xml <<
"\t\t\t<parent_petiole_index> " << shoot->parent_petiole_index <<
" </parent_petiole_index>" << std::endl;
466 output_xml <<
"\t\t\t<base_rotation> " <<
rad2deg(shoot->base_rotation.pitch) <<
" " <<
rad2deg(shoot->base_rotation.yaw) <<
" " <<
rad2deg(shoot->base_rotation.roll) <<
" </base_rotation>" << std::endl;
468 for (
auto &phytomer: shoot->phytomers) {
470 output_xml <<
"\t\t\t<phytomer>" << std::endl;
471 output_xml <<
"\t\t\t\t<internode>" << std::endl;
472 output_xml <<
"\t\t\t\t\t<internode_length>" << phytomer->getInternodeLength() <<
"</internode_length>" << std::endl;
473 output_xml <<
"\t\t\t\t\t<internode_radius>" << phytomer->getInternodeRadius() <<
"</internode_radius>" << std::endl;
474 output_xml <<
"\t\t\t\t\t<internode_pitch>" <<
rad2deg(phytomer->internode_pitch) <<
"</internode_pitch>" << std::endl;
475 output_xml <<
"\t\t\t\t\t<internode_phyllotactic_angle>" <<
rad2deg(phytomer->internode_phyllotactic_angle) <<
"</internode_phyllotactic_angle>" << std::endl;
477 for (
uint petiole = 0; petiole < phytomer->petiole_length.size(); petiole++) {
479 output_xml <<
"\t\t\t\t\t<petiole>" << std::endl;
480 output_xml <<
"\t\t\t\t\t\t<petiole_length>" << phytomer->petiole_length.at(petiole) <<
"</petiole_length>" << std::endl;
481 output_xml <<
"\t\t\t\t\t\t<petiole_radius>" << phytomer->petiole_radii.at(petiole).front() <<
"</petiole_radius>" << std::endl;
482 output_xml <<
"\t\t\t\t\t\t<petiole_pitch>" <<
rad2deg(phytomer->petiole_pitch.at(petiole)) <<
"</petiole_pitch>" << std::endl;
483 output_xml <<
"\t\t\t\t\t\t<petiole_curvature>" << phytomer->petiole_curvature.at(petiole) <<
"</petiole_curvature>" << std::endl;
484 if (phytomer->leaf_rotation.at(petiole).size() == 1) {
485 output_xml <<
"\t\t\t\t\t\t<leaflet_scale>" << 1.0 <<
"</leaflet_scale>" << std::endl;
487 float tip_ind = floor(
float(phytomer->leaf_rotation.at(petiole).size() - 1) / 2.f);
488 output_xml <<
"\t\t\t\t\t\t<leaflet_scale>" << phytomer->leaf_size_max.at(petiole).at(
int(tip_ind - 1)) /
max(phytomer->leaf_size_max.at(petiole)) <<
"</leaflet_scale>" << std::endl;
491 for (
uint leaf = 0; leaf < phytomer->leaf_rotation.at(petiole).size(); leaf++) {
492 output_xml <<
"\t\t\t\t\t\t<leaf>" << std::endl;
493 output_xml <<
"\t\t\t\t\t\t\t<leaf_scale>" << phytomer->leaf_size_max.at(petiole).at(leaf) * phytomer->current_leaf_scale_factor.at(petiole) <<
"</leaf_scale>" << std::endl;
494 output_xml <<
"\t\t\t\t\t\t\t<leaf_pitch>" <<
rad2deg(phytomer->leaf_rotation.at(petiole).at(leaf).pitch) <<
"</leaf_pitch>" << std::endl;
495 output_xml <<
"\t\t\t\t\t\t\t<leaf_yaw>" <<
rad2deg(phytomer->leaf_rotation.at(petiole).at(leaf).yaw) <<
"</leaf_yaw>" << std::endl;
496 output_xml <<
"\t\t\t\t\t\t\t<leaf_roll>" <<
rad2deg(phytomer->leaf_rotation.at(petiole).at(leaf).roll) <<
"</leaf_roll>" << std::endl;
497 output_xml <<
"\t\t\t\t\t\t</leaf>" << std::endl;
500 output_xml <<
"\t\t\t\t\t</petiole>" << std::endl;
502 output_xml <<
"\t\t\t\t</internode>" << std::endl;
503 output_xml <<
"\t\t\t</phytomer>" << std::endl;
505 output_xml <<
"\t\t</shoot>" << std::endl;
507 output_xml <<
"\t</plant_instance>" << std::endl;
508 output_xml <<
"</helios>" << std::endl;
514 if (plant_instances.find(plantID) == plant_instances.end()) {
515 helios_runtime_error(
"ERROR (PlantArchitecture::writeQSMCylinderFile): Plant ID " + std::to_string(plantID) +
" does not exist.");
518 std::string output_file = filename;
520 helios_runtime_error(
"ERROR (PlantArchitecture::writeQSMCylinderFile): Could not open file " + filename +
" for writing. Make sure the directory exists and is writable.");
522 helios_runtime_error(
"ERROR (PlantArchitecture::writeQSMCylinderFile): The output file given was a directory. This argument should be the path to a file not to a directory.");
525 std::ofstream output_qsm(filename);
527 if (!output_qsm.is_open()) {
528 helios_runtime_error(
"ERROR (PlantArchitecture::writeQSMCylinderFile): Could not open file " + filename +
" for writing. Make sure the directory exists and is writable.");
532 output_qsm <<
"radius (m)\tlength (m)\tstart_point\taxis_direction\tparent\textension\tbranch\tbranch_order\tposition_in_branch\tmad\tSurfCov\tadded\tUnmodRadius (m)" << std::endl;
534 const auto &plant = plant_instances.at(plantID);
537 uint cylinder_id = 1;
540 std::map<int, std::vector<uint>> shoot_cylinder_ids;
541 std::map<int, uint> shoot_branch_id;
542 std::map<int, uint> shoot_branch_order;
545 uint branch_id_counter = 1;
546 for (
const auto &shoot: plant.shoot_tree) {
547 shoot_branch_id[shoot->ID] = branch_id_counter++;
550 if (shoot->parent_shoot_ID == -1) {
552 shoot_branch_order[shoot->ID] = 0;
555 shoot_branch_order[shoot->ID] = shoot_branch_order[shoot->parent_shoot_ID] + 1;
560 for (
const auto &shoot: plant.shoot_tree) {
563 uint branch_id = shoot_branch_id[shoot->ID];
564 uint branch_order = shoot_branch_order[shoot->ID];
565 uint position_in_branch = 1;
568 bool has_last_vertex =
false;
571 for (
uint phytomer_idx = 0; phytomer_idx < shoot->phytomers.size(); phytomer_idx++) {
573 const auto &vertices = shoot->shoot_internode_vertices[phytomer_idx];
574 const auto &radii = shoot->shoot_internode_radii[phytomer_idx];
577 if (vertices.size() == 1 && has_last_vertex) {
581 float current_radius = radii[0];
587 axis = axis / length;
592 if (cylinder_id > 1) {
593 parent_id = cylinder_id - 1;
597 uint extension_id = 0;
600 output_qsm << std::fixed << std::setprecision(4);
601 output_qsm << current_radius <<
"\t";
602 output_qsm << length <<
"\t";
603 output_qsm << start.
x <<
"\t" << start.
y <<
"\t" << start.
z <<
"\t";
604 output_qsm << axis.
x <<
"\t" << axis.
y <<
"\t" << axis.
z <<
"\t";
605 output_qsm << parent_id <<
"\t";
606 output_qsm << extension_id <<
"\t";
607 output_qsm << branch_id <<
"\t";
608 output_qsm << branch_order <<
"\t";
609 output_qsm << position_in_branch <<
"\t";
610 output_qsm <<
"0.0002" <<
"\t";
611 output_qsm <<
"1" <<
"\t";
612 output_qsm <<
"0" <<
"\t";
613 output_qsm << current_radius << std::endl;
616 shoot_cylinder_ids[shoot->ID].push_back(cylinder_id);
619 position_in_branch++;
622 last_vertex_position = current_end;
626 for (
int seg = 0; seg < vertices.size() - 1; seg++) {
631 float current_radius = radii[seg];
637 axis = axis / length;
641 if (cylinder_id > 1) {
642 if (seg == 0 && phytomer_idx == 0 && shoot->parent_shoot_ID != -1) {
645 parent_id = cylinder_id - 1;
646 }
else if (seg == 0 && phytomer_idx > 0) {
648 parent_id = cylinder_id - 1;
651 parent_id = cylinder_id - 1;
656 uint extension_id = 0;
659 output_qsm << std::fixed << std::setprecision(4);
660 output_qsm << current_radius <<
"\t";
661 output_qsm << length <<
"\t";
662 output_qsm << start.
x <<
"\t" << start.
y <<
"\t" << start.
z <<
"\t";
663 output_qsm << axis.
x <<
"\t" << axis.
y <<
"\t" << axis.
z <<
"\t";
664 output_qsm << parent_id <<
"\t";
665 output_qsm << extension_id <<
"\t";
666 output_qsm << branch_id <<
"\t";
667 output_qsm << branch_order <<
"\t";
668 output_qsm << position_in_branch <<
"\t";
669 output_qsm <<
"0.0002" <<
"\t";
670 output_qsm <<
"1" <<
"\t";
671 output_qsm <<
"0" <<
"\t";
672 output_qsm << current_radius << std::endl;
675 shoot_cylinder_ids[shoot->ID].push_back(cylinder_id);
678 position_in_branch++;
682 if (vertices.size() >= 2) {
683 last_vertex_position = vertices.back();
684 has_last_vertex =
true;
685 }
else if (vertices.size() == 1) {
687 last_vertex_position = vertices[0];
688 has_last_vertex =
true;
700 std::cout <<
"Loading plant architecture XML file: " << filename <<
"..." << std::flush;
703 std::string fn = filename;
705 if (ext !=
".xml" && ext !=
".XML") {
709 std::vector<uint> plantIDs;
712 pugi::xml_document xmldoc;
716 std::string resolved_filename = resolved_path.string();
719 pugi::xml_parse_result load_result = xmldoc.load_file(resolved_filename.c_str());
723 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): Could not parse " + std::string(filename) +
":\nError description: " + load_result.description());
726 pugi::xml_node helios = xmldoc.child(
"helios");
729 std::string node_string;
731 if (helios.empty()) {
733 std::cout <<
"failed." << std::endl;
735 helios_runtime_error(
"ERROR (Context::readPlantStructureXML): XML file must have tag '<helios> ... </helios>' bounding all other tags.");
738 size_t phytomer_count = 0;
740 std::map<int, int> shoot_ID_mapping;
742 for (pugi::xml_node plant = helios.child(
"plant_instance"); plant; plant = plant.next_sibling(
"plant_instance")) {
744 int plantID = std::stoi(plant.attribute(
"ID").value());
747 node_string =
"base_position";
748 vec3 base_position =
parse_xml_tag_vec3(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
751 node_string =
"plant_age";
752 float plant_age =
parse_xml_tag_float(plant.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
755 plantIDs.push_back(plantID);
757 int current_shoot_ID;
759 for (pugi::xml_node shoot = plant.child(
"shoot"); shoot; shoot = shoot.next_sibling(
"shoot")) {
761 int shootID = std::stoi(shoot.attribute(
"ID").value());
762 bool base_shoot =
true;
765 node_string =
"shoot_type_label";
766 std::string shoot_type_label =
parse_xml_tag_string(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
769 node_string =
"parent_shoot_ID";
770 int parent_shoot_ID =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
773 node_string =
"parent_node_index";
774 int parent_node_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
777 node_string =
"parent_petiole_index";
778 int parent_petiole_index =
parse_xml_tag_int(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
781 node_string =
"base_rotation";
782 vec3 base_rot =
parse_xml_tag_vec3(shoot.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
785 for (pugi::xml_node phytomer = shoot.child(
"phytomer"); phytomer; phytomer = phytomer.next_sibling(
"phytomer")) {
787 pugi::xml_node internode = phytomer.child(
"internode");
790 node_string =
"internode_length";
791 float internode_length =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
794 node_string =
"internode_radius";
795 float internode_radius =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
798 node_string =
"internode_pitch";
799 float internode_pitch =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
802 node_string =
"internode_phyllotactic_angle";
803 float internode_phyllotactic_angle =
parse_xml_tag_float(internode.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
805 float petiole_length;
806 float petiole_radius;
808 float petiole_curvature;
810 std::vector<std::vector<float>> leaf_scale;
811 std::vector<std::vector<float>> leaf_pitch;
812 std::vector<std::vector<float>> leaf_yaw;
813 std::vector<std::vector<float>> leaf_roll;
814 for (pugi::xml_node petiole = internode.child(
"petiole"); petiole; petiole = petiole.next_sibling(
"petiole")) {
817 node_string =
"petiole_length";
818 petiole_length =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
821 node_string =
"petiole_radius";
822 petiole_radius =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
825 node_string =
"petiole_pitch";
826 petiole_pitch =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
829 node_string =
"petiole_curvature";
830 petiole_curvature =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
833 node_string =
"leaflet_scale";
834 leaflet_scale =
parse_xml_tag_float(petiole.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML");
836 leaf_scale.resize(leaf_scale.size() + 1);
837 leaf_pitch.resize(leaf_pitch.size() + 1);
838 leaf_yaw.resize(leaf_yaw.size() + 1);
839 leaf_roll.resize(leaf_roll.size() + 1);
840 for (pugi::xml_node leaf = petiole.child(
"leaf"); leaf; leaf = leaf.next_sibling(
"leaf")) {
843 node_string =
"leaf_scale";
844 leaf_scale.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
847 node_string =
"leaf_pitch";
848 leaf_pitch.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
851 node_string =
"leaf_yaw";
852 leaf_yaw.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
855 node_string =
"leaf_roll";
856 leaf_roll.back().push_back(
parse_xml_tag_float(leaf.child(node_string.c_str()), node_string,
"PlantArchitecture::readPlantStructureXML"));
860 if (shoot_types.find(shoot_type_label) == shoot_types.end()) {
861 helios_runtime_error(
"ERROR (PlantArchitecture::readPlantStructureXML): Shoot type " + shoot_type_label +
" not found in shoot types.");
883 std::string shoot_label =
"shoot_" + std::to_string(phytomer_count);
888 if (parent_shoot_ID < 0) {
889 current_shoot_ID =
addBaseStemShoot(plantID, 1, base_rotation, internode_radius, internode_length, 1.f, 1.f, 0, shoot_label);
890 shoot_ID_mapping[shootID] = current_shoot_ID;
892 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_label, parent_petiole_index);
893 shoot_ID_mapping[shootID] = current_shoot_ID;
902 assert(leaf_scale.size() == leaf_pitch.size());
903 auto phytomer_ptr = plant_instances.at(plantID).shoot_tree.at(current_shoot_ID)->phytomers.back();
904 for (
int petiole = 0; petiole < leaf_scale.size(); petiole++) {
906 float tip_ind = floor(
float(leaf_scale.at(petiole).size() - 1) / 2.f);
907 phytomer_ptr->setLeafPrototypeScale(petiole, leaf_scale.at(petiole).at(tip_ind));
909 phytomer_ptr->scaleLeafPrototypeScale(petiole, 5);
911 for (
int leaf = 0; leaf < phytomer_ptr->leaf_rotation.at(petiole).size(); leaf++) {
912 phytomer_ptr->rotateLeaf(petiole, leaf, make_AxisRotation(
deg2rad(leaf_pitch.at(petiole).at(leaf)),
deg2rad(leaf_yaw.at(petiole).at(leaf)),
deg2rad(-leaf_roll.at(petiole).at(leaf))));
924 std::cout <<
"done." << std::endl;