18using namespace helios;
22static constexpr float MIN_TRIANGLE_AREA_THRESHOLD = 1e-8f;
25 std::string data_str = node_data.child_value();
27 if (!data_str.empty()) {
28 std::istringstream data_stream(data_str);
31 while (data_stream >> tmp_s) {
33 data.push_back(tmp_f);
46 std::string data_str = node_data.child_value();
48 if (!data_str.empty()) {
49 std::istringstream data_stream(data_str);
52 while (data_stream >> tmp_s) {
54 data.push_back(tmp_f);
67 std::string data_str = node_data.child_value();
69 if (!data_str.empty()) {
70 std::istringstream data_stream(data_str);
73 while (data_stream >> tmp_s) {
75 data.push_back(tmp_f);
88 std::string data_str = node_data.child_value();
90 if (!data_str.empty()) {
91 std::istringstream data_stream(data_str);
94 while (data_stream >> tmp_s) {
96 data.push_back(tmp_f);
109 std::string data_str = node_data.child_value();
111 if (!data_str.empty()) {
112 std::istringstream data_stream(data_str);
114 while (data_stream >> tmp_s) {
115 data.push_back(tmp_s);
125 std::string data_str = node_data.child_value();
127 if (!data_str.empty()) {
128 std::istringstream data_stream(data_str);
129 std::vector<std::string> tmp_s(2);
131 while (data_stream >> tmp_s[0]) {
132 data_stream >> tmp_s[1];
147 std::string data_str = node_data.child_value();
149 if (!data_str.empty()) {
150 std::istringstream data_stream(data_str);
151 std::vector<std::string> tmp_s(3);
153 while (data_stream >> tmp_s[0]) {
154 data_stream >> tmp_s[1];
155 data_stream >> tmp_s[2];
170 std::string data_str = node_data.child_value();
172 if (!data_str.empty()) {
173 std::istringstream data_stream(data_str);
174 std::vector<std::string> tmp_s(4);
176 while (data_stream >> tmp_s[0]) {
177 data_stream >> tmp_s[1];
178 data_stream >> tmp_s[2];
179 data_stream >> tmp_s[3];
194 std::string data_str = node_data.child_value();
196 if (!data_str.empty()) {
197 std::istringstream data_stream(data_str);
198 std::vector<std::string> tmp_s(2);
200 while (data_stream >> tmp_s[0]) {
201 data_stream >> tmp_s[1];
216 std::string data_str = node_data.child_value();
218 if (!data_str.empty()) {
219 std::istringstream data_stream(data_str);
220 std::vector<std::string> tmp_s(3);
222 while (data_stream >> tmp_s[0]) {
223 data_stream >> tmp_s[1];
224 data_stream >> tmp_s[2];
239 std::string data_str = node_data.child_value();
241 if (!data_str.empty()) {
242 std::istringstream data_stream(data_str);
243 std::vector<std::string> tmp_s(4);
245 while (data_stream >> tmp_s[0]) {
246 data_stream >> tmp_s[1];
247 data_stream >> tmp_s[2];
248 data_stream >> tmp_s[3];
263 pugi::xml_node objID_node = node.child(
"objID");
278 pugi::xml_node transform_node = node.child(
"transform");
280 std::string transform_str = transform_node.child_value();
281 if (transform_str.empty()) {
285 std::istringstream stream(transform_str);
289 while (stream >> tmp_s) {
305 pugi::xml_node texture_node = node.child(
"texture");
307 if (texfile.empty()) {
317 pugi::xml_node uv_node = node.child(
"textureUV");
318 std::string texUV = uv_node.child_value();
319 if (!texUV.empty()) {
320 std::istringstream uv_stream(texUV);
321 std::vector<std::string> tmp_s(2);
323 while (uv_stream >> tmp_s[0]) {
324 uv_stream >> tmp_s[1];
339 pugi::xml_node sfrac_node = node.child(
"solid_fraction");
341 if (!sfrac.empty()) {
354 pugi::xml_node vertices_node = node.child(
"vertices");
356 std::string vertices_str = vertices_node.child_value();
357 if (!vertices_str.empty()) {
358 std::istringstream stream(vertices_str);
362 while (stream >> tmp_s) {
366 vertices.at(i) = tmp;
381 pugi::xml_node subdiv_node = node.child(
"subdivisions");
383 if (!subdiv.empty()) {
394 pugi::xml_node subdiv_node = node.child(
"subdivisions");
396 if (!subdiv.empty()) {
397 std::istringstream data_stream(subdiv);
398 std::vector<std::string> tmp_s(2);
399 data_stream >> tmp_s[0];
400 data_stream >> tmp_s[1];
411 pugi::xml_node subdiv_node = node.child(
"subdivisions");
413 if (!subdiv.empty()) {
414 std::istringstream data_stream(subdiv);
415 std::vector<std::string> tmp_s(3);
416 data_stream >> tmp_s[0];
417 data_stream >> tmp_s[1];
418 data_stream >> tmp_s[2];
429 pugi::xml_node node_data = node.child(
"nodes");
430 std::string data_str = node_data.child_value();
432 if (!data_str.empty()) {
433 std::istringstream data_stream(data_str);
434 std::vector<std::string> tmp_s(3);
436 while (data_stream >> tmp_s[0]) {
437 data_stream >> tmp_s[1];
438 data_stream >> tmp_s[2];
442 nodes.push_back(tmp);
453 pugi::xml_node node_data = node.child(
"radius");
454 std::string data_str = node_data.child_value();
456 if (!data_str.empty()) {
457 std::istringstream data_stream(data_str);
460 while (data_stream >> tmp_s) {
462 radius.push_back(tmp_f);
474void Context::loadMaterialData(pugi::xml_node mat_node,
const std::string &material_label) {
476 for (pugi::xml_node data = mat_node.child(
"data_uint"); data; data = data.next_sibling(
"data_uint")) {
477 const char *label = data.attribute(
"label").value();
478 std::vector<uint> datav;
480 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_uint> with label " + std::string(label) +
" contained invalid data.");
482 if (datav.size() == 1) {
484 }
else if (datav.size() > 1) {
490 for (pugi::xml_node data = mat_node.child(
"data_int"); data; data = data.next_sibling(
"data_int")) {
491 const char *label = data.attribute(
"label").value();
492 std::vector<int> datav;
494 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_int> with label " + std::string(label) +
" contained invalid data.");
496 if (datav.size() == 1) {
498 }
else if (datav.size() > 1) {
504 for (pugi::xml_node data = mat_node.child(
"data_float"); data; data = data.next_sibling(
"data_float")) {
505 const char *label = data.attribute(
"label").value();
506 std::vector<float> datav;
508 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_float> with label " + std::string(label) +
" contained invalid data.");
510 if (datav.size() == 1) {
512 }
else if (datav.size() > 1) {
518 for (pugi::xml_node data = mat_node.child(
"data_double"); data; data = data.next_sibling(
"data_double")) {
519 const char *label = data.attribute(
"label").value();
520 std::vector<double> datav;
522 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_double> with label " + std::string(label) +
" contained invalid data.");
524 if (datav.size() == 1) {
526 }
else if (datav.size() > 1) {
532 for (pugi::xml_node data = mat_node.child(
"data_vec2"); data; data = data.next_sibling(
"data_vec2")) {
533 const char *label = data.attribute(
"label").value();
534 std::vector<vec2> datav;
536 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_vec2> with label " + std::string(label) +
" contained invalid data.");
538 if (datav.size() == 1) {
540 }
else if (datav.size() > 1) {
546 for (pugi::xml_node data = mat_node.child(
"data_vec3"); data; data = data.next_sibling(
"data_vec3")) {
547 const char *label = data.attribute(
"label").value();
548 std::vector<vec3> datav;
550 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_vec3> with label " + std::string(label) +
" contained invalid data.");
552 if (datav.size() == 1) {
554 }
else if (datav.size() > 1) {
560 for (pugi::xml_node data = mat_node.child(
"data_vec4"); data; data = data.next_sibling(
"data_vec4")) {
561 const char *label = data.attribute(
"label").value();
562 std::vector<vec4> datav;
564 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_vec4> with label " + std::string(label) +
" contained invalid data.");
566 if (datav.size() == 1) {
568 }
else if (datav.size() > 1) {
574 for (pugi::xml_node data = mat_node.child(
"data_int2"); data; data = data.next_sibling(
"data_int2")) {
575 const char *label = data.attribute(
"label").value();
576 std::vector<int2> datav;
578 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_int2> with label " + std::string(label) +
" contained invalid data.");
580 if (datav.size() == 1) {
582 }
else if (datav.size() > 1) {
588 for (pugi::xml_node data = mat_node.child(
"data_int3"); data; data = data.next_sibling(
"data_int3")) {
589 const char *label = data.attribute(
"label").value();
590 std::vector<int3> datav;
592 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_int3> with label " + std::string(label) +
" contained invalid data.");
594 if (datav.size() == 1) {
596 }
else if (datav.size() > 1) {
602 for (pugi::xml_node data = mat_node.child(
"data_int4"); data; data = data.next_sibling(
"data_int4")) {
603 const char *label = data.attribute(
"label").value();
604 std::vector<int4> datav;
606 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_int4> with label " + std::string(label) +
" contained invalid data.");
608 if (datav.size() == 1) {
610 }
else if (datav.size() > 1) {
616 for (pugi::xml_node data = mat_node.child(
"data_string"); data; data = data.next_sibling(
"data_string")) {
617 const char *label = data.attribute(
"label").value();
618 std::vector<std::string> datav;
620 helios_runtime_error(
"ERROR (Context::loadXML): Material data tag <data_string> with label " + std::string(label) +
" contained invalid data.");
622 if (datav.size() == 1) {
624 }
else if (datav.size() > 1) {
630void Context::loadPData(pugi::xml_node p,
uint UUID) {
631 for (pugi::xml_node data = p.child(
"data_int"); data; data = data.next_sibling(
"data_int")) {
632 const char *label = data.attribute(
"label").value();
634 std::vector<int> datav;
636 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int> with label " + std::string(label) +
" contained invalid data.");
639 if (datav.size() == 1) {
641 }
else if (datav.size() > 1) {
646 for (pugi::xml_node data = p.child(
"data_uint"); data; data = data.next_sibling(
"data_uint")) {
647 const char *label = data.attribute(
"label").value();
649 std::vector<uint> datav;
651 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_uint> with label " + std::string(label) +
" contained invalid data.");
654 if (datav.size() == 1) {
656 }
else if (datav.size() > 1) {
661 for (pugi::xml_node data = p.child(
"data_float"); data; data = data.next_sibling(
"data_float")) {
662 const char *label = data.attribute(
"label").value();
664 std::vector<float> datav;
666 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_float> with label " + std::string(label) +
" contained invalid data.");
669 if (datav.size() == 1) {
671 }
else if (datav.size() > 1) {
676 for (pugi::xml_node data = p.child(
"data_double"); data; data = data.next_sibling(
"data_double")) {
677 const char *label = data.attribute(
"label").value();
679 std::vector<double> datav;
681 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_double> with label " + std::string(label) +
" contained invalid data.");
684 if (datav.size() == 1) {
686 }
else if (datav.size() > 1) {
691 for (pugi::xml_node data = p.child(
"data_vec2"); data; data = data.next_sibling(
"data_vec2")) {
692 const char *label = data.attribute(
"label").value();
694 std::vector<vec2> datav;
696 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec2> with label " + std::string(label) +
" contained invalid data.");
699 if (datav.size() == 1) {
701 }
else if (datav.size() > 1) {
706 for (pugi::xml_node data = p.child(
"data_vec3"); data; data = data.next_sibling(
"data_vec3")) {
707 const char *label = data.attribute(
"label").value();
709 std::vector<vec3> datav;
711 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec3> with label " + std::string(label) +
" contained invalid data.");
714 if (datav.size() == 1) {
716 }
else if (datav.size() > 1) {
721 for (pugi::xml_node data = p.child(
"data_vec4"); data; data = data.next_sibling(
"data_vec4")) {
722 const char *label = data.attribute(
"label").value();
724 std::vector<vec4> datav;
726 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_vec4> with label " + std::string(label) +
" contained invalid data.");
729 if (datav.size() == 1) {
731 }
else if (datav.size() > 1) {
736 for (pugi::xml_node data = p.child(
"data_int2"); data; data = data.next_sibling(
"data_int2")) {
737 const char *label = data.attribute(
"label").value();
739 std::vector<int2> datav;
741 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int2> with label " + std::string(label) +
" contained invalid data.");
744 if (datav.size() == 1) {
746 }
else if (datav.size() > 1) {
751 for (pugi::xml_node data = p.child(
"data_int3"); data; data = data.next_sibling(
"data_int3")) {
752 const char *label = data.attribute(
"label").value();
754 std::vector<int3> datav;
756 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int3> with label " + std::string(label) +
" contained invalid data.");
759 if (datav.size() == 1) {
761 }
else if (datav.size() > 1) {
766 for (pugi::xml_node data = p.child(
"data_int4"); data; data = data.next_sibling(
"data_int4")) {
767 const char *label = data.attribute(
"label").value();
769 std::vector<int4> datav;
771 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_int4> with label " + std::string(label) +
" contained invalid data.");
774 if (datav.size() == 1) {
776 }
else if (datav.size() > 1) {
781 for (pugi::xml_node data = p.child(
"data_string"); data; data = data.next_sibling(
"data_string")) {
782 const char *label = data.attribute(
"label").value();
784 std::vector<std::string> datav;
786 helios_runtime_error(
"ERROR (Context::loadXML): Primitive data tag <data_string> with label " + std::string(label) +
" contained invalid data.");
789 if (datav.size() == 1) {
791 }
else if (datav.size() > 1) {
797void Context::loadOData(pugi::xml_node p,
uint ID) {
800 for (pugi::xml_node data = p.child(
"data_int"); data; data = data.next_sibling(
"data_int")) {
801 const char *label = data.attribute(
"label").value();
803 std::vector<int> datav;
805 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int> with label " + std::string(label) +
" contained invalid data.");
808 if (datav.size() == 1) {
810 }
else if (datav.size() > 1) {
815 for (pugi::xml_node data = p.child(
"data_uint"); data; data = data.next_sibling(
"data_uint")) {
816 const char *label = data.attribute(
"label").value();
818 std::vector<uint> datav;
820 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_uint> with label " + std::string(label) +
" contained invalid data.");
823 if (datav.size() == 1) {
825 }
else if (datav.size() > 1) {
830 for (pugi::xml_node data = p.child(
"data_float"); data; data = data.next_sibling(
"data_float")) {
831 const char *label = data.attribute(
"label").value();
833 std::vector<float> datav;
835 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_float> with label " + std::string(label) +
" contained invalid data.");
838 if (datav.size() == 1) {
840 }
else if (datav.size() > 1) {
845 for (pugi::xml_node data = p.child(
"data_double"); data; data = data.next_sibling(
"data_double")) {
846 const char *label = data.attribute(
"label").value();
848 std::vector<double> datav;
850 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_double> with label " + std::string(label) +
" contained invalid data.");
853 if (datav.size() == 1) {
855 }
else if (datav.size() > 1) {
860 for (pugi::xml_node data = p.child(
"data_vec2"); data; data = data.next_sibling(
"data_vec2")) {
861 const char *label = data.attribute(
"label").value();
863 std::vector<vec2> datav;
865 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec2> with label " + std::string(label) +
" contained invalid data.");
868 if (datav.size() == 1) {
870 }
else if (datav.size() > 1) {
875 for (pugi::xml_node data = p.child(
"data_vec3"); data; data = data.next_sibling(
"data_vec3")) {
876 const char *label = data.attribute(
"label").value();
878 std::vector<vec3> datav;
880 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec3> with label " + std::string(label) +
" contained invalid data.");
883 if (datav.size() == 1) {
885 }
else if (datav.size() > 1) {
890 for (pugi::xml_node data = p.child(
"data_vec4"); data; data = data.next_sibling(
"data_vec4")) {
891 const char *label = data.attribute(
"label").value();
893 std::vector<vec4> datav;
895 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_vec4> with label " + std::string(label) +
" contained invalid data.");
898 if (datav.size() == 1) {
900 }
else if (datav.size() > 1) {
905 for (pugi::xml_node data = p.child(
"data_int2"); data; data = data.next_sibling(
"data_int2")) {
906 const char *label = data.attribute(
"label").value();
908 std::vector<int2> datav;
910 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int2> with label " + std::string(label) +
" contained invalid data.");
913 if (datav.size() == 1) {
915 }
else if (datav.size() > 1) {
920 for (pugi::xml_node data = p.child(
"data_int3"); data; data = data.next_sibling(
"data_int3")) {
921 const char *label = data.attribute(
"label").value();
923 std::vector<int3> datav;
925 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int3> with label " + std::string(label) +
" contained invalid data.");
928 if (datav.size() == 1) {
930 }
else if (datav.size() > 1) {
935 for (pugi::xml_node data = p.child(
"data_int4"); data; data = data.next_sibling(
"data_int4")) {
936 const char *label = data.attribute(
"label").value();
938 std::vector<int4> datav;
940 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_int4> with label " + std::string(label) +
" contained invalid data.");
943 if (datav.size() == 1) {
945 }
else if (datav.size() > 1) {
950 for (pugi::xml_node data = p.child(
"data_string"); data; data = data.next_sibling(
"data_string")) {
951 const char *label = data.attribute(
"label").value();
953 std::vector<std::string> datav;
955 helios_runtime_error(
"ERROR (Context::loadXML): Object data tag <data_string> with label " + std::string(label) +
" contained invalid data.");
958 if (datav.size() == 1) {
960 }
else if (datav.size() > 1) {
966void Context::loadOsubPData(pugi::xml_node p,
uint ID) {
969 std::vector<uint> prim_UUIDs = getObjectPointer_private(ID)->
getPrimitiveUUIDs();
973 for (pugi::xml_node prim_data = p.child(
"primitive_data_int"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int")) {
974 const char *label = prim_data.attribute(
"label").value();
977 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
978 if (u >= prim_UUIDs.size()) {
979 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
980 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
984 std::vector<int> datav;
986 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int> with label " + std::string(label) +
" contained invalid data.");
990 if (datav.size() == 1) {
992 }
else if (datav.size() > 1) {
1000 for (pugi::xml_node prim_data = p.child(
"primitive_data_uint"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_uint")) {
1001 const char *label = prim_data.attribute(
"label").value();
1004 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1005 if (u >= prim_UUIDs.size()) {
1006 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1007 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1011 std::vector<uint> datav;
1013 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_uint> with label " + std::string(label) +
" contained invalid data.");
1017 if (datav.size() == 1) {
1019 }
else if (datav.size() > 1) {
1027 for (pugi::xml_node prim_data = p.child(
"primitive_data_float"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_float")) {
1028 const char *label = prim_data.attribute(
"label").value();
1031 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1032 if (u >= prim_UUIDs.size()) {
1033 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1034 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1038 std::vector<float> datav;
1040 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_float> with label " + std::string(label) +
" contained invalid data.");
1044 if (datav.size() == 1) {
1046 }
else if (datav.size() > 1) {
1054 for (pugi::xml_node prim_data = p.child(
"primitive_data_double"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_double")) {
1055 const char *label = prim_data.attribute(
"label").value();
1058 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1059 if (u >= prim_UUIDs.size()) {
1060 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1061 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1065 std::vector<double> datav;
1067 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_double> with label " + std::string(label) +
" contained invalid data.");
1071 if (datav.size() == 1) {
1073 }
else if (datav.size() > 1) {
1081 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec2"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec2")) {
1082 const char *label = prim_data.attribute(
"label").value();
1085 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1086 if (u >= prim_UUIDs.size()) {
1087 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1088 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1092 std::vector<vec2> datav;
1094 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec2> with label " + std::string(label) +
" contained invalid data.");
1098 if (datav.size() == 1) {
1100 }
else if (datav.size() > 1) {
1108 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec3"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec3")) {
1109 const char *label = prim_data.attribute(
"label").value();
1112 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1113 if (u >= prim_UUIDs.size()) {
1114 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1115 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1119 std::vector<vec3> datav;
1121 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec3> with label " + std::string(label) +
" contained invalid data.");
1125 if (datav.size() == 1) {
1127 }
else if (datav.size() > 1) {
1135 for (pugi::xml_node prim_data = p.child(
"primitive_data_vec4"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_vec4")) {
1136 const char *label = prim_data.attribute(
"label").value();
1139 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1140 if (u >= prim_UUIDs.size()) {
1141 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1142 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1146 std::vector<vec4> datav;
1148 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_vec4> with label " + std::string(label) +
" contained invalid data.");
1152 if (datav.size() == 1) {
1154 }
else if (datav.size() > 1) {
1162 for (pugi::xml_node prim_data = p.child(
"primitive_data_int2"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int2")) {
1163 const char *label = prim_data.attribute(
"label").value();
1166 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1167 if (u >= prim_UUIDs.size()) {
1168 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1169 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1173 std::vector<int2> datav;
1175 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int2> with label " + std::string(label) +
" contained invalid data.");
1179 if (datav.size() == 1) {
1181 }
else if (datav.size() > 1) {
1189 for (pugi::xml_node prim_data = p.child(
"primitive_data_int3"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int3")) {
1190 const char *label = prim_data.attribute(
"label").value();
1193 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1194 if (u >= prim_UUIDs.size()) {
1195 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1196 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1200 std::vector<int3> datav;
1202 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int3> with label " + std::string(label) +
" contained invalid data.");
1206 if (datav.size() == 1) {
1208 }
else if (datav.size() > 1) {
1216 for (pugi::xml_node prim_data = p.child(
"primitive_data_int4"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_int4")) {
1217 const char *label = prim_data.attribute(
"label").value();
1220 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1221 if (u >= prim_UUIDs.size()) {
1222 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1223 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1227 std::vector<int4> datav;
1229 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_int4> with label " + std::string(label) +
" contained invalid data.");
1233 if (datav.size() == 1) {
1235 }
else if (datav.size() > 1) {
1243 for (pugi::xml_node prim_data = p.child(
"primitive_data_string"); prim_data; prim_data = prim_data.next_sibling(
"primitive_data_string")) {
1244 const char *label = prim_data.attribute(
"label").value();
1247 for (pugi::xml_node data = prim_data.child(
"data"); data; data = data.next_sibling(
"data")) {
1248 if (u >= prim_UUIDs.size()) {
1249 std::cerr <<
"WARNING (Context::loadXML): There was a problem with reading object primitive data \"" << label
1250 <<
"\". The number of data values provided does not match the number of primitives contained in this object. Skipping remaining data values." << std::endl;
1254 std::vector<std::string> datav;
1256 helios_runtime_error(
"ERROR (Context::loadXML): Object member primitive data tag <primitive_data_string> with label " + std::string(label) +
" contained invalid data.");
1260 if (datav.size() == 1) {
1262 }
else if (datav.size() > 1) {
1273 std::cout <<
"Loading XML file: " << filename <<
"..." << std::flush;
1276 std::string fn = filename;
1278 if (ext !=
".xml" && ext !=
".XML") {
1284 std::string resolved_filename = resolved_path.string();
1286 XMLfiles.emplace_back(resolved_filename);
1289 std::vector<uint> UUID;
1292 pugi::xml_document xmldoc;
1295 pugi::xml_parse_result load_result = xmldoc.load_file(resolved_filename.c_str());
1299 helios_runtime_error(
"failed.\n XML [" + std::string(filename) +
"] parsed with errors, attr value: [" + xmldoc.child(
"node").attribute(
"attr").value() +
"]\nError description: " + load_result.description() +
1300 "\nError offset: " + std::to_string(load_result.offset) +
" (error at [..." + (filename + load_result.offset) +
"]\n");
1303 pugi::xml_node helios = xmldoc.child(
"helios");
1305 if (helios.empty()) {
1307 std::cout <<
"failed." << std::endl;
1309 helios_runtime_error(
"ERROR (Context::loadXML): XML file must have tag '<helios> ... </helios>' bounding all other tags.");
1313 std::map<uint, std::vector<uint>> object_prim_UUIDs;
1317 for (pugi::xml_node p = helios.child(
"date"); p; p = p.next_sibling(
"date")) {
1318 pugi::xml_node year_node = p.child(
"year");
1319 const char *year_str = year_node.child_value();
1322 helios_runtime_error(
"ERROR (Context::loadXML): Year given in 'date' block must be an integer value.");
1325 pugi::xml_node month_node = p.child(
"month");
1326 const char *month_str = month_node.child_value();
1329 helios_runtime_error(
"ERROR (Context::loadXML): Month given in 'date' block must be an integer value.");
1332 pugi::xml_node day_node = p.child(
"day");
1333 const char *day_str = day_node.child_value();
1336 helios_runtime_error(
"ERROR (Context::loadXML): Day given in 'date' block must be an integer value.");
1342 for (pugi::xml_node p = helios.child(
"time"); p; p = p.next_sibling(
"time")) {
1343 pugi::xml_node hour_node = p.child(
"hour");
1344 const char *hour_str = hour_node.child_value();
1347 helios_runtime_error(
"ERROR (Context::loadXML): Hour given in 'time' block must be an integer value.");
1350 pugi::xml_node minute_node = p.child(
"minute");
1351 const char *minute_str = minute_node.child_value();
1354 helios_runtime_error(
"ERROR (Context::loadXML): Minute given in 'time' block must be an integer value.");
1357 pugi::xml_node second_node = p.child(
"second");
1358 const char *second_str = second_node.child_value();
1361 helios_runtime_error(
"ERROR (Context::loadXML): Second given in 'time' block must be an integer value.");
1364 setTime(second, minute, hour);
1369 std::map<uint, std::string> legacy_material_id_to_label;
1371 for (pugi::xml_node m = helios.child(
"materials"); m; m = m.next_sibling(
"materials")) {
1372 for (pugi::xml_node mat = m.child(
"material"); mat; mat = mat.next_sibling(
"material")) {
1373 std::string material_label;
1375 std::string texture_file;
1376 bool texture_override =
false;
1379 pugi::xml_attribute label_attr = mat.attribute(
"label");
1380 if (!label_attr.empty()) {
1381 material_label = label_attr.value();
1384 pugi::xml_attribute id_attr = mat.attribute(
"id");
1385 if (!id_attr.empty()) {
1387 const char *id_str = id_attr.value();
1389 helios_runtime_error(
"ERROR (Context::loadXML): Material ID must be an unsigned integer value.");
1392 material_label =
"__auto_material_" + std::to_string(matID);
1393 legacy_material_id_to_label[matID] = material_label;
1395 helios_runtime_error(
"ERROR (Context::loadXML): Material must have either a 'label' or 'id' attribute.");
1400 pugi::xml_node color_node = mat.child(
"color");
1401 if (!color_node.empty()) {
1402 const char *color_str = color_node.child_value();
1403 std::istringstream color_stream(color_str);
1404 std::vector<float> color_vec;
1406 while (color_stream >> tmp) {
1407 color_vec.push_back(tmp);
1409 if (color_vec.size() == 3) {
1410 color =
make_RGBAcolor(color_vec.at(0), color_vec.at(1), color_vec.at(2), 1.f);
1411 }
else if (color_vec.size() == 4) {
1412 color =
make_RGBAcolor(color_vec.at(0), color_vec.at(1), color_vec.at(2), color_vec.at(3));
1417 pugi::xml_node texture_node = mat.child(
"texture");
1418 if (!texture_node.empty()) {
1419 texture_file =
deblank(texture_node.child_value());
1420 if (!texture_file.empty()) {
1421 addTexture(texture_file.c_str());
1426 pugi::xml_node override_node = mat.child(
"texture_override");
1427 if (!override_node.empty()) {
1428 const char *override_str = override_node.child_value();
1430 if (
parse_int(override_str, override_val)) {
1431 texture_override = (override_val != 0);
1437 pugi::xml_node twosided_node = mat.child(
"twosided_flag");
1438 if (!twosided_node.empty()) {
1439 const char *twosided_str = twosided_node.child_value();
1441 if (
parse_int(twosided_str, twosided_val) && twosided_val >= 0) {
1442 twosided = (
uint) twosided_val;
1449 uint newID = currentMaterialID++;
1450 Material loaded_mat(newID, material_label, color, texture_file, texture_override, twosided);
1451 materials[newID] = loaded_mat;
1452 material_label_to_id[material_label] = newID;
1456 if (!texture_file.empty()) {
1464 loadMaterialData(mat, material_label);
1469 for (pugi::xml_node p = helios.child(
"patch"); p; p = p.next_sibling(
"patch")) {
1473 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'patch' block must be a non-negative integer value.");
1477 float transform[16];
1480 helios_runtime_error(
"ERROR (Context::loadXML): Patch <transform> node contains less than 16 data values.");
1481 }
else if (result == 2) {
1482 helios_runtime_error(
"ERROR (Context::loadXML): Patch <transform> node contains invalid data.");
1486 std::string texture_file;
1490 std::vector<vec2> uv;
1492 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'patch' block contain invalid data.");
1496 float solid_fraction = -1;
1498 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'patch' block contains invalid data.");
1502 pugi::xml_node material_node = p.child(
"material");
1503 pugi::xml_node material_id_node = p.child(
"material_id");
1504 std::string material_label_from_xml;
1505 bool has_material =
false;
1507 if (!material_node.empty()) {
1509 material_label_from_xml =
deblank(material_node.child_value());
1510 if (!material_label_from_xml.empty() &&
doesMaterialExist(material_label_from_xml)) {
1511 has_material =
true;
1512 ID =
addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1),
make_SphericalCoord(0, 0),
make_RGBAcolor(0, 0, 0, 1));
1514 }
else if (!material_id_node.empty()) {
1516 uint materialID_from_xml = 0;
1517 const char *mat_id_str = material_id_node.child_value();
1518 if (
parse_uint(mat_id_str, materialID_from_xml)) {
1520 auto it = legacy_material_id_to_label.find(materialID_from_xml);
1521 if (it != legacy_material_id_to_label.end()) {
1522 material_label_from_xml = it->second;
1523 has_material =
true;
1524 ID =
addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1),
make_SphericalCoord(0, 0),
make_RGBAcolor(0, 0, 0, 1));
1529 if (!has_material) {
1532 pugi::xml_node color_node = p.child(
"color");
1534 const char *color_str = color_node.child_value();
1535 if (strlen(color_str) == 0) {
1542 if (strcmp(texture_file.c_str(),
"none") == 0) {
1545 std::string texture_file_copy;
1546 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1547 texture_file_copy = texture_file;
1548 texture_file =
"lib/images/solid.jpg";
1555 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1556 getPrimitivePointer_private(ID)->setTextureFile(texture_file_copy.c_str());
1557 addTexture(texture_file_copy.c_str());
1558 getPrimitivePointer_private(ID)->setSolidFraction(solid_fraction);
1563 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1566 if (has_material && !material_label_from_xml.empty()) {
1571 object_prim_UUIDs[objID].push_back(ID);
1586 for (pugi::xml_node tri = helios.child(
"triangle"); tri; tri = tri.next_sibling(
"triangle")) {
1590 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'triangle' block must be a non-negative integer value.");
1594 float transform[16];
1597 helios_runtime_error(
"ERROR (Context::loadXML): Triangle <transform> node contains less than 16 data values.");
1598 }
else if (result == 2) {
1599 helios_runtime_error(
"ERROR (Context::loadXML): Triangle <transform> node contains invalid data.");
1603 std::string texture_file;
1607 std::vector<vec2> uv;
1609 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'triangle' block contain invalid data.");
1613 float solid_fraction = -1;
1615 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'triangle' block contains invalid data.");
1619 pugi::xml_node material_node_tri = tri.child(
"material");
1620 pugi::xml_node material_id_node_tri = tri.child(
"material_id");
1621 std::string material_label_from_xml_tri;
1622 bool has_material_tri =
false;
1624 if (!material_node_tri.empty()) {
1626 material_label_from_xml_tri =
deblank(material_node_tri.child_value());
1627 if (!material_label_from_xml_tri.empty() &&
doesMaterialExist(material_label_from_xml_tri)) {
1628 has_material_tri =
true;
1630 }
else if (!material_id_node_tri.empty()) {
1632 uint materialID_from_xml_tri = 0;
1633 const char *mat_id_str = material_id_node_tri.child_value();
1634 if (
parse_uint(mat_id_str, materialID_from_xml_tri)) {
1636 auto it = legacy_material_id_to_label.find(materialID_from_xml_tri);
1637 if (it != legacy_material_id_to_label.end()) {
1638 material_label_from_xml_tri = it->second;
1639 has_material_tri =
true;
1644 std::vector<vec3> vert_pos;
1646 vert_pos.at(0) =
make_vec3(0.f, 0.f, 0.f);
1647 vert_pos.at(1) =
make_vec3(0.f, 1.f, 0.f);
1648 vert_pos.at(2) =
make_vec3(1.f, 1.f, 0.f);
1650 if (has_material_tri) {
1656 pugi::xml_node color_node = tri.child(
"color");
1658 const char *color_str = color_node.child_value();
1659 if (strlen(color_str) == 0) {
1666 if (strcmp(texture_file.c_str(),
"none") == 0 || uv.empty()) {
1667 ID =
addTriangle(vert_pos.at(0), vert_pos.at(1), vert_pos.at(2), color);
1669 std::string texture_file_copy;
1670 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1671 texture_file_copy = texture_file;
1672 texture_file =
"lib/images/solid.jpg";
1674 ID =
addTriangle(vert_pos.at(0), vert_pos.at(1), vert_pos.at(2), texture_file.c_str(), uv.at(0), uv.at(1), uv.at(2));
1675 if (solid_fraction < 1.f && solid_fraction >= 0.f) {
1676 getPrimitivePointer_private(ID)->setTextureFile(texture_file_copy.c_str());
1677 addTexture(texture_file_copy.c_str());
1678 getPrimitivePointer_private(ID)->setSolidFraction(solid_fraction);
1683 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1686 if (has_material_tri && !material_label_from_xml_tri.empty()) {
1691 object_prim_UUIDs[objID].push_back(ID);
1704 for (pugi::xml_node p = helios.child(
"voxel"); p; p = p.next_sibling(
"voxel")) {
1708 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'voxel' block must be a non-negative integer value.");
1712 float transform[16];
1715 helios_runtime_error(
"ERROR (Context::loadXML): Voxel <transform> node contains less than 16 data values.");
1716 }
else if (result == 2) {
1717 helios_runtime_error(
"ERROR (Context::loadXML): Voxel <transform> node contains invalid data.");
1721 float solid_fraction = 1;
1723 helios_runtime_error(
"ERROR (Context::loadXML): Solid fraction given in 'voxel' block contains invalid data.");
1727 pugi::xml_node material_node_vox = p.child(
"material");
1728 pugi::xml_node material_id_node_vox = p.child(
"material_id");
1729 std::string material_label_from_xml_vox;
1730 bool has_material_vox =
false;
1732 if (!material_node_vox.empty()) {
1734 material_label_from_xml_vox =
deblank(material_node_vox.child_value());
1735 if (!material_label_from_xml_vox.empty() &&
doesMaterialExist(material_label_from_xml_vox)) {
1736 has_material_vox =
true;
1738 }
else if (!material_id_node_vox.empty()) {
1740 uint materialID_from_xml_vox = 0;
1741 const char *mat_id_str = material_id_node_vox.child_value();
1742 if (
parse_uint(mat_id_str, materialID_from_xml_vox)) {
1744 auto it = legacy_material_id_to_label.find(materialID_from_xml_vox);
1745 if (it != legacy_material_id_to_label.end()) {
1746 material_label_from_xml_vox = it->second;
1747 has_material_vox =
true;
1752 if (has_material_vox) {
1754 ID =
addVoxel(
make_vec3(0, 0, 0),
make_vec3(0, 0, 0), 0,
make_RGBAcolor(0, 0, 0, 1));
1758 pugi::xml_node color_node = p.child(
"color");
1760 const char *color_str = color_node.child_value();
1761 if (strlen(color_str) == 0) {
1771 getPrimitivePointer_private(ID)->setTransformationMatrix(transform);
1774 if (has_material_vox && !material_label_from_xml_vox.empty()) {
1779 object_prim_UUIDs[objID].push_back(ID);
1794 for (pugi::xml_node p = helios.child(
"tile"); p; p = p.next_sibling(
"tile")) {
1798 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'tile' block must be a non-negative integer value.");
1802 float transform[16];
1805 helios_runtime_error(
"ERROR (Context::loadXML): Tile <transform> node contains less than 16 data values.");
1806 }
else if (result == 2) {
1807 helios_runtime_error(
"ERROR (Context::loadXML): Tile <transform> node contains invalid data.");
1811 std::string texture_file;
1815 std::vector<vec2> uv;
1817 helios_runtime_error(
"ERROR (Context::loadXML): (u,v) coordinates given in 'tile' block contain invalid data.");
1822 pugi::xml_node color_node = p.child(
"color");
1824 const char *color_str = color_node.child_value();
1825 if (strlen(color_str) != 0) {
1832 if (result_subdiv == 1) {
1833 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for tile was not provided. Assuming 1x1." << std::endl;
1835 }
else if (result_subdiv == 2) {
1836 helios_runtime_error(
"ERROR (Context::loadXML): Tile <subdivisions> node contains invalid data. ");
1841 patch.setTransformationMatrix(transform);
1856 if (strcmp(texture_file.c_str(),
"none") == 0) {
1857 if (strlen(color_str) == 0) {
1871 if (objID > 0 && !object_prim_UUIDs.empty() && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1882 loadOsubPData(p, ID);
1889 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1893 for (pugi::xml_node p = helios.child(
"sphere"); p; p = p.next_sibling(
"sphere")) {
1897 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'sphere' block must be a non-negative integer value.");
1901 objID = currentObjectID;
1906 float transform[16];
1909 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <transform> node contains less than 16 data values.");
1910 }
else if (result == 2) {
1911 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <transform> node contains invalid data.");
1915 std::string texture_file;
1920 pugi::xml_node color_node = p.child(
"color");
1922 const char *color_str = color_node.child_value();
1923 if (strlen(color_str) != 0) {
1930 if (result_subdiv == 1) {
1931 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for sphere was not provided. Assuming 1x1." << std::endl;
1933 }
else if (result_subdiv == 2) {
1934 helios_runtime_error(
"ERROR (Context::loadXML): Sphere <subdivisions> node contains invalid data. ");
1938 std::vector<uint> empty;
1939 Sphere sphere(0, empty, 3,
"",
this);
1943 if (strcmp(texture_file.c_str(),
"none") == 0) {
1944 if (strlen(color_str) == 0) {
1954 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
1967 loadOsubPData(p, ID);
1974 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
1978 for (pugi::xml_node p = helios.child(
"tube"); p; p = p.next_sibling(
"tube")) {
1982 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'tube' block must be a non-negative integer value.");
1986 objID = currentObjectID;
1991 float transform[16];
1994 helios_runtime_error(
"ERROR (Context::loadXML): Tube <transform> node contains less than 16 data values.");
1995 }
else if (result == 2) {
1996 helios_runtime_error(
"ERROR (Context::loadXML): Tube <transform> node contains invalid data.");
2000 std::string texture_file;
2006 if (result_subdiv == 1) {
2007 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for tube was not provided. Assuming 1x1." << std::endl;
2009 }
else if (result_subdiv == 2) {
2010 helios_runtime_error(
"ERROR (Context::loadXML): Tube <subdivisions> node contains invalid data. ");
2014 std::vector<vec3> nodes;
2015 pugi::xml_node nodes_node = p.child(
"nodes");
2021 std::vector<float> radii;
2022 pugi::xml_node radii_node = p.child(
"radius");
2029 pugi::xml_node color_node = p.child(
"color");
2030 const char *color_str = color_node.child_value();
2032 std::vector<RGBcolor> colors;
2033 if (strlen(color_str) > 0) {
2034 std::istringstream data_stream(color_str);
2035 std::vector<float> tmp;
2038 while (data_stream >> tmp.at(c)) {
2041 colors.push_back(
make_RGBcolor(tmp.at(0), tmp.at(1), tmp.at(2)));
2048 if (texture_file ==
"none") {
2051 ID =
addTubeObject(subdiv, nodes, radii, texture_file.c_str());
2057 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
2070 loadOsubPData(p, ID);
2077 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2081 for (pugi::xml_node p = helios.child(
"box"); p; p = p.next_sibling(
"box")) {
2085 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'box' block must be a non-negative integer value.");
2089 objID = currentObjectID;
2094 float transform[16];
2097 helios_runtime_error(
"ERROR (Context::loadXML): Box <transform> node contains less than 16 data values.");
2098 }
else if (result == 2) {
2103 std::string texture_file;
2108 pugi::xml_node color_node = p.child(
"color");
2110 const char *color_str = color_node.child_value();
2111 if (strlen(color_str) != 0) {
2118 if (result_subdiv == 1) {
2119 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for box was not provided. Assuming 1x1." << std::endl;
2121 }
else if (result_subdiv == 2) {
2122 helios_runtime_error(
"ERROR (Context::loadXML): Box <subdivisions> node contains invalid data. ");
2126 std::vector<uint> empty;
2131 if (strcmp(texture_file.c_str(),
"none") == 0) {
2132 if (strlen(color_str) == 0) {
2142 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
2155 loadOsubPData(p, ID);
2162 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2166 for (pugi::xml_node p = helios.child(
"disk"); p; p = p.next_sibling(
"disk")) {
2170 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'disk' block must be a non-negative integer value.");
2174 objID = currentObjectID;
2179 float transform[16];
2182 helios_runtime_error(
"ERROR (Context::loadXML): Disk <transform> node contains less than 16 data values.");
2183 }
else if (result == 2) {
2184 helios_runtime_error(
"ERROR (Context::loadXML): Disk <transform> node contains invalid data.");
2188 std::string texture_file;
2193 pugi::xml_node color_node = p.child(
"color");
2195 const char *color_str = color_node.child_value();
2196 if (strlen(color_str) != 0) {
2203 if (result_subdiv == 1) {
2204 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for disk was not provided. Assuming 1x1." << std::endl;
2206 }
else if (result_subdiv == 2) {
2207 helios_runtime_error(
"ERROR (Context::loadXML): Disk <subdivisions> node contains invalid data. ");
2211 std::vector<uint> empty;
2216 if (strcmp(texture_file.c_str(),
"none") == 0) {
2217 if (strlen(color_str) == 0) {
2227 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
2240 loadOsubPData(p, ID);
2247 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2251 for (pugi::xml_node p = helios.child(
"cone"); p; p = p.next_sibling(
"cone")) {
2255 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'cone' block must be a non-negative integer value.");
2259 objID = currentObjectID;
2264 float transform[16];
2267 helios_runtime_error(
"ERROR (Context::loadXML): Cone <transform> node contains less than 16 data values.");
2268 }
else if (result == 2) {
2269 helios_runtime_error(
"ERROR (Context::loadXML): Cone <transform> node contains invalid data.");
2273 std::string texture_file;
2278 pugi::xml_node color_node = p.child(
"color");
2280 const char *color_str = color_node.child_value();
2281 if (strlen(color_str) != 0) {
2288 if (result_subdiv == 1) {
2289 std::cerr <<
"WARNING (Context::loadXML): Number of subdivisions for cone was not provided. Assuming 1x1." << std::endl;
2291 }
else if (result_subdiv == 2) {
2292 helios_runtime_error(
"ERROR (Context::loadXML): Cone <subdivisions> node contains invalid data. ");
2296 std::vector<vec3> nodes;
2297 pugi::xml_node nodes_node = p.child(
"nodes");
2303 std::vector<float> radii;
2304 pugi::xml_node radii_node = p.child(
"radius");
2310 if (texture_file ==
"none") {
2313 ID =
addConeObject(subdiv, nodes.at(0), nodes.at(1), radii.at(0), radii.at(1), texture_file.c_str());
2319 if (objID > 0 && object_prim_UUIDs.find(objID) != object_prim_UUIDs.end()) {
2332 loadOsubPData(p, ID);
2339 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2343 for (pugi::xml_node p = helios.child(
"polymesh"); p; p = p.next_sibling(
"polymesh")) {
2347 helios_runtime_error(
"ERROR (Context::loadXML): Object ID (objID) given in 'polymesh' block must be a non-negative integer value.");
2351 objID = currentObjectID;
2361 loadOsubPData(p, ID);
2367 std::vector<uint> childUUIDs = object_prim_UUIDs.at(objID);
2368 UUID.insert(UUID.end(), childUUIDs.begin(), childUUIDs.end());
2373 for (pugi::xml_node data = helios.child(
"globaldata_int"); data; data = data.next_sibling(
"globaldata_int")) {
2374 const char *label = data.attribute(
"label").value();
2376 std::vector<int> datav;
2378 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int> with label " + std::string(label) +
" contained invalid data.");
2381 if (datav.size() == 1) {
2383 }
else if (datav.size() > 1) {
2388 for (pugi::xml_node data = helios.child(
"globaldata_uint"); data; data = data.next_sibling(
"globaldata_uint")) {
2389 const char *label = data.attribute(
"label").value();
2391 std::vector<uint> datav;
2393 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_uint> with label " + std::string(label) +
" contained invalid data.");
2396 if (datav.size() == 1) {
2398 }
else if (datav.size() > 1) {
2403 for (pugi::xml_node data = helios.child(
"globaldata_float"); data; data = data.next_sibling(
"globaldata_float")) {
2404 const char *label = data.attribute(
"label").value();
2406 std::vector<float> datav;
2408 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_float> with label " + std::string(label) +
" contained invalid data.");
2411 if (datav.size() == 1) {
2413 }
else if (datav.size() > 1) {
2418 for (pugi::xml_node data = helios.child(
"globaldata_double"); data; data = data.next_sibling(
"globaldata_double")) {
2419 const char *label = data.attribute(
"label").value();
2421 std::vector<double> datav;
2423 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_double> with label " + std::string(label) +
" contained invalid data.");
2426 if (datav.size() == 1) {
2428 }
else if (datav.size() > 1) {
2433 for (pugi::xml_node data = helios.child(
"globaldata_vec2"); data; data = data.next_sibling(
"globaldata_vec2")) {
2434 const char *label = data.attribute(
"label").value();
2436 std::vector<vec2> datav;
2438 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec2> with label " + std::string(label) +
" contained invalid data.");
2441 if (datav.size() == 1) {
2443 }
else if (datav.size() > 1) {
2448 for (pugi::xml_node data = helios.child(
"globaldata_vec3"); data; data = data.next_sibling(
"globaldata_vec3")) {
2449 const char *label = data.attribute(
"label").value();
2451 std::vector<vec3> datav;
2453 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec3> with label " + std::string(label) +
" contained invalid data.");
2456 if (datav.size() == 1) {
2458 }
else if (datav.size() > 1) {
2463 for (pugi::xml_node data = helios.child(
"globaldata_vec4"); data; data = data.next_sibling(
"globaldata_vec4")) {
2464 const char *label = data.attribute(
"label").value();
2466 std::vector<vec4> datav;
2468 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_vec4> with label " + std::string(label) +
" contained invalid data.");
2471 if (datav.size() == 1) {
2473 }
else if (datav.size() > 1) {
2478 for (pugi::xml_node data = helios.child(
"globaldata_int2"); data; data = data.next_sibling(
"globaldata_int2")) {
2479 const char *label = data.attribute(
"label").value();
2481 std::vector<int2> datav;
2483 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int2> with label " + std::string(label) +
" contained invalid data.");
2486 if (datav.size() == 1) {
2488 }
else if (datav.size() > 1) {
2493 for (pugi::xml_node data = helios.child(
"globaldata_int3"); data; data = data.next_sibling(
"globaldata_int3")) {
2494 const char *label = data.attribute(
"label").value();
2496 std::vector<int3> datav;
2498 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int3> with label " + std::string(label) +
" contained invalid data.");
2501 if (datav.size() == 1) {
2503 }
else if (datav.size() > 1) {
2508 for (pugi::xml_node data = helios.child(
"globaldata_int4"); data; data = data.next_sibling(
"globaldata_int4")) {
2509 const char *label = data.attribute(
"label").value();
2511 std::vector<int4> datav;
2513 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_int4> with label " + std::string(label) +
" contained invalid data.");
2516 if (datav.size() == 1) {
2518 }
else if (datav.size() > 1) {
2523 for (pugi::xml_node data = helios.child(
"globaldata_string"); data; data = data.next_sibling(
"globaldata_string")) {
2524 const char *label = data.attribute(
"label").value();
2526 std::vector<std::string> datav;
2528 helios_runtime_error(
"ERROR (Context::loadXML): Global data tag <globaldata_string> with label " + std::string(label) +
" contained invalid data.");
2531 if (datav.size() == 1) {
2533 }
else if (datav.size() > 1) {
2539 for (pugi::xml_node p = helios.child(
"timeseries"); p; p = p.next_sibling(
"timeseries")) {
2540 const char *label = p.attribute(
"label").value();
2542 for (pugi::xml_node d = p.child(
"datapoint"); d; d = d.next_sibling(
"datapoint")) {
2544 pugi::xml_node time_node = d.child(
"time");
2545 const char *time_str = time_node.child_value();
2546 if (strlen(time_str) > 0) {
2548 if (time_.
x < 0 || time_.
x > 23) {
2549 helios_runtime_error(
"ERROR (Context::loadXML): Invalid hour of " + std::to_string(time_.
x) +
" given in timeseries. Hour must be positive and not greater than 23.");
2550 }
else if (time_.
y < 0 || time_.
y > 59) {
2551 helios_runtime_error(
"ERROR (Context::loadXML): Invalid minute of " + std::to_string(time_.
y) +
" given in timeseries. Minute must be positive and not greater than 59.");
2552 }
else if (time_.
z < 0 || time_.
z > 59) {
2553 helios_runtime_error(
"ERROR (Context::loadXML): Invalid second of " + std::to_string(time_.
z) +
" given in timeseries. Second must be positive and not greater than 59.");
2557 helios_runtime_error(
"ERROR (Context::loadXML): No time was specified for timeseries datapoint.");
2561 bool date_flag =
false;
2563 pugi::xml_node date_node = d.child(
"date");
2564 const char *date_str = date_node.child_value();
2565 if (strlen(date_str) > 0) {
2567 if (date_.
x < 1 || date_.
x > 31) {
2568 helios_runtime_error(
"ERROR (Context::loadXML): Invalid day of month " + std::to_string(date_.
x) +
" given in timeseries. Day must be greater than zero and not greater than 31.");
2569 }
else if (date_.
y < 1 || date_.
y > 12) {
2570 helios_runtime_error(
"ERROR (Context::loadXML): Invalid month of " + std::to_string(date_.
y) +
" given in timeseries. Month must be greater than zero and not greater than 12.");
2571 }
else if (date_.
z < 1000 || date_.
z > 10000) {
2572 helios_runtime_error(
"ERROR (Context::loadXML): Invalid year of " + std::to_string(date_.
z) +
" given in timeseries. Year should be in YYYY format.");
2578 pugi::xml_node Jdate_node = d.child(
"dateJulian");
2579 const char *Jdate_str = Jdate_node.child_value();
2580 if (strlen(Jdate_str) > 0) {
2582 if (date_.
x < 1 || date_.
x > 366) {
2583 helios_runtime_error(
"ERROR (Context::loadXML): Invalid Julian day of year " + std::to_string(date_.
x) +
" given in timeseries. Julian day must be greater than zero and not greater than 366.");
2584 }
else if (date_.
y < 1000 || date_.
y > 10000) {
2585 helios_runtime_error(
"ERROR (Context::loadXML): Invalid year of " + std::to_string(date_.
y) +
" given in timeseries. Year should be in YYYY format.");
2592 helios_runtime_error(
"ERROR (Context::loadXML): No date was specified for timeseries datapoint.");
2596 pugi::xml_node value_node = d.child(
"value");
2597 const char *value_str = value_node.child_value();
2598 if (strlen(value_str) > 0) {
2600 helios_runtime_error(
"ERROR (Context::loadXML): Datapoint value in 'timeseries' block must be a float value.");
2603 helios_runtime_error(
"ERROR (Context::loadXML): No value was specified for timeseries datapoint.");
2611 std::cout <<
"done." << std::endl;
2622 const std::string &fn = filename;
2624 if (ext !=
".xml" && ext !=
".XML") {
2629 pugi::xml_document xmldoc;
2632 pugi::xml_parse_result load_result = xmldoc.load_file(filename.c_str());
2636 helios_runtime_error(
"failed.\n XML [" + filename +
"] parsed with errors, attr value: [" + xmldoc.child(
"node").attribute(
"attr").value() +
"]\nError description: " + load_result.description() +
2637 "\nError offset: " + std::to_string(load_result.offset) +
" (error at [..." + (filename.c_str() + load_result.offset) +
"]\n");
2640 pugi::xml_node helios = xmldoc.child(
"helios");
2642 if (helios.empty()) {
2646 for (pugi::xml_node p = helios.child(tag.c_str()); p; p = p.next_sibling(tag.c_str())) {
2647 const char *labelquery = p.attribute(
"label").value();
2649 if (labelquery == label || label.empty()) {
2657void Context::writeDataToXMLstream(
const char *data_group,
const std::vector<std::string> &data_labels,
void *ptr, std::ofstream &outfile)
const {
2658 for (
const auto &label: data_labels) {
2661 if (strcmp(data_group,
"primitive") == 0) {
2662 dtype = ((Primitive *) ptr)->getPrimitiveDataType(label.c_str());
2663 }
else if (strcmp(data_group,
"object") == 0) {
2664 dtype = ((
CompoundObject *) ptr)->getObjectDataType(label.c_str());
2665 }
else if (strcmp(data_group,
"material") == 0) {
2666 dtype = ((
Material *) ptr)->getMaterialDataType(label.c_str());
2667 }
else if (strcmp(data_group,
"global") == 0) {
2670 helios_runtime_error(
"ERROR (Context::writeDataToXMLstream): unknown data group argument of " + std::string(data_group) +
". Must be one of primitive, object, material, or global.");
2673 if (dtype == HELIOS_TYPE_UINT) {
2674 outfile <<
"\t<data_uint label=\"" << label <<
"\">" << std::flush;
2675 std::vector<uint> data;
2676 if (strcmp(data_group,
"primitive") == 0) {
2677 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2678 }
else if (strcmp(data_group,
"object") == 0) {
2680 }
else if (strcmp(data_group,
"material") == 0) {
2681 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2685 for (
int j = 0; j < data.size(); j++) {
2686 outfile << data.at(j) << std::flush;
2687 if (j != data.size() - 1) {
2688 outfile <<
" " << std::flush;
2691 outfile <<
"</data_uint>" << std::endl;
2692 }
else if (dtype == HELIOS_TYPE_INT) {
2693 outfile <<
"\t<data_int label=\"" << label <<
"\">" << std::flush;
2694 std::vector<int> data;
2695 if (strcmp(data_group,
"primitive") == 0) {
2696 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2697 }
else if (strcmp(data_group,
"object") == 0) {
2699 }
else if (strcmp(data_group,
"material") == 0) {
2700 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2704 for (
int j = 0; j < data.size(); j++) {
2705 outfile << data.at(j) << std::flush;
2706 if (j != data.size() - 1) {
2707 outfile <<
" " << std::flush;
2710 outfile <<
"</data_int>" << std::endl;
2711 }
else if (dtype == HELIOS_TYPE_FLOAT) {
2712 outfile <<
"\t<data_float label=\"" << label <<
"\">" << std::flush;
2713 std::vector<float> data;
2714 if (strcmp(data_group,
"primitive") == 0) {
2715 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2716 }
else if (strcmp(data_group,
"object") == 0) {
2718 }
else if (strcmp(data_group,
"material") == 0) {
2719 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2723 for (
int j = 0; j < data.size(); j++) {
2724 outfile << data.at(j) << std::flush;
2725 if (j != data.size() - 1) {
2726 outfile <<
" " << std::flush;
2729 outfile <<
"</data_float>" << std::endl;
2730 }
else if (dtype == HELIOS_TYPE_DOUBLE) {
2731 outfile <<
"\t<data_double label=\"" << label <<
"\">" << std::flush;
2732 std::vector<double> data;
2733 if (strcmp(data_group,
"primitive") == 0) {
2734 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2735 }
else if (strcmp(data_group,
"object") == 0) {
2737 }
else if (strcmp(data_group,
"material") == 0) {
2738 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2742 for (
int j = 0; j < data.size(); j++) {
2743 outfile << data.at(j) << std::flush;
2744 if (j != data.size() - 1) {
2745 outfile <<
" " << std::flush;
2748 outfile <<
"</data_double>" << std::endl;
2749 }
else if (dtype == HELIOS_TYPE_VEC2) {
2750 outfile <<
"\t<data_vec2 label=\"" << label <<
"\">" << std::flush;
2751 std::vector<vec2> data;
2752 if (strcmp(data_group,
"primitive") == 0) {
2753 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2754 }
else if (strcmp(data_group,
"object") == 0) {
2756 }
else if (strcmp(data_group,
"material") == 0) {
2757 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2761 for (
int j = 0; j < data.size(); j++) {
2762 outfile << data.at(j).x <<
" " << data.at(j).y << std::flush;
2763 if (j != data.size() - 1) {
2764 outfile <<
" " << std::flush;
2767 outfile <<
"</data_vec2>" << std::endl;
2768 }
else if (dtype == HELIOS_TYPE_VEC3) {
2769 outfile <<
"\t<data_vec3 label=\"" << label <<
"\">" << std::flush;
2770 std::vector<vec3> data;
2771 if (strcmp(data_group,
"primitive") == 0) {
2772 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2773 }
else if (strcmp(data_group,
"object") == 0) {
2775 }
else if (strcmp(data_group,
"material") == 0) {
2776 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2780 for (
int j = 0; j < data.size(); j++) {
2781 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z << std::flush;
2782 if (j != data.size() - 1) {
2783 outfile <<
" " << std::flush;
2786 outfile <<
"</data_vec3>" << std::endl;
2787 }
else if (dtype == HELIOS_TYPE_VEC4) {
2788 outfile <<
"\t<data_vec4 label=\"" << label <<
"\">" << std::flush;
2789 std::vector<vec4> data;
2790 if (strcmp(data_group,
"primitive") == 0) {
2791 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2792 }
else if (strcmp(data_group,
"object") == 0) {
2794 }
else if (strcmp(data_group,
"material") == 0) {
2795 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2799 for (
int j = 0; j < data.size(); j++) {
2800 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z <<
" " << data.at(j).w << std::flush;
2801 if (j != data.size() - 1) {
2802 outfile <<
" " << std::flush;
2805 outfile <<
"</data_vec4>" << std::endl;
2806 }
else if (dtype == HELIOS_TYPE_INT2) {
2807 outfile <<
"\t<data_int2 label=\"" << label <<
"\">" << std::flush;
2808 std::vector<int2> data;
2809 if (strcmp(data_group,
"primitive") == 0) {
2810 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2811 }
else if (strcmp(data_group,
"object") == 0) {
2813 }
else if (strcmp(data_group,
"material") == 0) {
2814 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2818 for (
int j = 0; j < data.size(); j++) {
2819 outfile << data.at(j).x <<
" " << data.at(j).y << std::flush;
2820 if (j != data.size() - 1) {
2821 outfile <<
" " << std::flush;
2824 outfile <<
"</data_int2>" << std::endl;
2825 }
else if (dtype == HELIOS_TYPE_INT3) {
2826 outfile <<
"\t<data_int3 label=\"" << label <<
"\">" << std::flush;
2827 std::vector<int3> data;
2828 if (strcmp(data_group,
"primitive") == 0) {
2829 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2830 }
else if (strcmp(data_group,
"object") == 0) {
2832 }
else if (strcmp(data_group,
"material") == 0) {
2833 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2837 for (
int j = 0; j < data.size(); j++) {
2838 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z << std::flush;
2839 if (j != data.size() - 1) {
2840 outfile <<
" " << std::flush;
2843 outfile <<
"</data_int3>" << std::endl;
2844 }
else if (dtype == HELIOS_TYPE_INT4) {
2845 outfile <<
"\t<data_int3 label=\"" << label <<
"\">" << std::flush;
2846 std::vector<int4> data;
2847 if (strcmp(data_group,
"primitive") == 0) {
2848 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2849 }
else if (strcmp(data_group,
"object") == 0) {
2851 }
else if (strcmp(data_group,
"material") == 0) {
2852 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2856 for (
int j = 0; j < data.size(); j++) {
2857 outfile << data.at(j).x <<
" " << data.at(j).y <<
" " << data.at(j).z <<
" " << data.at(j).w << std::flush;
2858 if (j != data.size() - 1) {
2859 outfile <<
" " << std::flush;
2862 outfile <<
"</data_int4>" << std::endl;
2863 }
else if (dtype == HELIOS_TYPE_STRING) {
2864 outfile <<
"\t<data_string label=\"" << label <<
"\">" << std::flush;
2865 std::vector<std::string> data;
2866 if (strcmp(data_group,
"primitive") == 0) {
2867 ((Primitive *) ptr)->getPrimitiveData(label.c_str(), data);
2868 }
else if (strcmp(data_group,
"object") == 0) {
2870 }
else if (strcmp(data_group,
"material") == 0) {
2871 ((
Material *) ptr)->getMaterialData(label.c_str(), data);
2875 for (
int j = 0; j < data.size(); j++) {
2876 outfile << data.at(j) << std::flush;
2877 if (j != data.size() - 1) {
2878 outfile <<
" " << std::flush;
2881 outfile <<
"</data_string>" << std::endl;
2891 for (
uint objID: objIDs) {
2893 helios_runtime_error(
"ERROR (Context::writeXML_byobject): Object with ID of " + std::to_string(objID) +
" does not exist.");
2901 std::cout <<
"Writing XML file " << filename <<
"..." << std::flush;
2904 std::string xmlfilename = filename;
2911 helios_runtime_error(
"ERROR (Context::writeXML): Invalid output file " + xmlfilename +
". No file name was provided.");
2915 if (file_extension !=
".xml" && file_extension !=
".XML") {
2916 xmlfilename.append(
".xml");
2921 std::ofstream outfile;
2922 outfile.open(xmlfilename);
2924 outfile <<
"<?xml version=\"1.0\"?>\n\n";
2926 outfile <<
"<helios>\n\n";
2931 std::set<std::string> material_labels_used;
2932 for (
uint UUID: UUIDs) {
2934 uint matID = getPrimitivePointer_private(UUID)->materialID;
2935 if (materials.find(matID) != materials.end()) {
2936 material_labels_used.insert(materials.at(matID).label);
2941 if (!material_labels_used.empty()) {
2942 outfile <<
" <materials>" << std::endl;
2943 for (
const std::string &label: material_labels_used) {
2946 const Material &mat = materials.at(matID);
2947 outfile <<
"\t<material label=\"" << mat.
label <<
"\">" << std::endl;
2948 outfile <<
"\t\t<color>" << mat.
color.
r <<
" " << mat.
color.
g <<
" " << mat.
color.
b <<
" " << mat.
color.
a <<
"</color>" << std::endl;
2950 outfile <<
"\t\t<texture>" << mat.
texture_file <<
"</texture>" << std::endl;
2953 outfile <<
"\t\t<texture_override>1</texture_override>" << std::endl;
2956 outfile <<
"\t\t<twosided_flag>" << mat.
twosided_flag <<
"</twosided_flag>" << std::endl;
2960 if (!mdata.empty()) {
2961 writeDataToXMLstream(
"material", mdata,
const_cast<Material *
>(&mat), outfile);
2963 outfile <<
"\t</material>" << std::endl;
2966 outfile <<
" </materials>\n" << std::endl;
2973 outfile <<
" <date>" << std::endl;
2975 outfile <<
"\t<day>" << date.
day <<
"</day>" << std::endl;
2976 outfile <<
"\t<month>" << date.
month <<
"</month>" << std::endl;
2977 outfile <<
"\t<year>" << date.
year <<
"</year>" << std::endl;
2979 outfile <<
" </date>" << std::endl;
2983 outfile <<
" <time>" << std::endl;
2985 outfile <<
"\t<hour>" << time.
hour <<
"</hour>" << std::endl;
2986 outfile <<
"\t<minute>" << time.
minute <<
"</minute>" << std::endl;
2987 outfile <<
"\t<second>" << time.
second <<
"</second>" << std::endl;
2989 outfile <<
" </time>" << std::endl;
2993 for (
uint UUID: UUIDs) {
2998 helios_runtime_error(
"ERROR (Context::writeXML): Primitive with UUID of " + std::to_string(p) +
" does not exist. There is a compound object with this ID - did you mean to call Context::writeXML_byobject()?");
3000 helios_runtime_error(
"ERROR (Context::writeXML): Primitive with UUID of " + std::to_string(p) +
" does not exist.");
3004 Primitive *prim = getPrimitivePointer_private(p);
3006 uint parent_objID = prim->getParentObjectID();
3010 std::string texture_file = prim->getTextureFile();
3012 std::vector<std::string> pdata = prim->listPrimitiveData();
3021 outfile <<
" <patch>" << std::endl;
3023 outfile <<
" <triangle>" << std::endl;
3025 outfile <<
" <voxel>" << std::endl;
3028 outfile <<
"\t<UUID>" << p <<
"</UUID>" << std::endl;
3030 if (parent_objID > 0) {
3031 outfile <<
"\t<objID>" << parent_objID <<
"</objID>" << std::endl;
3035 if (materials.find(prim->materialID) != materials.end()) {
3036 outfile <<
"\t<material>" << materials.at(prim->materialID).label <<
"</material>" << std::endl;
3039 if (!pdata.empty()) {
3040 writeDataToXMLstream(
"primitive", pdata, prim, outfile);
3045 Patch *patch = getPatchPointer_private(p);
3046 float transform[16];
3047 prim->getTransformationMatrix(transform);
3049 outfile <<
"\t<transform>";
3050 for (
float i: transform) {
3051 outfile << i <<
" ";
3053 outfile <<
"</transform>" << std::endl;
3054 std::vector<vec2> uv = patch->getTextureUV();
3056 outfile <<
"\t<textureUV>" << std::flush;
3057 for (
int i = 0; i < uv.size(); i++) {
3058 outfile << uv.at(i).x <<
" " << uv.at(i).y << std::flush;
3059 if (i != uv.size() - 1) {
3060 outfile <<
" " << std::flush;
3063 outfile <<
"</textureUV>" << std::endl;
3068 outfile <<
" </patch>" << std::endl;
3072 float transform[16];
3073 prim->getTransformationMatrix(transform);
3075 outfile <<
"\t<transform>";
3076 for (
float i: transform) {
3077 outfile << i <<
" ";
3079 outfile <<
"</transform>" << std::endl;
3081 std::vector<vec2> uv = getTrianglePointer_private(p)->getTextureUV();
3083 outfile <<
"\t<textureUV>" << std::flush;
3084 for (
int i = 0; i < uv.size(); i++) {
3085 outfile << uv.at(i).x <<
" " << uv.at(i).y << std::flush;
3086 if (i != uv.size() - 1) {
3087 outfile <<
" " << std::flush;
3090 outfile <<
"</textureUV>" << std::endl;
3095 outfile <<
" </triangle>" << std::endl;
3099 float transform[16];
3100 prim->getTransformationMatrix(transform);
3102 outfile <<
"\t<transform>";
3103 for (
float i: transform) {
3104 outfile << i <<
" ";
3106 outfile <<
"</transform>" << std::endl;
3111 outfile <<
" </voxel>" << std::endl;
3117 for (
auto o: objectIDs) {
3120 std::string texture_file =
obj->getTextureFile();
3122 std::vector<std::string> odata =
obj->listObjectData();
3125 outfile <<
" <tile>" << std::endl;
3127 outfile <<
" <box>" << std::endl;
3129 outfile <<
" <cone>" << std::endl;
3131 outfile <<
" <disk>" << std::endl;
3133 outfile <<
" <sphere>" << std::endl;
3135 outfile <<
" <tube>" << std::endl;
3137 outfile <<
" <polymesh>" << std::endl;
3140 outfile <<
"\t<objID>" << o <<
"</objID>" << std::endl;
3141 if (
obj->hasTexture()) {
3142 outfile <<
"\t<texture>" << texture_file <<
"</texture>" << std::endl;
3145 if (!odata.empty()) {
3146 writeDataToXMLstream(
"object", odata,
obj, outfile);
3149 std::vector<std::string> pdata_labels;
3150 std::vector<HeliosDataType> pdata_types;
3151 std::vector<uint> primitiveUUIDs =
obj->getPrimitiveUUIDs();
3152 for (
uint UUID: primitiveUUIDs) {
3153 std::vector<std::string> labels = getPrimitivePointer_private(UUID)->listPrimitiveData();
3154 for (
const auto &label: labels) {
3155 if (find(pdata_labels.begin(), pdata_labels.end(), label) == pdata_labels.end()) {
3156 pdata_labels.push_back(label);
3161 for (
size_t l = 0; l < pdata_labels.size(); l++) {
3163 outfile <<
"\t<primitive_data_float " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3164 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3166 std::vector<float> data;
3168 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3169 for (
float i: data) {
3170 outfile << i << std::flush;
3172 outfile <<
" </data>" << std::endl;
3175 outfile <<
"\t</primitive_data_float>" << std::endl;
3177 outfile <<
"\t<primitive_data_double " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3178 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3180 std::vector<double> data;
3182 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3183 for (
double i: data) {
3184 outfile << i << std::flush;
3186 outfile <<
" </data>" << std::endl;
3189 outfile <<
"\t</primitive_data_double>" << std::endl;
3191 outfile <<
"\t<primitive_data_uint " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3192 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3194 std::vector<uint> data;
3196 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3197 for (
unsigned int i: data) {
3198 outfile << i << std::flush;
3200 outfile <<
" </data>" << std::endl;
3203 outfile <<
"\t</primitive_data_uint>" << std::endl;
3205 outfile <<
"\t<primitive_data_int " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3206 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3208 std::vector<int> data;
3210 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3212 outfile << i << std::flush;
3214 outfile <<
" </data>" << std::endl;
3217 outfile <<
"\t</primitive_data_int>" << std::endl;
3219 outfile <<
"\t<primitive_data_int2 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3220 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3222 std::vector<int2> data;
3224 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3225 for (
auto &i: data) {
3226 outfile << i.x <<
" " << i.y << std::flush;
3228 outfile <<
" </data>" << std::endl;
3231 outfile <<
"\t</primitive_data_int2>" << std::endl;
3233 outfile <<
"\t<primitive_data_int3 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3234 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3236 std::vector<int3> data;
3238 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3239 for (
auto &i: data) {
3240 outfile << i.x <<
" " << i.y <<
" " << i.z << std::flush;
3242 outfile <<
" </data>" << std::endl;
3245 outfile <<
"\t</primitive_data_int3>" << std::endl;
3247 outfile <<
"\t<primitive_data_int4 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3248 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3250 std::vector<int4> data;
3252 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3253 for (
auto &i: data) {
3254 outfile << i.x <<
" " << i.y <<
" " << i.z <<
" " << i.w << std::flush;
3256 outfile <<
" </data>" << std::endl;
3259 outfile <<
"\t</primitive_data_int4>" << std::endl;
3261 outfile <<
"\t<primitive_data_vec2 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3262 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3264 std::vector<vec2> data;
3266 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3267 for (
auto &i: data) {
3268 outfile << i.x <<
" " << i.y << std::flush;
3270 outfile <<
" </data>" << std::endl;
3273 outfile <<
"\t</primitive_data_vec2>" << std::endl;
3275 outfile <<
"\t<primitive_data_vec3 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3276 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3278 std::vector<vec3> data;
3280 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3281 for (
auto &i: data) {
3282 outfile << i.x <<
" " << i.y <<
" " << i.z << std::flush;
3284 outfile <<
" </data>" << std::endl;
3287 outfile <<
"\t</primitive_data_vec3>" << std::endl;
3289 outfile <<
"\t<primitive_data_vec4 " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3290 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3292 std::vector<vec4> data;
3294 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3295 for (
auto &i: data) {
3296 outfile << i.x <<
" " << i.y <<
" " << i.z <<
" " << i.w << std::flush;
3298 outfile <<
" </data>" << std::endl;
3301 outfile <<
"\t</primitive_data_vec4>" << std::endl;
3303 outfile <<
"\t<primitive_data_string " <<
"label=\"" << pdata_labels.at(l) <<
"\">" << std::endl;
3304 for (
size_t p = 0; p < primitiveUUIDs.size(); p++) {
3306 std::vector<std::string> data;
3308 outfile <<
"\t\t<data label=\"" << p <<
"\"> " << std::flush;
3309 for (
const auto &i: data) {
3310 outfile << i << std::flush;
3312 outfile <<
" </data>" << std::endl;
3315 outfile <<
"\t</primitive_data_string>" << std::endl;
3321 Tile *tile = getTileObjectPointer_private(o);
3323 float transform[16];
3327 outfile <<
"\t<subdivisions>" << subdiv.
x <<
" " << subdiv.
y <<
"</subdivisions>" << std::endl;
3329 outfile <<
"\t<transform> ";
3330 for (
float i: transform) {
3331 outfile << i <<
" ";
3333 outfile <<
"</transform>" << std::endl;
3335 outfile <<
" </tile>" << std::endl;
3339 Sphere *sphere = getSphereObjectPointer_private(o);
3341 float transform[16];
3344 outfile <<
"\t<transform> ";
3345 for (
float i: transform) {
3346 outfile << i <<
" ";
3348 outfile <<
"</transform>" << std::endl;
3351 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
3353 outfile <<
" </sphere>" << std::endl;
3357 Tube *tube = getTubeObjectPointer_private(o);
3359 float transform[16];
3362 outfile <<
"\t<transform> ";
3363 for (
float i: transform) {
3364 outfile << i <<
" ";
3366 outfile <<
"</transform>" << std::endl;
3369 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
3371 std::vector<vec3> nodes = tube->
getNodes();
3374 assert(nodes.size() == radius.size());
3375 outfile <<
"\t<nodes> " << std::endl;
3376 for (
auto &node: nodes) {
3377 outfile <<
"\t\t" << node.x <<
" " << node.y <<
" " << node.z << std::endl;
3379 outfile <<
"\t</nodes> " << std::endl;
3380 outfile <<
"\t<radius> " << std::endl;
3381 for (
float radiu: radius) {
3382 outfile <<
"\t\t" << radiu << std::endl;
3384 outfile <<
"\t</radius> " << std::endl;
3386 if (texture_file.empty()) {
3389 outfile <<
"\t<color> " << std::endl;
3390 for (
auto &color: colors) {
3391 outfile <<
"\t\t" << color.r <<
" " << color.g <<
" " << color.b << std::endl;
3393 outfile <<
"\t</color> " << std::endl;
3396 outfile <<
" </tube>" << std::endl;
3400 Box *box = getBoxObjectPointer_private(o);
3402 float transform[16];
3405 outfile <<
"\t<transform> ";
3406 for (
float i: transform) {
3407 outfile << i <<
" ";
3409 outfile <<
"</transform>" << std::endl;
3412 outfile <<
"\t<subdivisions> " << subdiv.
x <<
" " << subdiv.
y <<
" " << subdiv.
z <<
" </subdivisions>" << std::endl;
3414 outfile <<
" </box>" << std::endl;
3418 Disk *disk = getDiskObjectPointer_private(o);
3420 float transform[16];
3423 outfile <<
"\t<transform> ";
3424 for (
float i: transform) {
3425 outfile << i <<
" ";
3427 outfile <<
"</transform>" << std::endl;
3430 outfile <<
"\t<subdivisions> " << subdiv.
x <<
" " << subdiv.
y <<
" </subdivisions>" << std::endl;
3432 outfile <<
" </disk>" << std::endl;
3436 Cone *cone = getConeObjectPointer_private(o);
3438 float transform[16];
3441 outfile <<
"\t<transform> ";
3442 for (
float i: transform) {
3443 outfile << i <<
" ";
3445 outfile <<
"</transform>" << std::endl;
3448 outfile <<
"\t<subdivisions> " << subdiv <<
" </subdivisions>" << std::endl;
3453 assert(nodes.size() == radius.size());
3454 outfile <<
"\t<nodes> " << std::endl;
3455 for (
auto &node: nodes) {
3456 outfile <<
"\t\t" << node.x <<
" " << node.y <<
" " << node.z << std::endl;
3458 outfile <<
"\t</nodes> " << std::endl;
3459 outfile <<
"\t<radius> " << std::endl;
3460 for (
float radiu: radius) {
3461 outfile <<
"\t\t" << radiu << std::endl;
3463 outfile <<
"\t</radius> " << std::endl;
3465 outfile <<
" </cone>" << std::endl;
3469 outfile <<
" </polymesh>" << std::endl;
3476 for (
const auto &iter: globaldata) {
3477 std::string label = iter.first;
3481 outfile <<
" <globaldata_uint label=\"" << label <<
"\">" << std::flush;
3482 for (
size_t i = 0; i < data.
size; i++) {
3484 if (i != data.
size - 1) {
3485 outfile <<
" " << std::flush;
3488 outfile <<
"</globaldata_uint>" << std::endl;
3490 outfile <<
" <globaldata_int label=\"" << label <<
"\">" << std::flush;
3491 for (
size_t i = 0; i < data.
size; i++) {
3493 if (i != data.
size - 1) {
3494 outfile <<
" " << std::flush;
3497 outfile <<
"</globaldata_int>" << std::endl;
3499 outfile <<
" <globaldata_float label=\"" << label <<
"\">" << std::flush;
3500 for (
size_t i = 0; i < data.
size; i++) {
3502 if (i != data.
size - 1) {
3503 outfile <<
" " << std::flush;
3506 outfile <<
"</globaldata_float>" << std::endl;
3508 outfile <<
" <globaldata_double label=\"" << label <<
"\">" << std::flush;
3509 for (
size_t i = 0; i < data.
size; i++) {
3511 if (i != data.
size - 1) {
3512 outfile <<
" " << std::flush;
3515 outfile <<
"</globaldata_double>" << std::endl;
3517 outfile <<
" <globaldata_vec2 label=\"" << label <<
"\">" << std::endl;
3518 for (
size_t i = 0; i < data.
size; i++) {
3521 outfile <<
" </globaldata_vec2>" << std::endl;
3523 outfile <<
" <globaldata_vec3 label=\"" << label <<
"\">" << std::endl;
3524 for (
size_t i = 0; i < data.
size; i++) {
3527 outfile <<
" </globaldata_vec3>" << std::endl;
3529 outfile <<
" <globaldata_vec4 label=\"" << label <<
"\">" << std::endl;
3530 for (
size_t i = 0; i < data.
size; i++) {
3533 outfile <<
" </globaldata_vec4>" << std::endl;
3535 outfile <<
" <globaldata_int2 label=\"" << label <<
"\">" << std::endl;
3536 for (
size_t i = 0; i < data.
size; i++) {
3539 outfile <<
" </globaldata_int2>" << std::endl;
3541 outfile <<
" <globaldata_int3 label=\"" << label <<
"\">" << std::endl;
3542 for (
size_t i = 0; i < data.
size; i++) {
3545 outfile <<
" </globaldata_int3>" << std::endl;
3547 outfile <<
" <globaldata_int4 label=\"" << label <<
"\">" << std::endl;
3548 for (
size_t i = 0; i < data.
size; i++) {
3551 outfile <<
" </globaldata_int4>" << std::endl;
3553 outfile <<
" <globaldata_string label=\"" << label <<
"\">" << std::flush;
3554 for (
size_t i = 0; i < data.
size; i++) {
3556 if (i != data.
size - 1) {
3557 outfile <<
" " << std::flush;
3560 outfile <<
"</globaldata_string>" << std::endl;
3566 for (
const auto &iter: timeseries_data) {
3567 std::string label = iter.first;
3569 std::vector<float> data = iter.second;
3570 std::vector<double> dateval = timeseries_datevalue.at(label);
3572 assert(data.size() == dateval.size());
3574 outfile <<
" <timeseries label=\"" << label <<
"\">" << std::endl;
3576 for (
size_t i = 0; i < data.size(); i++) {
3580 outfile <<
"\t<datapoint>" << std::endl;
3582 outfile <<
"\t <date>" << a_date.
day <<
" " << a_date.
month <<
" " << a_date.
year <<
"</date>" << std::endl;
3584 outfile <<
"\t <time>" << a_time.
hour <<
" " << a_time.
minute <<
" " << a_time.
second <<
"</time>" << std::endl;
3586 outfile <<
"\t <value>" << data.at(i) <<
"</value>" << std::endl;
3588 outfile <<
"\t</datapoint>" << std::endl;
3591 outfile <<
" </timeseries>" << std::endl;
3596 outfile <<
"\n</helios>\n";
3601 std::cout <<
"done." << std::endl;
3609std::vector<uint>
Context::loadPLY(
const char *filename,
const vec3 &origin,
float height,
const std::string &upaxis,
bool silent) {
3614 return loadPLY(filename, origin, height, rotation, RGB::blue, upaxis, silent);
3617std::vector<uint>
Context::loadPLY(
const char *filename,
const vec3 &origin,
float height,
const RGBcolor &default_color,
const std::string &upaxis,
bool silent) {
3623 std::cout <<
"Reading PLY file " << filename <<
"..." << std::flush;
3626 std::string fn = filename;
3628 if (ext !=
".ply" && ext !=
".PLY") {
3632 if (upaxis !=
"XUP" && upaxis !=
"YUP" && upaxis !=
"ZUP") {
3633 helios_runtime_error(
"ERROR (Context::loadPLY): " + upaxis +
" is not a valid up-axis. Please specify a value of XUP, YUP, or ZUP.");
3636 std::string line, prop;
3638 uint vertexCount = 0, faceCount = 0;
3640 std::vector<vec3> vertices;
3641 std::vector<std::vector<int>> faces;
3642 std::vector<RGBcolor> colors;
3643 std::vector<std::string> properties;
3645 bool ifColor =
false;
3649 std::string resolved_filename = resolved_path.string();
3651 std::ifstream inputPly;
3652 inputPly.open(resolved_filename);
3654 if (!inputPly.is_open()) {
3662 if (
"ply" != line) {
3663 helios_runtime_error(
"ERROR (Context::loadPLY): " + std::string(filename) +
" is not a PLY file.");
3668 if (
"format" != line) {
3669 helios_runtime_error(
"ERROR (Context::loadPLY): could not determine data format of " + std::string(filename));
3673 if (
"ascii" != line) {
3677 std::string temp_string;
3679 while (
"end_header" != line) {
3682 if (
"comment" == line) {
3683 getline(inputPly, line);
3684 }
else if (
"element" == line) {
3687 if (
"vertex" == line) {
3688 inputPly >> temp_string;
3690 helios_runtime_error(
"ERROR (Context::loadPLY): PLY file read failed. Vertex count value should be a non-negative integer.");
3692 }
else if (
"face" == line) {
3693 inputPly >> temp_string;
3695 helios_runtime_error(
"ERROR (Context::loadPLY): PLY file read failed. Face count value should be a non-negative integer.");
3698 }
else if (
"property" == line) {
3701 if (
"list" != line) {
3703 properties.push_back(prop);
3708 for (
auto &property: properties) {
3709 if (property ==
"red") {
3714 std::cout <<
"forming " << faceCount <<
" triangles..." << std::flush;
3717 vertices.resize(vertexCount);
3718 colors.resize(vertexCount);
3719 faces.resize(faceCount);
3724 for (
uint row = 0; row < vertexCount; row++) {
3725 for (
auto &property: properties) {
3726 if (property ==
"x") {
3727 inputPly >> temp_string;
3730 helios_runtime_error(
"ERROR (Context::loadPLY): X value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3732 if (upaxis ==
"XUP") {
3733 vertices.at(row).z = x;
3734 }
else if (upaxis ==
"YUP") {
3735 vertices.at(row).y = x;
3736 }
else if (upaxis ==
"ZUP") {
3737 vertices.at(row).x = x;
3739 }
else if (property ==
"y") {
3740 inputPly >> temp_string;
3743 helios_runtime_error(
"ERROR (Context::loadPLY): Y value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3745 if (upaxis ==
"XUP") {
3746 vertices.at(row).x = y;
3747 }
else if (upaxis ==
"YUP") {
3748 vertices.at(row).z = y;
3749 }
else if (upaxis ==
"ZUP") {
3750 vertices.at(row).y = y;
3752 }
else if (property ==
"z") {
3753 inputPly >> temp_string;
3756 helios_runtime_error(
"ERROR (Context::loadPLY): Z value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3758 if (upaxis ==
"XUP") {
3759 vertices.at(row).y = z;
3760 }
else if (upaxis ==
"YUP") {
3761 vertices.at(row).x = z;
3762 }
else if (upaxis ==
"ZUP") {
3763 vertices.at(row).z = z;
3765 }
else if (property ==
"red") {
3766 inputPly >> temp_string;
3767 if (!
parse_float(temp_string, colors.at(row).r)) {
3768 helios_runtime_error(
"ERROR (Context::loadPLY): red color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3770 colors.at(row).r /= 255.f;
3771 }
else if (property ==
"green") {
3772 inputPly >> temp_string;
3773 if (!
parse_float(temp_string, colors.at(row).g)) {
3774 helios_runtime_error(
"ERROR (Context::loadPLY): green color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3776 colors.at(row).g /= 255.f;
3777 }
else if (property ==
"blue") {
3778 inputPly >> temp_string;
3779 if (!
parse_float(temp_string, colors.at(row).b)) {
3780 helios_runtime_error(
"ERROR (Context::loadPLY): blue color value for vertex " + std::to_string(row) +
" is invalid and could not be read.");
3782 colors.at(row).b /= 255.f;
3788 if (inputPly.eof()) {
3789 helios_runtime_error(
"ERROR (Context::loadPLY): Read past end of file while reading vertices. Vertex count specified in header may be incorrect.");
3798 for (
uint row = 0; row < vertexCount; row++) {
3799 if (vertices.at(row).x < boxmin.
x) {
3800 boxmin.
x = vertices.at(row).x;
3802 if (vertices.at(row).y < boxmin.
y) {
3803 boxmin.
y = vertices.at(row).y;
3805 if (vertices.at(row).z < boxmin.
z) {
3806 boxmin.
z = vertices.at(row).z;
3809 if (vertices.at(row).x > boxmax.
x) {
3810 boxmax.
x = vertices.at(row).x;
3812 if (vertices.at(row).y > boxmax.
y) {
3813 boxmax.
y = vertices.at(row).y;
3815 if (vertices.at(row).z > boxmax.
z) {
3816 boxmax.
z = vertices.at(row).z;
3823 scl = height / (boxmax.
z - boxmin.
z);
3825 for (
uint row = 0; row < vertexCount; row++) {
3826 vertices.at(row).z -= boxmin.
z;
3828 vertices.at(row).x *= scl;
3829 vertices.at(row).y *= scl;
3830 vertices.at(row).z *= scl;
3832 vertices.at(row) =
rotatePoint(vertices.at(row), rotation) + origin;
3838 std::vector<uint> UUID;
3839 for (
uint row = 0; row < faceCount; row++) {
3840 inputPly >> temp_string;
3843 helios_runtime_error(
"ERROR (Context::loadPLY): Vertex count for face " + std::to_string(row) +
" should be a non-negative integer.");
3846 faces.at(row).resize(v);
3848 for (
uint i = 0; i < v; i++) {
3849 inputPly >> temp_string;
3850 if (!
parse_int(temp_string, faces.at(row).at(i))) {
3851 helios_runtime_error(
"ERROR (Context::loadPLY): Vertex index for face " + std::to_string(row) +
" is invalid and could not be read.");
3857 for (
uint t = 2; t < v; t++) {
3860 color = colors.at(faces.at(row).front());
3862 color = default_color;
3865 vec3 v0 = vertices.at(faces.at(row).front());
3866 vec3 v1 = vertices.at(faces.at(row).at(t - 1));
3867 vec3 v2 = vertices.at(faces.at(row).at(t));
3869 if ((v0 - v1).magnitude() < 1e-10f || (v0 - v2).magnitude() < 1e-10f || (v1 - v2).magnitude() < 1e-10f) {
3875 if (triangle_area < MIN_TRIANGLE_AREA_THRESHOLD) {
3884 if (inputPly.eof()) {
3885 helios_runtime_error(
"ERROR (Context::loadPLY): Read past end of file while reading faces. Face count specified in header may be incorrect.");
3890 std::cout <<
"done." << std::endl;
3902 std::string fname{filename ? filename :
""};
3904 const auto dotPos = fname.find_last_of(
'.');
3905 const std::string ext = (dotPos != std::string::npos) ? fname.substr(dotPos) :
"";
3907 auto ciEqual = [](
const char a,
const char b) {
return std::tolower(a) == std::tolower(b); };
3908 bool isPly = (ext.size() == 4) && ciEqual(ext[1],
'p') && ciEqual(ext[2],
'l') && ciEqual(ext[3],
'y');
3911 helios_runtime_error(
"ERROR (Context::writePLY) Invalid file extension for " + fname +
". Expected a file ending in '.ply'.");
3915 std::ofstream PLYfile;
3916 PLYfile.open(fname, std::ios::out | std::ios::trunc);
3918 if (!PLYfile.is_open()) {
3922 PLYfile <<
"ply" << std::endl <<
"format ascii 1.0" << std::endl <<
"comment Helios generated" << std::endl;
3924 std::vector<int3> faces;
3925 std::vector<vec3> verts;
3926 std::vector<RGBcolor> colors;
3928 size_t vertex_count = 0;
3930 for (
auto UUID: UUIDs) {
3931 std::vector<vec3> vertices = getPrimitivePointer_private(UUID)->getVertices();
3932 PrimitiveType type = getPrimitivePointer_private(UUID)->getType();
3933 RGBcolor C = getPrimitivePointer_private(UUID)->getColor();
3937 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2));
3938 for (
int i = 0; i < 3; i++) {
3939 verts.push_back(vertices.at(i));
3940 colors.push_back(C);
3944 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2));
3945 faces.push_back(
make_int3((
int) vertex_count, (
int) vertex_count + 2, (
int) vertex_count + 3));
3946 for (
int i = 0; i < 4; i++) {
3947 verts.push_back(vertices.at(i));
3948 colors.push_back(C);
3954 PLYfile <<
"element vertex " << verts.size() << std::endl;
3955 PLYfile <<
"property float x" << std::endl <<
"property float y" << std::endl <<
"property float z" << std::endl;
3956 PLYfile <<
"property uchar red" << std::endl <<
"property uchar green" << std::endl <<
"property uchar blue" << std::endl;
3957 PLYfile <<
"element face " << faces.size() << std::endl;
3958 PLYfile <<
"property list uchar int vertex_indices" << std::endl <<
"end_header" << std::endl;
3960 for (
size_t v = 0; v < verts.size(); v++) {
3961 PLYfile << verts.at(v).x <<
" " << verts.at(v).y <<
" " << verts.at(v).z <<
" " << round(colors.at(v).r) <<
" " << round(colors.at(v).g) <<
" " << round(colors.at(v).b) << std::endl;
3964 for (
auto &face: faces) {
3965 PLYfile <<
"3 " << face.x <<
" " << face.y <<
" " << face.z << std::endl;
3976 return loadOBJ(filename, origin,
make_vec3(0, 0, height), rotation, default_color,
"ZUP", silent);
3980 return loadOBJ(filename, origin,
make_vec3(0, 0, height), rotation, default_color, upaxis, silent);
3986 std::cout <<
"Reading OBJ file " << filename <<
"..." << std::flush;
3989 std::string fn = filename;
3991 if (ext !=
".obj" && ext !=
".OBJ") {
3995 if (strcmp(upaxis,
"XUP") != 0 && strcmp(upaxis,
"YUP") != 0 && strcmp(upaxis,
"ZUP") != 0) {
3996 helios_runtime_error(
"ERROR (Context::loadOBJ): Up axis of " + std::string(upaxis) +
" is not valid. Should be one of 'XUP', 'YUP', or 'ZUP'.");
3999 std::string line, prop;
4001 std::vector<vec3> vertices;
4002 std::vector<std::string> objects;
4003 std::vector<vec2> texture_uv;
4004 std::map<std::string, std::vector<std::vector<int>>> face_inds, texture_inds;
4006 std::map<std::string, OBJmaterial> materials;
4008 std::vector<uint> UUID;
4012 std::string resolved_filename = resolved_path.string();
4014 std::ifstream inputOBJ, inputMTL;
4015 inputOBJ.open(resolved_filename);
4017 if (!inputOBJ.is_open()) {
4022 std::string filebase =
getFilePath(resolved_filename);
4025 float boxmin = 100000;
4026 float boxmax = -100000;
4028 std::string current_material =
"none";
4029 std::string current_object =
"none";
4032 while (inputOBJ.good()) {
4039 getline(inputOBJ, line);
4042 }
else if (line ==
"mtllib") {
4043 getline(inputOBJ, line);
4045 materials = loadMTL(filebase, material_file, default_color);
4048 }
else if (line ==
"o") {
4049 getline(inputOBJ, line);
4053 }
else if (line ==
"v") {
4054 getline(inputOBJ, line);
4057 vertices.emplace_back(verts);
4058 objects.emplace_back(current_object);
4060 if (verts.
z < boxmin) {
4063 if (verts.
z > boxmax) {
4068 }
else if (line ==
"vt") {
4069 getline(inputOBJ, line);
4073 texture_uv.emplace_back(uv);
4076 }
else if (line ==
"usemtl") {
4077 getline(inputOBJ, line);
4081 }
else if (line ==
"f") {
4082 getline(inputOBJ, line);
4084 std::istringstream stream(line);
4085 std::string tmp, digitf, digitu;
4086 std::vector<int> f, u;
4087 while (stream.good()) {
4094 digitf.push_back(i);
4102 for (
int i = ic + 1; i < tmp.size(); i++) {
4103 if (isdigit(tmp[i])) {
4104 digitu.push_back(tmp[i]);
4110 if (!digitf.empty()) {
4113 helios_runtime_error(
"ERROR (Context::loadOBJ): Face index on line " + std::to_string(lineno) +
" must be a non-negative integer value.");
4116 if (face <= 0 || face > vertices.size()) {
4117 helios_runtime_error(
"ERROR (Context::loadOBJ): Face vertex index " + std::to_string(face) +
" on line " + std::to_string(lineno) +
" is out of range. Valid range is 1-" + std::to_string(vertices.size()) +
4118 ". Check that vertex indices in face definitions reference existing vertices.");
4122 if (!digitu.empty()) {
4125 helios_runtime_error(
"ERROR (Context::loadOBJ): u,v index on line " + std::to_string(lineno) +
" must be a non-negative integer value.");
4128 if (uv <= 0 || uv > texture_uv.size()) {
4129 helios_runtime_error(
"ERROR (Context::loadOBJ): Texture coordinate index " + std::to_string(uv) +
" on line " + std::to_string(lineno) +
" is out of range. Valid range is 1-" + std::to_string(texture_uv.size()) +
4130 ". Check that texture coordinate indices in face definitions reference existing texture coordinates.");
4135 face_inds[current_material].push_back(f);
4136 texture_inds[current_material].push_back(u);
4140 getline(inputOBJ, line);
4145 if (scl.
x == 0 && scl.
y == 0 && scl.
z > 0) {
4146 if (boxmax - boxmin > 1e-6f) {
4147 scl =
make_vec3(scale.
z / (boxmax - boxmin), scale.
z / (boxmax - boxmin), scale.
z / (boxmax - boxmin));
4153 if (scl.
x == 0 && (scl.
y != 0 || scl.
z != 0)) {
4154 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for x-direction is zero. Setting scaling factor to 1" << std::endl;
4156 if (scl.
y == 0 && (scl.
x != 0 || scl.
z != 0)) {
4157 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for y-direction is zero. Setting scaling factor to 1" << std::endl;
4159 if (scl.
z == 0 && (scl.
x != 0 || scl.
y != 0)) {
4160 std::cout <<
"WARNING (Context::loadOBJ): Scaling factor given for z-direction is zero. Setting scaling factor to 1" << std::endl;
4175 struct TriangleData {
4176 vec3 vert0, vert1, vert2;
4177 std::string texture;
4181 bool textureColorIsOverridden;
4185 std::vector<TriangleData> triangleDataList;
4188 for (
auto iter = face_inds.begin(); iter != face_inds.end(); ++iter) {
4189 std::string materialname = iter->first;
4191 std::string texture;
4193 bool textureColorIsOverridden =
false;
4195 if (materials.find(materialname) != materials.end()) {
4196 const OBJmaterial &mat = materials.at(materialname);
4198 texture = mat.texture;
4200 textureColorIsOverridden = mat.textureColorIsOverridden;
4204 const auto &material_faces = face_inds.at(materialname);
4205 const auto &material_texture_inds = texture_inds.count(materialname) ? texture_inds.at(materialname) : std::vector<std::vector<int>>();
4208 std::string exception_message;
4209 bool exception_occurred =
false;
4212#pragma omp parallel for schedule(dynamic)
4214 for (
int i = 0; i < static_cast<int>(material_faces.size()); i++) {
4216 for (
uint t = 2; t < material_faces[i].size(); t++) {
4217 vec3 v0 = vertices.at(material_faces[i][0] - 1);
4218 vec3 v1 = vertices.at(material_faces[i][t - 1] - 1);
4219 vec3 v2 = vertices.at(material_faces[i][t] - 1);
4221 if ((v0 - v1).magnitude() == 0 || (v0 - v2).magnitude() == 0 || (v1 - v2).magnitude() == 0) {
4225 if (strcmp(upaxis,
"YUP") == 0) {
4243 if (triangle_area > MIN_TRIANGLE_AREA_THRESHOLD) {
4244 TriangleData triangleData;
4245 triangleData.vert0 = vert0;
4246 triangleData.vert1 = vert1;
4247 triangleData.vert2 = vert2;
4248 triangleData.texture = texture;
4249 triangleData.color = color;
4250 triangleData.textureColorIsOverridden = textureColorIsOverridden;
4251 triangleData.object = objects.at(material_faces[i][0] - 1);
4255 triangleData.hasTexture = !texture.empty();
4259 if (triangleData.hasTexture && i < material_texture_inds.size() && !material_texture_inds[i].empty() && t < material_texture_inds[i].size()) {
4261 int iuv0 = material_texture_inds[i][0] - 1;
4262 int iuv1 = material_texture_inds[i][t - 1] - 1;
4263 int iuv2 = material_texture_inds[i][t] - 1;
4265 if (iuv0 >= 0 && iuv0 < texture_uv.size() && iuv1 >= 0 && iuv1 < texture_uv.size() && iuv2 >= 0 && iuv2 < texture_uv.size()) {
4266 triangleData.uv0 = texture_uv.at(iuv0);
4267 triangleData.uv1 = texture_uv.at(iuv1);
4268 triangleData.uv2 = texture_uv.at(iuv2);
4270 helios_runtime_error(
"ERROR (Context::loadOBJ): Invalid texture coordinate indices in face for material '" + materialname +
"'. " +
"UV indices [" + std::to_string(iuv0 + 1) +
", " + std::to_string(iuv1 + 1) +
", " +
4271 std::to_string(iuv2 + 1) +
"] " +
"exceed available UV coordinates (1-" + std::to_string(texture_uv.size()) +
"). " +
4272 "Check that all face texture coordinate references in the OBJ file are valid.");
4274 }
else if (triangleData.hasTexture) {
4275 helios_runtime_error(
"ERROR (Context::loadOBJ): Material '" + materialname +
"' specifies texture file '" + texture +
"' " +
"but face has no texture coordinates. Either remove the texture from the material " +
4276 "or add texture coordinates (vt) and face texture indices (f v1/vt1 v2/vt2 v3/vt3) to the OBJ file.");
4283 triangleDataList.push_back(triangleData);
4287 }
catch (
const std::exception &e) {
4293 if (!exception_occurred) {
4294 exception_message = e.what();
4295 exception_occurred =
true;
4302 if (exception_occurred) {
4308 for (
const auto &triangleData: triangleDataList) {
4311 if (triangleData.hasTexture) {
4312 ID =
addTriangle(triangleData.vert0, triangleData.vert1, triangleData.vert2, triangleData.texture.c_str(), triangleData.uv0, triangleData.uv1, triangleData.uv2);
4314 if (triangleData.textureColorIsOverridden) {
4319 ID =
addTriangle(triangleData.vert0, triangleData.vert1, triangleData.vert2, triangleData.color);
4330 std::cout <<
"done." << std::endl;
4336std::map<std::string, Context::OBJmaterial> Context::loadMTL(
const std::string &filebase,
const std::string &material_file,
const RGBcolor &default_color) {
4337 std::ifstream inputMTL;
4339 std::string file = material_file;
4343 std::filesystem::path resolved_path;
4345 if (std::filesystem::path(file).is_absolute()) {
4350 std::filesystem::path mtl_path = std::filesystem::path(filebase) / file;
4354 std::string resolved_file = resolved_path.string();
4355 inputMTL.open(resolved_file.c_str());
4357 if (!inputMTL.is_open()) {
4358 helios_runtime_error(
"ERROR (Context::loadMTL): Could not open material file " + resolved_file +
" after successful path resolution.");
4361 std::map<std::string, OBJmaterial> materials;
4367 while (inputMTL.good()) {
4368 if (strcmp(
"#", line.c_str()) == 0) {
4369 getline(inputMTL, line);
4371 }
else if (line ==
"newmtl") {
4372 getline(inputMTL, line);
4374 OBJmaterial mat(default_color,
"", 0);
4375 materials.emplace(material_name, mat);
4377 std::string map_Kd, map_d;
4379 while (line !=
"newmtl" && inputMTL.good()) {
4382 if (line ==
"newmtl") {
4384 }
else if (line ==
"map_a" || line ==
"map_Ka" || line ==
"Ks" || line ==
"Ka" || line ==
"map_Ks") {
4385 getline(inputMTL, line);
4386 }
else if (line ==
"map_Kd" || line ==
"map_d") {
4387 std::string maptype = line;
4388 getline(inputMTL, line);
4390 std::istringstream stream(line);
4392 while (stream.good()) {
4395 if (ext ==
".png" || ext ==
".PNG" || ext ==
".jpg" || ext ==
".JPG" || ext ==
".jpeg" || ext ==
".JPEG") {
4396 std::string texturefile = tmp;
4399 std::filesystem::path texture_path = texturefile;
4400 bool texture_exists =
false;
4403 if (std::filesystem::exists(texture_path)) {
4404 texture_exists =
true;
4407 texture_path = std::filesystem::path(filebase) / tmp;
4408 texturefile = texture_path.string();
4409 if (std::filesystem::exists(texture_path)) {
4410 texture_exists =
true;
4414 if (!texture_exists) {
4415 helios_runtime_error(
"ERROR (Context::loadOBJ): Texture file '" + tmp +
"' referenced in .mtl file cannot be found. " +
"Searched in current directory and OBJ file directory (" + filebase +
"). " +
4416 "Ensure texture file exists or remove texture reference from material.");
4419 if (maptype ==
"map_d") {
4420 map_d = texturefile;
4422 map_Kd = texturefile;
4426 }
else if (line ==
"Kd") {
4427 getline(inputMTL, line);
4430 materials.at(material_name).color =
make_RGBcolor(color.
r, color.
g, color.
b);
4432 getline(inputMTL, line);
4436 if (!map_Kd.empty()) {
4437 materials.at(material_name).texture = map_Kd;
4438 if (!map_d.empty() && map_d != map_Kd) {
4439 materials.at(material_name).textureHasTransparency =
true;
4441 }
else if (!map_d.empty()) {
4442 materials.at(material_name).texture = map_d;
4443 materials.at(material_name).textureColorIsOverridden =
true;
4446 getline(inputMTL, line);
4458void Context::writeOBJ(
const std::string &filename,
const std::vector<uint> &UUIDs,
bool write_normals,
bool silent)
const {
4459 writeOBJ(filename, UUIDs, {}, write_normals, silent);
4462void Context::writeOBJ(
const std::string &filename,
const std::vector<uint> &UUIDs,
const std::vector<std::string> &primitive_dat_fields,
bool write_normals,
bool silent)
const {
4464 if (UUIDs.empty()) {
4465 std::cout <<
"WARNING (Context::writeOBJ): No primitives found to write - OBJ file " << filename <<
" will not be written." << std::endl;
4468 if (filename.empty()) {
4469 std::cout <<
"WARNING (Context::writeOBJ): Filename was empty - OBJ file " << filename <<
" will not be written." << std::endl;
4473 std::string objfilename = filename;
4474 std::string mtlfilename = filename;
4480 if (file_extension !=
".obj" && file_extension !=
".OBJ") {
4481 objfilename.append(
".obj");
4482 mtlfilename.append(
".mtl");
4484 if (!file_path.empty()) {
4485 std::filesystem::path mtl_path = std::filesystem::path(file_path) / (file_stem +
".mtl");
4486 mtlfilename = mtl_path.string();
4488 mtlfilename = file_stem +
".mtl";
4492 if (!file_path.empty() && !std::filesystem::exists(file_path)) {
4493 if (!std::filesystem::create_directory(file_path)) {
4494 std::cerr <<
"failed. Directory " << file_path <<
" does not exist and it could not be created - OBJ file will not be written." << std::endl;
4500 std::cout <<
"Writing OBJ file " << objfilename <<
"..." << std::flush;
4503 std::vector<OBJmaterial> materials;
4504 std::unordered_map<std::string, uint> material_cache;
4505 const size_t primitive_count = UUIDs.size();
4506 const size_t estimated_vertices = primitive_count * 4;
4508 std::vector<vec3> verts;
4509 verts.reserve(estimated_vertices);
4510 std::vector<vec3> normals;
4511 if (write_normals) {
4512 normals.reserve(primitive_count);
4514 std::vector<vec2> uv;
4515 uv.reserve(estimated_vertices);
4517 std::map<uint, std::vector<int3>> faces;
4518 std::map<uint, std::vector<int>> normal_inds;
4519 std::map<uint, std::vector<int3>> uv_inds;
4520 size_t vertex_count = 1;
4521 size_t normal_count = 0;
4522 size_t uv_count = 1;
4523 std::map<uint, std::vector<uint>> UUIDs_write;
4525 std::map<std::string, std::map<uint, std::vector<int3>>> object_faces;
4526 std::map<std::string, std::map<uint, std::vector<int>>> object_normal_inds;
4527 std::map<std::string, std::map<uint, std::vector<int3>>> object_uv_inds;
4528 std::vector<std::string> object_order;
4529 object_order.reserve(primitive_count / 10);
4530 bool object_groups_found =
false;
4532 for (
size_t p: UUIDs) {
4534 std::ostringstream err_stream;
4535 err_stream <<
"ERROR (Context::writeOBJ): Primitive with UUID " << p <<
" does not exist. "
4536 <<
"Ensure all UUIDs in the input vector correspond to valid primitives before calling writeOBJ.";
4540 const Primitive *prim_ptr = getPrimitivePointer_private(p);
4543 std::ostringstream err_stream;
4544 err_stream <<
"ERROR (Context::writeOBJ): Voxel primitives (UUID " << p <<
") cannot be written to OBJ format. "
4545 <<
"OBJ format only supports surface primitives (triangles, patches). "
4546 <<
"Filter out voxel primitives before calling writeOBJ.";
4550 std::vector<vec3> vertices = prim_ptr->getVertices();
4553 std::string texturefile = prim_ptr->getTextureFile();
4554 bool texture_color_overridden = prim_ptr->isTextureColorOverridden();
4556 std::string obj_label =
"default";
4559 object_groups_found =
true;
4561 if (object_faces.find(obj_label) == object_faces.end()) {
4562 object_faces[obj_label] = {};
4563 object_normal_inds[obj_label] = {};
4564 object_uv_inds[obj_label] = {};
4565 object_order.push_back(obj_label);
4568 std::string material_key = texturefile +
"|" + std::to_string(C.
r) +
"," + std::to_string(C.
g) +
"," + std::to_string(C.
b) +
"|" + std::to_string(texture_color_overridden);
4571 auto material_iter = material_cache.find(material_key);
4573 if (material_iter != material_cache.end()) {
4575 material_ID = material_iter->second;
4578 OBJmaterial mat(C, texturefile, materials.size());
4579 materials.emplace_back(mat);
4580 material_ID = mat.materialID;
4583 materials.back().textureHasTransparency =
true;
4585 if (texture_color_overridden) {
4586 materials.back().textureColorIsOverridden =
true;
4589 material_cache[material_key] = material_ID;
4592 if (!primitive_dat_fields.empty()) {
4593 UUIDs_write[material_ID].push_back(p);
4596 if (write_normals) {
4598 normals.push_back(normal);
4603 int3 ftmp =
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2);
4604 faces[material_ID].push_back(ftmp);
4605 object_faces[obj_label][material_ID].push_back(ftmp);
4606 for (
int i = 0; i < 3; i++) {
4607 verts.push_back(vertices.at(i));
4611 if (write_normals) {
4612 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4613 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4616 std::vector<vec2> uv_v = getTrianglePointer_private(p)->getTextureUV();
4617 if (getTrianglePointer_private(p)->hasTexture()) {
4618 int3 tuv =
make_int3((
int) uv_count, (
int) uv_count + 1, (
int) uv_count + 2);
4619 uv_inds[material_ID].push_back(tuv);
4620 object_uv_inds[obj_label][material_ID].push_back(tuv);
4621 for (
int i = 0; i < 3; i++) {
4622 uv.push_back(uv_v.at(i));
4627 uv_inds[material_ID].push_back(tuv);
4628 object_uv_inds[obj_label][material_ID].push_back(tuv);
4631 int3 ftmp1 =
make_int3((
int) vertex_count, (
int) vertex_count + 1, (
int) vertex_count + 2);
4632 int3 ftmp2 =
make_int3((
int) vertex_count, (
int) vertex_count + 2, (
int) vertex_count + 3);
4633 faces[material_ID].push_back(ftmp1);
4634 faces[material_ID].push_back(ftmp2);
4635 object_faces[obj_label][material_ID].push_back(ftmp1);
4636 object_faces[obj_label][material_ID].push_back(ftmp2);
4637 for (
int i = 0; i < 4; i++) {
4638 verts.push_back(vertices.at(i));
4641 std::vector<vec2> uv_v;
4642 uv_v = getPatchPointer_private(p)->getTextureUV();
4644 if (write_normals) {
4645 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4646 normal_inds[material_ID].push_back(
static_cast<int>(normal_count));
4647 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4648 object_normal_inds[obj_label][material_ID].push_back(
static_cast<int>(normal_count));
4651 if (getPatchPointer_private(p)->hasTexture()) {
4652 int3 tuv1 =
make_int3((
int) uv_count, (
int) uv_count + 1, (
int) uv_count + 2);
4653 int3 tuv2 =
make_int3((
int) uv_count, (
int) uv_count + 2, (
int) uv_count + 3);
4654 uv_inds[material_ID].push_back(tuv1);
4655 uv_inds[material_ID].push_back(tuv2);
4656 object_uv_inds[obj_label][material_ID].push_back(tuv1);
4657 object_uv_inds[obj_label][material_ID].push_back(tuv2);
4665 for (
int i = 0; i < 4; i++) {
4666 uv.push_back(uv_v.at(i));
4672 uv_inds[material_ID].push_back(tuv);
4673 uv_inds[material_ID].push_back(tuv);
4674 object_uv_inds[obj_label][material_ID].push_back(tuv);
4675 object_uv_inds[obj_label][material_ID].push_back(tuv);
4681 assert(normal_inds.size() == faces.size());
4683 assert(uv_inds.size() == faces.size());
4684 for (
int i = 0; i < faces.size(); i++) {
4685 assert(uv_inds.at(i).size() == faces.at(i).size());
4689 std::filesystem::path output_path = std::filesystem::path(file_path);
4690 std::filesystem::path texture_dir = output_path.parent_path();
4693 if (texture_dir.empty()) {
4697 for (
auto &material: materials) {
4698 std::string texture = material.texture;
4699 if (!texture.empty()) {
4701 std::filesystem::path source_path = std::filesystem::absolute(texture, ec);
4705 source_path = std::filesystem::path(texture);
4708 if (!std::filesystem::exists(source_path)) {
4713 auto filename = source_path.filename();
4714 std::filesystem::path dest_path = texture_dir / filename;
4717 bool same_file =
false;
4719 same_file = std::filesystem::equivalent(source_path, dest_path, ec);
4727 material.texture = filename.string();
4733 std::filesystem::copy_file(source_path, dest_path, std::filesystem::copy_options::overwrite_existing, ec);
4735 material.texture = filename.string();
4744 std::ofstream objfstream;
4745 objfstream.open(objfilename);
4746 std::ofstream mtlfstream;
4747 mtlfstream.open(mtlfilename);
4749 objfstream <<
"# Helios auto-generated OBJ File" << std::endl;
4750 objfstream <<
"# baileylab.ucdavis.edu/software/helios" << std::endl;
4751 objfstream <<
"mtllib " <<
getFileName(mtlfilename) << std::endl;
4754 std::vector<std::string> vertex_chunks;
4755 const int num_threads = std::min(
static_cast<int>(verts.size() / 1000 + 1), std::max(1,
static_cast<int>(std::thread::hardware_concurrency())));
4756 vertex_chunks.resize(num_threads);
4759#pragma omp parallel num_threads(num_threads)
4764 tid = omp_get_thread_num();
4766 std::ostringstream vertex_stream;
4767 vertex_stream.precision(8);
4769 const size_t chunk_size = (verts.size() + num_threads - 1) / num_threads;
4770 const size_t start_idx = tid * chunk_size;
4771 const size_t end_idx = std::min(start_idx + chunk_size, verts.size());
4773 for (
size_t i = start_idx; i < end_idx; i++) {
4774 vertex_stream <<
"v " << verts[i].x <<
" " << verts[i].y <<
" " << verts[i].z <<
"\n";
4777 vertex_chunks[tid] = vertex_stream.str();
4780 for (
const auto &chunk: vertex_chunks) {
4781 objfstream << chunk;
4784 if (write_normals) {
4785 std::vector<std::string> normal_chunks;
4786 normal_chunks.resize(num_threads);
4789#pragma omp parallel num_threads(num_threads)
4794 tid = omp_get_thread_num();
4796 std::ostringstream normal_stream;
4797 normal_stream.precision(8);
4799 const size_t chunk_size = (normals.size() + num_threads - 1) / num_threads;
4800 const size_t start_idx = tid * chunk_size;
4801 const size_t end_idx = std::min(start_idx + chunk_size, normals.size());
4803 const float epsilon = 1e-7;
4804 for (
size_t i = start_idx; i < end_idx; i++) {
4805 vec3 n = normals[i];
4806 if (std::abs(n.
x) < epsilon)
4808 if (std::abs(n.
y) < epsilon)
4810 if (std::abs(n.
z) < epsilon)
4812 normal_stream <<
"vn " << n.
x <<
" " << n.
y <<
" " << n.
z <<
"\n";
4815 normal_chunks[tid] = normal_stream.str();
4818 for (
const auto &chunk: normal_chunks) {
4819 objfstream << chunk;
4824 std::vector<std::string> uv_chunks;
4825 uv_chunks.resize(num_threads);
4828#pragma omp parallel num_threads(num_threads)
4833 tid = omp_get_thread_num();
4835 std::ostringstream uv_stream;
4836 uv_stream.precision(8);
4838 const size_t chunk_size = (uv.size() + num_threads - 1) / num_threads;
4839 const size_t start_idx = tid * chunk_size;
4840 const size_t end_idx = std::min(start_idx + chunk_size, uv.size());
4842 for (
size_t i = start_idx; i < end_idx; i++) {
4843 uv_stream <<
"vt " << uv[i].x <<
" " << uv[i].y <<
"\n";
4846 uv_chunks[tid] = uv_stream.str();
4849 for (
const auto &chunk: uv_chunks) {
4850 objfstream << chunk;
4856 if (object_groups_found) {
4859 for (
const auto &obj_label: object_order) {
4860 objfstream <<
"o " << obj_label <<
"\n";
4862 for (
int mat = 0; mat < materials.size(); mat++) {
4863 auto fit = object_faces[obj_label].find(mat);
4864 if (fit == object_faces[obj_label].end())
4867 objfstream <<
"usemtl material" << mat <<
"\n";
4869 const auto ¤t_faces = fit->second;
4870 if (current_faces.size() > 100) {
4872 std::vector<std::string> face_chunks;
4873 face_chunks.resize(num_threads);
4876#pragma omp parallel num_threads(num_threads)
4881 tid = omp_get_thread_num();
4883 std::ostringstream face_stream;
4885 const size_t chunk_size = (current_faces.size() + num_threads - 1) / num_threads;
4886 const size_t start_idx = tid * chunk_size;
4887 const size_t end_idx = std::min(start_idx + chunk_size, current_faces.size());
4889 for (
size_t f = start_idx; f < end_idx; f++) {
4891 if (write_normals) {
4892 face_stream <<
"f " << current_faces[f].x <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"//"
4893 << object_normal_inds[obj_label][mat][f] <<
"\n";
4895 face_stream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z <<
"\n";
4897 }
else if (object_uv_inds[obj_label][mat][f].x < 0) {
4898 face_stream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1\n";
4900 if (write_normals) {
4901 face_stream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y
4902 <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"/" << object_uv_inds[obj_label][mat][f].z <<
"/" << object_normal_inds[obj_label][mat][f] <<
"\n";
4904 face_stream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
" " << current_faces[f].z <<
"/"
4905 << object_uv_inds[obj_label][mat][f].z <<
"\n";
4910 face_chunks[tid] = face_stream.str();
4914 for (
const auto &chunk: face_chunks) {
4915 objfstream << chunk;
4919 for (
size_t f = 0; f < current_faces.size(); ++f) {
4921 if (write_normals) {
4922 objfstream <<
"f " << current_faces[f].x <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"//" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"//"
4923 << object_normal_inds[obj_label][mat][f] << std::endl;
4925 objfstream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z << std::endl;
4927 }
else if (object_uv_inds[obj_label][mat][f].x < 0) {
4928 objfstream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1" << std::endl;
4930 if (write_normals) {
4931 objfstream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
"/" << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
"/"
4932 << object_normal_inds[obj_label][mat][f] <<
" " << current_faces[f].z <<
"/" << object_uv_inds[obj_label][mat][f].z <<
"/" << object_normal_inds[obj_label][mat][f] << std::endl;
4934 objfstream <<
"f " << current_faces[f].x <<
"/" << object_uv_inds[obj_label][mat][f].x <<
" " << current_faces[f].y <<
"/" << object_uv_inds[obj_label][mat][f].y <<
" " << current_faces[f].z <<
"/"
4935 << object_uv_inds[obj_label][mat][f].z << std::endl;
4944 for (
int mat = 0; mat < materials.size(); mat++) {
4945 assert(materials.at(mat).materialID == mat);
4946 objfstream <<
"usemtl material" << mat <<
"\n";
4948 const auto ¤t_faces = faces.at(mat);
4949 if (current_faces.size() > 100) {
4951 std::vector<std::string> face_chunks;
4952 face_chunks.resize(num_threads);
4955#pragma omp parallel num_threads(num_threads)
4960 tid = omp_get_thread_num();
4962 std::ostringstream face_stream;
4964 const size_t chunk_size = (current_faces.size() + num_threads - 1) / num_threads;
4965 const size_t start_idx = tid * chunk_size;
4966 const size_t end_idx = std::min(start_idx + chunk_size, current_faces.size());
4968 for (
size_t f = start_idx; f < end_idx; f++) {
4970 if (write_normals) {
4971 face_stream <<
"f " << current_faces[f].x <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].z <<
"//" << normal_inds.at(mat)[f] <<
"\n";
4973 face_stream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z <<
"\n";
4975 }
else if (uv_inds.at(mat)[f].x < 0) {
4976 face_stream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1\n";
4978 if (write_normals) {
4979 face_stream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
"/" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
"/" << normal_inds.at(mat)[f] <<
" "
4980 << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"/" << normal_inds.at(mat)[f] <<
"\n";
4982 face_stream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
" " << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"\n";
4987 face_chunks[tid] = face_stream.str();
4991 for (
const auto &chunk: face_chunks) {
4992 objfstream << chunk;
4996 for (
int f = 0; f < current_faces.size(); f++) {
4998 if (write_normals) {
4999 objfstream <<
"f " << current_faces[f].x <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"//" << normal_inds.at(mat)[f] <<
" " << current_faces[f].z <<
"//" << normal_inds.at(mat)[f] << std::endl;
5001 objfstream <<
"f " << current_faces[f].x <<
" " << current_faces[f].y <<
" " << current_faces[f].z << std::endl;
5003 }
else if (uv_inds.at(mat)[f].x < 0) {
5004 objfstream <<
"f " << current_faces[f].x <<
"/1 " << current_faces[f].y <<
"/1 " << current_faces[f].z <<
"/1" << std::endl;
5006 if (write_normals) {
5007 objfstream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
"/" << normal_inds.at(mat)[f] <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
"/" << normal_inds.at(mat)[f] <<
" "
5008 << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z <<
"/" << normal_inds.at(mat)[f] << std::endl;
5010 objfstream <<
"f " << current_faces[f].x <<
"/" << uv_inds.at(mat)[f].x <<
" " << current_faces[f].y <<
"/" << uv_inds.at(mat)[f].y <<
" " << current_faces[f].z <<
"/" << uv_inds.at(mat)[f].z << std::endl;
5018 for (
int mat = 0; mat < materials.size(); mat++) {
5019 if (materials.at(mat).texture.empty()) {
5020 RGBcolor current_color = materials.at(mat).color;
5021 mtlfstream <<
"newmtl material" << mat << std::endl;
5022 mtlfstream <<
"Ka " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
5023 mtlfstream <<
"Kd " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
5024 mtlfstream <<
"Ks 0.0 0.0 0.0" << std::endl;
5025 mtlfstream <<
"illum 2 " << std::endl;
5027 std::string current_texture = materials.at(mat).texture;
5028 mtlfstream <<
"newmtl material" << mat << std::endl;
5029 if (materials.at(mat).textureColorIsOverridden) {
5030 RGBcolor current_color = materials.at(mat).color;
5031 mtlfstream <<
"Ka " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
5032 mtlfstream <<
"Kd " << current_color.
r <<
" " << current_color.
g <<
" " << current_color.
b << std::endl;
5034 mtlfstream <<
"map_Kd " << current_texture << std::endl;
5036 if (materials.at(mat).textureHasTransparency) {
5037 mtlfstream <<
"map_d " << current_texture << std::endl;
5039 mtlfstream <<
"Ks 0.0 0.0 0.0" << std::endl;
5040 mtlfstream <<
"illum 2 " << std::endl;
5047 if (!primitive_dat_fields.empty()) {
5048 bool uuidexistswarning =
false;
5049 bool dataexistswarning =
false;
5050 bool datatypewarning =
false;
5052 for (
const std::string &label: primitive_dat_fields) {
5053 std::filesystem::path dat_path = std::filesystem::path(file_path) / (file_stem +
"_" + std::string(label) +
".dat");
5054 std::string datfilename = dat_path.string();
5055 std::ofstream datout(datfilename);
5057 for (
int mat = 0; mat < materials.size(); mat++) {
5058 for (
uint UUID: UUIDs_write.at(mat)) {
5060 uuidexistswarning =
true;
5071 dataexistswarning =
true;
5072 for (
int i = 0; i < Nprims; i++) {
5073 datout << 0 << std::endl;
5082 for (
int i = 0; i < Nprims; i++) {
5083 datout << data << std::endl;
5088 for (
int i = 0; i < Nprims; i++) {
5089 datout << data << std::endl;
5094 for (
int i = 0; i < Nprims; i++) {
5095 datout << data << std::endl;
5100 for (
int i = 0; i < Nprims; i++) {
5101 datout << data << std::endl;
5106 for (
int i = 0; i < Nprims; i++) {
5107 datout << data << std::endl;
5110 datatypewarning =
true;
5111 for (
int i = 0; i < Nprims; i++) {
5112 datout << 0 << std::endl;
5121 if (uuidexistswarning) {
5122 helios_runtime_error(
"Context::writeOBJ: One or more UUIDs do not exist in the Context. Cannot write OBJ file with invalid primitives.");
5124 if (dataexistswarning) {
5125 helios_runtime_error(
"Context::writeOBJ: Primitive data requested did not exist for one or more primitives. Cannot write incomplete data to OBJ file.");
5127 if (datatypewarning) {
5128 helios_runtime_error(
"Context::writeOBJ: Only scalar primitive data types (uint, int, float, double, and string) are supported for primitive data export.");
5137void Context::writePrimitiveData(
const std::string &filename,
const std::vector<std::string> &column_format,
const std::vector<uint> &UUIDs,
bool print_header)
const {
5138 std::ofstream file(filename);
5141 for (
const auto &label: column_format) {
5142 file << label <<
" ";
5144 file.seekp(-1, std::ios_base::end);
5148 bool uuidexistswarning =
false;
5149 bool dataexistswarning =
false;
5150 bool datatypewarning =
false;
5152 for (
uint UUID: UUIDs) {
5154 uuidexistswarning =
true;
5157 for (
const auto &label: column_format) {
5158 if (label ==
"UUID") {
5159 file << UUID <<
" ";
5163 dataexistswarning =
true;
5171 file << data <<
" ";
5175 file << data <<
" ";
5179 file << data <<
" ";
5183 file << data <<
" ";
5187 file << data <<
" ";
5189 datatypewarning =
true;
5193 file.seekp(-1, std::ios_base::end);
5197 if (uuidexistswarning) {
5198 std::cerr <<
"WARNING (Context::writePrimitiveData): Vector of UUIDs passed to writePrimitiveData() function contained UUIDs that do not exist, which were skipped." << std::endl;
5200 if (dataexistswarning) {
5201 std::cerr <<
"WARNING (Context::writePrimitiveData): Primitive data requested did not exist for one or more primitives. A default value of 0 was written in these cases." << std::endl;
5203 if (datatypewarning) {
5204 std::cerr <<
"WARNING (Context::writePrimitiveData): Only scalar primitive data types (uint, int, float, and double) are supported for this function. A column of 0's was written in these cases." << std::endl;
5210void Context::loadTabularTimeseriesData(
const std::string &data_file,
const std::vector<std::string> &col_labels,
const std::string &a_delimeter,
const std::string &a_date_string_format,
uint headerlines) {
5213 std::string resolved_filename = resolved_path.string();
5215 std::ifstream datafile(resolved_filename);
5217 if (!datafile.is_open()) {
5218 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Weather data file '" + data_file +
"' does not exist.");
5223 int datestrcol = -1;
5227 std::map<std::string, int> datacols;
5229 size_t Ncolumns = 0;
5231 size_t row = headerlines;
5233 std::vector<std::string> column_labels = col_labels;
5234 std::string delimiter = a_delimeter;
5235 std::string date_string_format = a_date_string_format;
5238 if (col_labels.size() == 1 && (col_labels.front() ==
"CIMIS" || col_labels.front() ==
"cimis")) {
5240 "",
"",
"",
"date",
"hour",
"DOY",
"ETo",
"",
"precipitation",
"",
"net_radiation",
"",
"vapor_pressure",
"",
"air_temperature",
"",
"air_humidity",
"",
"dew_point",
"",
"wind_speed",
"",
"wind_direction",
"",
"soil_temperature",
""};
5243 date_string_format =
"MMDDYYYY";
5247 if (!column_labels.empty()) {
5249 for (
auto &label: column_labels) {
5250 if (label ==
"year" || label ==
"Year") {
5252 }
else if (label ==
"DOY" || label ==
"Jul") {
5254 }
else if (label ==
"date" || label ==
"Date") {
5256 }
else if (label ==
"hour" || label ==
"Hour") {
5258 }
else if (label ==
"minute" || label ==
"Minute") {
5260 }
else if (label ==
"second" || label ==
"Second") {
5262 }
else if (!label.empty()) {
5263 if (datacols.find(label) == datacols.end()) {
5264 datacols[label] = col;
5266 datacols[label +
"_dup"] = col;
5273 Ncolumns = column_labels.size();
5277 if (headerlines == 0) {
5278 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): "
5280 " argument was specified as zero, and no column label information was given. Attempting to read the first line to see if it contains label information."
5286 if (std::getline(datafile, line)) {
5289 if (line_parsed.empty()) {
5290 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but it did not contain the specified delimiter.");
5293 Ncolumns = line_parsed.size();
5295 for (
int col = 0; col < Ncolumns; col++) {
5296 const std::string &label = line_parsed.at(col);
5298 if (label ==
"year" || label ==
"Year") {
5300 }
else if (label ==
"DOY" || label ==
"Jul") {
5302 }
else if (label ==
"date" || label ==
"Date") {
5304 }
else if (label ==
"hour" || label ==
"Hour") {
5306 }
else if (label ==
"minute" || label ==
"Minute") {
5308 }
else if (label ==
"second" || label ==
"Second") {
5310 }
else if (!label.empty()) {
5311 if (datacols.find(label) == datacols.end()) {
5312 datacols[label] = col;
5314 datacols[label +
"_dup"] = col;
5321 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but read failed.");
5324 if (yearcol == -1 && DOYcol == -1 && datestrcol == -1) {
5325 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Attempted to parse first line of file for column labels, but could not find valid label information.");
5329 if (datestrcol < 0 && (yearcol < 0 || DOYcol < 0)) {
5330 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): The date must be specified by either a column labeled "
5332 ", or by two columns labeled "
5337 }
else if (hourcol < 0) {
5338 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): At a minimum, the time must be specified by a column labeled "
5341 }
else if (datacols.empty()) {
5342 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): No columns were found containing data variables (e.g., temperature, humidity, wind speed).");
5349 for (
int i = 0; i < headerlines; i++) {
5350 std::getline(datafile, line);
5353 while (std::getline(datafile, line)) {
5363 if (line_separated.size() != Ncolumns) {
5364 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Line " + std::to_string(row) +
" had " + std::to_string(line_separated.size()) +
" columns, but was expecting " + std::to_string(Ncolumns));
5369 if (yearcol >= 0 && DOYcol >= 0) {
5371 parse_int(line_separated.at(DOYcol), DOY);
5372 if (DOY < 1 || DOY > 366) {
5373 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid date specified on line " + std::to_string(row) +
".");
5376 parse_int(line_separated.at(yearcol), year);
5378 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid year specified on line " + std::to_string(row) +
".");
5381 }
else if (datestrcol >= 0) {
5383 const std::string &datestr = line_separated.at(datestrcol);
5388 if (thisdatestr.size() != 3) {
5393 if (thisdatestr.size() != 3) {
5394 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
5395 ". It should be in the format YYYY-MM-DD, delimited by either "
5403 std::vector<int> thisdate(3);
5404 for (
int i = 0; i < 3; i++) {
5405 if (!
parse_int(thisdatestr.at(i), thisdate.at(i))) {
5406 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
5407 ". It should be in the format YYYY-MM-DD, delimited by either "
5419 if (date_string_format ==
"YYYYMMDD") {
5420 year = thisdate.at(0);
5421 month = thisdate.at(1);
5422 day = thisdate.at(2);
5423 }
else if (date_string_format ==
"YYYYDDMM") {
5424 year = thisdate.at(0);
5425 month = thisdate.at(2);
5426 day = thisdate.at(1);
5427 }
else if (date_string_format ==
"DDMMYYYY") {
5428 year = thisdate.at(2);
5429 month = thisdate.at(1);
5430 day = thisdate.at(0);
5431 }
else if (date_string_format ==
"MMDDYYYY") {
5432 year = thisdate.at(2);
5433 month = thisdate.at(0);
5434 day = thisdate.at(1);
5436 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Invalid date string format in file " + data_file +
": " + date_string_format +
5445 ". Check that the date string does not include a delimiter (i.e., should be MMDDYYYY not MM/DD/YYYY).");
5448 if (year < 1000 || month < 1 || month > 12 || day < 1 || day > 31) {
5449 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse date string on line " + std::to_string(row) +
" of file " + data_file +
5450 ". It should be in the format YYYY-MM-DD, delimited by either "
5468 if (!
parse_int(line_separated.at(hourcol), hour)) {
5469 helios_runtime_error(
"ERROR (Context::loadTabularTimeseriesData): Could not parse hour string on line " + std::to_string(row) +
" of file " + data_file +
".");
5471 if (hour > 24 && minutecol < 0 && secondcol < 0) {
5473 hour = std::floor(hr_min / 100);
5474 minute = hr_min - hour * 100;
5480 if (minutecol >= 0) {
5481 if (!
parse_int(line_separated.at(minutecol), minute)) {
5483 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Could not parse minute string on line " << row <<
" of file " << data_file <<
". Setting minute equal to 0." << std::endl;
5486 if (secondcol >= 0) {
5487 if (!
parse_int(line_separated.at(secondcol), second)) {
5489 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Could not parse second string on line " << row <<
" of file " << data_file <<
". Setting second equal to 0." << std::endl;
5495 for (
auto &dat: datacols) {
5496 std::string label = dat.first;
5497 int col = dat.second;
5500 if (!
parse_float(line_separated.at(col), dataval)) {
5501 std::cout <<
"WARNING (Context::loadTabularTimeseriesData): Failed to parse data value as "
5504 << row <<
", column " << col + 1 <<
" of file " << data_file <<
". Skipping this value..." << std::endl;
5508 if (label ==
"air_humidity" && col_labels.size() == 1 && (col_labels.front() ==
"CIMIS" || col_labels.front() ==
"cimis")) {
5509 dataval = dataval / 100.f;