1.3.64
 
Loading...
Searching...
No Matches
global.h
Go to the documentation of this file.
1
16#ifndef HELIOS_GLOBAL
17#define HELIOS_GLOBAL
18
20#ifndef M_PI
21#define M_PI 3.14159265358979323846
22#endif
23
25constexpr float PI_F = 3.14159265358979323846f;
26
27#include <algorithm>
28#include <array>
29#include <cassert>
30#include <chrono>
31#include <cmath>
32#include <cstdio>
33#include <cstdlib>
34#include <cstring>
35#include <ctime>
36#include <exception>
37#include <filesystem>
38#include <fstream>
39#include <iomanip>
40#include <iostream>
41#include <map>
42#include <memory>
43#include <random>
44#include <set>
45#include <sstream>
46#include <stdexcept>
47#include <thread>
48#include <type_traits>
49#include <unordered_map>
50#include <vector>
51
52#ifdef USE_OPENMP
53#include <omp.h>
54#endif
55
57typedef unsigned int uint;
58
60template<typename To, typename From>
61constexpr To scast(From &&v) noexcept {
62 return static_cast<To>(std::forward<From>(v));
63}
64
65
66#include "helios_vector_types.h"
67
68// pugi XML parser
69#include "pugixml.hpp"
70
71// Standard library for file path resolution
72#include <filesystem>
73
74// Thread synchronization
75#include <mutex>
76
77// *** Groups *** //
78
80
85
90
94namespace helios {
95
97
100 void helios_runtime_error(const std::string &error_message);
101
102 //--------------------- HELPER FUNCTIONS -----------------------------------//
103
105
111 void makeRotationMatrix(float rotation, const char *axis, float (&transform)[16]);
112
114
120 void makeRotationMatrix(float rotation, const helios::vec3 &axis, float (&transform)[16]);
121
123
130 void makeRotationMatrix(float rotation, const helios::vec3 &origin, const helios::vec3 &axis, float (&transform)[16]);
131
133
138 void makeTranslationMatrix(const helios::vec3 &translation, float (&transform)[16]);
139
141
146 void makeScaleMatrix(const helios::vec3 &scale, float (&transform)[16]);
147
149
155 void makeScaleMatrix(const helios::vec3 &scale, const helios::vec3 &point, float (&transform)[16]);
156
158
168 void matmult(const float ML[16], const float MR[16], float (&T)[16]);
169
171
176 void vecmult(const float M[16], const float v[3], float (&result)[3]);
177
179
184 void vecmult(const float M[16], const helios::vec3 &v3, helios::vec3 &result);
185
187
190 void makeIdentityMatrix(float (&T)[16]);
191
193
199 [[nodiscard]] float deg2rad(float deg);
200
202
208 [[nodiscard]] float rad2deg(float rad);
209
211
215 [[nodiscard]] float atan2_2pi(float y, float x);
216
218
224 [[nodiscard]] SphericalCoord cart2sphere(const vec3 &Cartesian);
225
227
233 [[nodiscard]] vec3 sphere2cart(const SphericalCoord &Spherical);
234
236
239 [[nodiscard]] vec2 string2vec2(const char *str);
240
242
245 [[nodiscard]] vec3 string2vec3(const char *str);
246
248
251 [[nodiscard]] vec4 string2vec4(const char *str);
252
254
257 [[nodiscard]] int2 string2int2(const char *str);
258
260
263 [[nodiscard]] int3 string2int3(const char *str);
264
266
269 [[nodiscard]] int4 string2int4(const char *str);
270
272
277 bool parse_float(const std::string &input_string, float &converted_float);
278
280
285 bool parse_double(const std::string &input_string, double &converted_double);
286
288
293 bool parse_int(const std::string &input_string, int &converted_int);
294
296
301 bool parse_int2(const std::string &input_string, int2 &converted_int2);
302
304
309 bool parse_int3(const std::string &input_string, int3 &converted_int3);
310
312
317 bool parse_uint(const std::string &input_string, uint &converted_uint);
318
320
325 bool parse_vec2(const std::string &input_string, vec2 &converted_vec2);
326
328
333 bool parse_vec3(const std::string &input_string, vec3 &converted_vec3);
334
336
341 bool parse_RGBcolor(const std::string &input_string, RGBcolor &converted_rgb);
342
344
350 bool open_xml_file(const std::string &xml_file, pugi::xml_document &xmldoc, std::string &error_string);
351
353
359 [[nodiscard]] int parse_xml_tag_int(const pugi::xml_node &node, const std::string &tag, const std::string &calling_function);
360
362
368 [[nodiscard]] float parse_xml_tag_float(const pugi::xml_node &node, const std::string &tag, const std::string &calling_function);
369
371
377 [[nodiscard]] vec2 parse_xml_tag_vec2(const pugi::xml_node &node, const std::string &tag, const std::string &calling_function);
378
380
386 [[nodiscard]] vec3 parse_xml_tag_vec3(const pugi::xml_node &node, const std::string &tag, const std::string &calling_function);
387
389
395 [[nodiscard]] std::string parse_xml_tag_string(const pugi::xml_node &node, const std::string &tag, const std::string &calling_function);
396
398
401 [[nodiscard]] RGBAcolor string2RGBcolor(const char *str);
402
404
407 [[nodiscard]] std::string deblank(const char *input);
408
410
413 [[nodiscard]] std::string deblank(const std::string &input);
414
416
419 [[nodiscard]] std::string trim_whitespace(const std::string &input);
420
422
428 [[nodiscard]] std::vector<std::string> separate_string_by_delimiter(const std::string &inputstring, const std::string &delimiter);
429
431
437 template<typename anytype>
438 [[nodiscard]] anytype clamp(anytype value, anytype min, anytype max) {
439 static_assert(std::is_same_v<anytype, int> || std::is_same_v<anytype, uint> || std::is_same_v<anytype, float> || std::is_same_v<anytype, double> || std::is_same_v<anytype, char> || std::is_same_v<anytype, unsigned char>,
440 "helios::clamp() was called with an unsupported type.");
441 if (value < min) {
442 value = min;
443 } else if (value > max) {
444 value = max;
445 }
446 return value;
447 }
448
450
454 [[nodiscard]] float sum(const std::vector<float> &vect);
455
457
461 [[nodiscard]] float mean(const std::vector<float> &vect);
462
464
468 [[nodiscard]] float min(const std::vector<float> &vect);
469
471
475 [[nodiscard]] int min(const std::vector<int> &vect);
476
478
482 [[nodiscard]] vec3 min(const std::vector<vec3> &vect);
483
485
489 [[nodiscard]] float max(const std::vector<float> &vect);
490
492
496 [[nodiscard]] int max(const std::vector<int> &vect);
497
499
503 [[nodiscard]] vec3 max(const std::vector<vec3> &vect);
504
506
510 [[nodiscard]] float stdev(const std::vector<float> &vect);
511
513
517 [[nodiscard]] float median(std::vector<float> vect);
518
520
526 template<typename anytype>
527 typename std::enable_if<std::is_default_constructible<anytype>::value>::type resize_vector(std::vector<std::vector<anytype>> &vec, size_t Nx, size_t Ny) {
528 vec.assign(Ny, std::vector<anytype>(Nx));
529 }
530
532
539 template<typename anytype>
540 typename std::enable_if<std::is_default_constructible<anytype>::value>::type resize_vector(std::vector<std::vector<std::vector<anytype>>> &vec, size_t Nx, size_t Ny, size_t Nz) {
541 vec.assign(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx)));
542 }
543
545
553 template<typename anytype>
554 typename std::enable_if<std::is_default_constructible<anytype>::value>::type resize_vector(std::vector<std::vector<std::vector<std::vector<anytype>>>> &vec, size_t Nx, size_t Ny, size_t Nz, size_t Nw) {
555 vec.assign(Nw, std::vector<std::vector<std::vector<anytype>>>(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx))));
556 }
557
559
564 [[nodiscard]] RGBcolor blend(const RGBcolor &color0, const RGBcolor &color1, float weight);
565
567
572 [[nodiscard]] RGBAcolor blend(const RGBAcolor &color0, const RGBAcolor &color1, float weight);
573
575
580 [[nodiscard]] vec3 rotatePoint(const vec3 &position, const SphericalCoord &rotation);
581
583
589 [[nodiscard]] vec3 rotatePoint(const vec3 &position, float theta, float phi);
590
592
598 [[nodiscard]] vec3 rotatePointAboutLine(const vec3 &point, const vec3 &line_base, const vec3 &line_direction, float theta);
599
601
608 [[nodiscard]] float calculateTriangleArea(const vec3 &v0, const vec3 &v1, const vec3 &v2);
609
611
617 [[nodiscard]] Date CalendarDay(int Julian_day, int year);
618
620
627 [[nodiscard]] int JulianDay(int day, int month, int year);
628
630
636 [[nodiscard]] int JulianDay(const Date &date);
637
639
640 [[nodiscard]] float randu();
641
643
646 [[nodiscard]] int randu(int imin, int imax);
647
649
652 [[nodiscard]] float acos_safe(float x);
653
655
658 [[nodiscard]] float asin_safe(float x);
659
661
665 template<typename T>
666 T powi(T base, std::size_t exp) {
667 static_assert(std::is_same_v<T, uint> || std::is_same_v<T, int> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, char> || std::is_same_v<T, size_t>, "helios::powi() was called with an unsupported type.");
668 T result = static_cast<T>(1);
669 while (exp > 0) {
670 // If the low bit is set, multiply result by current base
671 if (exp & 1) {
672 result *= base;
673 }
674 // Square the base for the next bit
675 base *= base;
676 // Shift off the processed bit
677 exp >>= 1;
678 }
679 return result;
680 }
681
683
686 [[nodiscard]] bool lineIntersection(const vec2 &p1, const vec2 &q1, const vec2 &p2, const vec2 &q2);
687
689
695 [[nodiscard]] bool pointOnSegment(const vec2 &point, const vec2 &seg_start, const vec2 &seg_end);
696
698
701 [[nodiscard]] bool pointInPolygon(const vec2 &point, const std::vector<vec2> &polygon_verts);
702
704
707 struct Timer {
708 public:
709 Timer() {
710 running = false;
711 }
712
714 void tic() {
715 timer_start = std::chrono::high_resolution_clock::now();
716 running = true;
717 };
718
720 double toc() const {
721 return toc("");
722 }
723
725
729 double toc(const char *message) const {
730 if (!running) {
731 std::cerr << "ERROR (Timer): You must call `tic' before calling `toc'. Ignoring call to `toc'..." << std::endl;
732 return 0;
733 }
734
735 auto timer_end = std::chrono::high_resolution_clock::now();
736 ;
737 double duration = std::chrono::duration<double>(timer_end - timer_start).count();
738 if (strcmp(message, "mute") != 0) {
739 std::cout << "Elapsed time is " << duration << " seconds: " << message << std::endl;
740 }
741 return duration;
742 }
743
744 private:
745 bool running;
746 std::chrono::high_resolution_clock::time_point timer_start;
747 };
748
750
754 private:
755 size_t total_steps;
756 size_t current_step;
757 int bar_width;
758 bool enabled;
759 std::string message;
760
761 public:
763
769 ProgressBar(size_t total, int width = 50, bool enable = true, const std::string &progress_message = "Progress");
770
772 void update();
773
775
778 void update(size_t step_number);
779
781 void finish();
782
784
787 void setEnabled(bool enable);
788
790
793 [[nodiscard]] bool isEnabled() const;
794
797 };
798
800
804 void wait(float seconds);
805
807
810 [[nodiscard]] bool PNGHasAlpha(const char *filename);
811
813
817 [[nodiscard]] std::vector<std::vector<bool>> readPNGAlpha(const std::string &filename);
818
820
826 void readPNG(const std::string &filename, uint &width, uint &height, std::vector<helios::RGBAcolor> &pixel_data);
827
829
835 void writePNG(const std::string &filename, uint width, uint height, const std::vector<helios::RGBAcolor> &pixel_data);
836
838
845 void writePNG(const std::string &filename, uint width, uint height, const std::vector<unsigned char> &pixel_data);
846
848
854 void readJPEG(const std::string &filename, uint &width, uint &height, std::vector<helios::RGBcolor> &pixel_data);
855
857
860 [[nodiscard]] helios::int2 getImageResolutionJPEG(const std::string &filename);
861
863
869 void writeJPEG(const std::string &filename, uint width, uint height, const std::vector<helios::RGBcolor> &pixel_data);
870
872
879 void writeJPEG(const std::string &filename, uint width, uint height, const std::vector<unsigned char> &pixel_data);
880
882
886 template<typename T>
887 [[nodiscard]] std::vector<T> flatten(const std::vector<std::vector<T>> &vec) {
888 std::vector<T> result;
889 for (const auto &row: vec) {
890 result.insert(result.end(), row.begin(), row.end());
891 }
892 return result;
893 }
894
896
900 template<typename T>
901 [[nodiscard]] std::vector<T> flatten(const std::vector<std::vector<std::vector<T>>> &vec) {
902 std::vector<T> result;
903 for (const auto &matrix: vec) {
904 for (const auto &row: matrix) {
905 result.insert(result.end(), row.begin(), row.end());
906 }
907 }
908 return result;
909 }
910
912
916 template<typename T>
917 [[nodiscard]] std::vector<T> flatten(const std::vector<std::vector<std::vector<std::vector<T>>>> &vec) {
918 std::vector<T> result;
919 for (const auto &tensor: vec) {
920 for (const auto &matrix: tensor) {
921 for (const auto &row: matrix) {
922 result.insert(result.end(), row.begin(), row.end());
923 }
924 }
925 }
926 return result;
927 }
928
930
939 [[nodiscard]] helios::vec3 spline_interp3(float u, const vec3 &x_start, const vec3 &tan_start, const vec3 &x_end, const vec3 &tan_end);
940
942
946 [[nodiscard]] float XMLloadfloat(pugi::xml_node node, const char *field);
947
949
953 [[nodiscard]] int XMLloadint(pugi::xml_node node, const char *field);
954
956
960 [[nodiscard]] std::string XMLloadstring(pugi::xml_node node, const char *field);
961
963
967 [[nodiscard]] helios::vec2 XMLloadvec2(pugi::xml_node node, const char *field);
968
970
974 [[nodiscard]] helios::vec3 XMLloadvec3(pugi::xml_node node, const char *field);
975
977
981 [[nodiscard]] helios::vec4 XMLloadvec4(pugi::xml_node node, const char *field);
982
984
988 [[nodiscard]] helios::int2 XMLloadint2(pugi::xml_node node, const char *field);
989
991
995 [[nodiscard]] helios::int3 XMLloadint3(pugi::xml_node node, const char *field);
996
998
1002 [[nodiscard]] helios::int4 XMLloadint4(pugi::xml_node node, const char *field);
1003
1005
1009 [[nodiscard]] helios::RGBcolor XMLloadrgb(pugi::xml_node node, const char *field);
1010
1012
1016 [[nodiscard]] helios::RGBAcolor XMLloadrgba(pugi::xml_node node, const char *field);
1017
1018 // Forward declaration for fzero()
1019 class WarningAggregator;
1020
1022
1031 [[nodiscard]] float fzero(float (*function)(float value, std::vector<float> &variables, const void *parameters), std::vector<float> &variables, const void *parameters, float init_guess, float err_tol = 0.0001f, int max_iterations = 100,
1032 WarningAggregator *warnings = nullptr);
1033
1035
1045 [[nodiscard]] float fzero(float (*function)(float value, std::vector<float> &variables, const void *parameters), std::vector<float> &variables, const void *parameters, float init_guess, bool &converged, float err_tol = 0.0001f,
1046 int max_iterations = 100);
1047
1049
1054 [[nodiscard]] float interp1(const std::vector<helios::vec2> &points, float x);
1055
1057
1062 [[nodiscard]] float point_distance(const helios::vec3 &p1, const helios::vec3 &p2);
1063
1065
1072 [[nodiscard]] std::vector<float> linspace(float start, float end, int num);
1073
1075
1082 [[nodiscard]] std::vector<vec2> linspace(const vec2 &start, const vec2 &end, int num);
1083
1085
1092 [[nodiscard]] std::vector<vec3> linspace(const vec3 &start, const vec3 &end, int num);
1093
1095
1102 [[nodiscard]] std::vector<vec4> linspace(const vec4 &start, const vec4 &end, int num);
1103
1105
1111 [[nodiscard]] std::string getFileExtension(const std::string &filepath);
1112
1114
1120 [[nodiscard]] std::string getFileStem(const std::string &filepath);
1121
1123
1129 [[nodiscard]] std::string getFileName(const std::string &filepath);
1130
1132
1139 [[nodiscard]] std::string getFilePath(const std::string &filepath, bool trailingslash = true);
1140
1142
1147 [[nodiscard]] bool validateOutputPath(std::string &output_directory, const std::vector<std::string> &allowable_file_extensions = {});
1148
1150
1155 [[nodiscard]] bool isDirectoryPath(const std::string &path);
1156
1157 //--------------------- ASSET PATH RESOLUTION -----------------------------------//
1158
1160
1166 [[nodiscard]] std::filesystem::path resolveAssetPath(const std::string &relativePath);
1167
1169
1175 [[nodiscard]] std::filesystem::path resolvePluginAsset(const std::string &pluginName, const std::string &assetPath);
1176
1178
1187 [[nodiscard]] std::filesystem::path tryResolvePluginAsset(const std::string &pluginName, const std::string &assetPath);
1188
1190
1201 [[nodiscard]] std::filesystem::path resolveFilePath(const std::string &filename);
1202
1204
1212 [[nodiscard]] std::filesystem::path tryResolveFilePath(const std::string &filename);
1213
1215
1220 [[nodiscard]] std::filesystem::path resolveSpectraPath(const std::string &spectraFile);
1221
1223
1228 [[nodiscard]] bool validateAssetPath(const std::filesystem::path &assetPath);
1229
1231
1237 [[nodiscard]] std::filesystem::path findProjectRoot(const std::filesystem::path &startPath = std::filesystem::current_path());
1238
1240
1247 [[nodiscard]] std::filesystem::path resolveProjectFile(const std::string &relativePath);
1248
1250
1254 [[nodiscard]] std::vector<float> importVectorFromFile(const std::string &filepath);
1255
1257
1263 [[nodiscard]] float sample_Beta_distribution(float mu, float nu, std::minstd_rand0 *generator);
1264
1273 [[nodiscard]] float sample_ellipsoidal_azimuth(float e, float phi0_degrees, std::minstd_rand0 *generator);
1274
1275 inline std::vector<float> &operator+=(std::vector<float> &lhs, const std::vector<float> &rhs) {
1276 // Make sure vectors have the same size
1277 if (lhs.size() != rhs.size()) {
1278 throw std::invalid_argument("Vector sizes must match for element-wise addition");
1279 }
1280
1281 // Perform element-by-element addition
1282 for (size_t i = 0; i < lhs.size(); ++i) {
1283 lhs[i] += rhs[i];
1284 }
1285
1286 return lhs;
1287 }
1288
1289 inline std::vector<float> operator+(const std::vector<float> &vector1, const std::vector<float> &vector2) {
1290 if (vector1.size() != vector2.size()) {
1291 throw std::invalid_argument("Vector sizes must match for element-wise addition");
1292 }
1293
1294 std::vector<float> result(vector1.size());
1295 for (std::size_t i = 0; i < vector1.size(); ++i) {
1296 result[i] = vector1[i] + vector2[i];
1297 }
1298 return result;
1299 }
1300
1302
1308 inline std::vector<float> operator+(const std::vector<float> &vec, float scalar) {
1309 std::vector<float> result(vec.size());
1310 for (std::size_t i = 0; i < vec.size(); ++i) {
1311 result[i] = vec[i] + scalar;
1312 }
1313 return result;
1314 }
1315
1317
1323 inline std::vector<float> operator+(float scalar, const std::vector<float> &vec) {
1324 return vec + scalar;
1325 }
1326
1328
1334 inline std::vector<float> operator-(const std::vector<float> &vec, float scalar) {
1335 std::vector<float> result(vec.size());
1336 for (std::size_t i = 0; i < vec.size(); ++i) {
1337 result[i] = vec[i] - scalar;
1338 }
1339 return result;
1340 }
1341
1343
1349 inline std::vector<float> operator-(float scalar, const std::vector<float> &vec) {
1350 std::vector<float> result(vec.size());
1351 for (std::size_t i = 0; i < vec.size(); ++i) {
1352 result[i] = scalar - vec[i];
1353 }
1354 return result;
1355 }
1356
1358
1364 inline std::vector<float> operator*(const std::vector<float> &vec, float scalar) {
1365 std::vector<float> result(vec.size());
1366 for (std::size_t i = 0; i < vec.size(); ++i) {
1367 result[i] = vec[i] * scalar;
1368 }
1369 return result;
1370 }
1371
1373
1379 inline std::vector<float> operator*(float scalar, const std::vector<float> &vec) {
1380 return vec * scalar;
1381 }
1382
1384
1390 inline std::vector<float> operator/(const std::vector<float> &vec, float scalar) {
1391 std::vector<float> result(vec.size());
1392 for (std::size_t i = 0; i < vec.size(); ++i) {
1393 result[i] = vec[i] / scalar;
1394 }
1395 return result;
1396 }
1397
1399
1405 inline std::vector<float> operator/(float scalar, const std::vector<float> &vec) {
1406 std::vector<float> result(vec.size());
1407 for (std::size_t i = 0; i < vec.size(); ++i) {
1408 result[i] = scalar / vec[i];
1409 }
1410 return result;
1411 }
1412
1419 struct PixelUVKey {
1420 std::vector<int> coords; // {x0,y0, x1,y1, …}
1421 bool operator==(PixelUVKey const &o) const noexcept {
1422 return coords == o.coords;
1423 }
1424 };
1425
1432 size_t operator()(PixelUVKey const &k) const noexcept {
1433 uint64_t h = 146527; // arbitrary seed
1434 for (int v: k.coords) {
1435 // mix in v
1436 h ^= uint64_t(v) + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
1437 }
1438 return size_t(h);
1439 }
1440 };
1441
1443
1450 std::streambuf *old_buf;
1451 std::ostringstream captured_stream;
1452
1453 capture_cerr() {
1454 old_buf = std::cerr.rdbuf(captured_stream.rdbuf());
1455 }
1456
1457 ~capture_cerr() {
1458 std::cerr.rdbuf(old_buf);
1459 }
1460
1462 std::string get_captured_output() const {
1463 return captured_stream.str();
1464 }
1465
1467 bool has_output() const {
1468 return !captured_stream.str().empty();
1469 }
1470
1472 void clear() {
1473 captured_stream.str("");
1474 captured_stream.clear();
1475 }
1476
1478 size_t size() const {
1479 return captured_stream.str().size();
1480 }
1481 };
1482
1484
1491 std::streambuf *old_buf;
1492 std::ostringstream captured_stream;
1493
1494 capture_cout() {
1495 old_buf = std::cout.rdbuf(captured_stream.rdbuf());
1496 }
1497
1498 ~capture_cout() {
1499 std::cout.rdbuf(old_buf);
1500 }
1501
1503 std::string get_captured_output() const {
1504 return captured_stream.str();
1505 }
1506
1508 bool has_output() const {
1509 return !captured_stream.str().empty();
1510 }
1511
1513 void clear() {
1514 captured_stream.str("");
1515 captured_stream.clear();
1516 }
1517
1519 size_t size() const {
1520 return captured_stream.str().size();
1521 }
1522 };
1523
1524
1526
1547 public:
1550
1552
1557 void addWarning(const std::string &category, const std::string &message);
1558
1560
1566 void report(std::ostream &stream = std::cerr, bool compact = false);
1567
1569
1573 [[nodiscard]] size_t getCount(const std::string &category) const;
1574
1576 void clear();
1577
1579
1583 void setEnabled(bool enabled);
1584
1586
1589 [[nodiscard]] bool isEnabled() const;
1590
1591 private:
1592 mutable std::mutex mutex_;
1593 std::unordered_map<std::string, std::vector<std::string>> warnings_;
1594 std::unordered_map<std::string, size_t> counts_;
1595 bool enabled_ = true;
1596 static constexpr size_t MAX_EXAMPLES = 100;
1597 };
1598
1602 extern vec3 nullorigin;
1603} // namespace helios
1604
1605#endif