21#define M_PI 3.14159265358979323846
25constexpr float PI_F = 3.14159265358979323846f;
49#include <unordered_map>
60template<
typename To,
typename From>
61constexpr To
scast(From &&v)
noexcept {
62 return static_cast<To
>(std::forward<From>(v));
168 void matmult(
const float ML[16],
const float MR[16],
float (&T)[16]);
176 void vecmult(
const float M[16],
const float v[3],
float (&result)[3]);
199 [[nodiscard]]
float deg2rad(
float deg);
208 [[nodiscard]]
float rad2deg(
float rad);
215 [[nodiscard]]
float atan2_2pi(
float y,
float x);
224 [[nodiscard]] SphericalCoord
cart2sphere(
const vec3 &Cartesian);
233 [[nodiscard]] vec3
sphere2cart(
const SphericalCoord &Spherical);
277 bool parse_float(
const std::string &input_string,
float &converted_float);
285 bool parse_double(
const std::string &input_string,
double &converted_double);
293 bool parse_int(
const std::string &input_string,
int &converted_int);
301 bool parse_int2(
const std::string &input_string, int2 &converted_int2);
309 bool parse_int3(
const std::string &input_string, int3 &converted_int3);
317 bool parse_uint(
const std::string &input_string,
uint &converted_uint);
325 bool parse_vec2(
const std::string &input_string, vec2 &converted_vec2);
333 bool parse_vec3(
const std::string &input_string, vec3 &converted_vec3);
341 bool parse_RGBcolor(
const std::string &input_string, RGBcolor &converted_rgb);
350 bool open_xml_file(
const std::string &xml_file, pugi::xml_document &xmldoc, std::string &error_string);
359 [[nodiscard]]
int parse_xml_tag_int(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
368 [[nodiscard]]
float parse_xml_tag_float(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
377 [[nodiscard]] vec2
parse_xml_tag_vec2(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
386 [[nodiscard]] vec3
parse_xml_tag_vec3(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
395 [[nodiscard]] std::string
parse_xml_tag_string(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
407 [[nodiscard]] std::string
deblank(
const char *input);
413 [[nodiscard]] std::string
deblank(
const std::string &input);
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.");
443 }
else if (value >
max) {
454 [[nodiscard]]
float sum(
const std::vector<float> &vect);
461 [[nodiscard]]
float mean(
const std::vector<float> &vect);
468 [[nodiscard]]
float min(
const std::vector<float> &vect);
475 [[nodiscard]]
int min(
const std::vector<int> &vect);
482 [[nodiscard]] vec3
min(
const std::vector<vec3> &vect);
489 [[nodiscard]]
float max(
const std::vector<float> &vect);
496 [[nodiscard]]
int max(
const std::vector<int> &vect);
503 [[nodiscard]] vec3
max(
const std::vector<vec3> &vect);
510 [[nodiscard]]
float stdev(
const std::vector<float> &vect);
517 [[nodiscard]]
float median(std::vector<float> vect);
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));
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)));
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))));
564 [[nodiscard]] RGBcolor blend(
const RGBcolor &color0,
const RGBcolor &color1,
float weight);
572 [[nodiscard]] RGBAcolor blend(
const RGBAcolor &color0,
const RGBAcolor &color1,
float weight);
580 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
const SphericalCoord &rotation);
589 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
float theta,
float phi);
598 [[nodiscard]] vec3 rotatePointAboutLine(
const vec3 &point,
const vec3 &line_base,
const vec3 &line_direction,
float theta);
617 [[nodiscard]] Date CalendarDay(
int Julian_day,
int year);
627 [[nodiscard]]
int JulianDay(
int day,
int month,
int year);
636 [[nodiscard]]
int JulianDay(
const Date &date);
640 [[nodiscard]]
float randu();
646 [[nodiscard]]
int randu(
int imin,
int imax);
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);
686 [[nodiscard]]
bool lineIntersection(
const vec2 &p1,
const vec2 &q1,
const vec2 &p2,
const vec2 &q2);
695 [[nodiscard]]
bool pointOnSegment(
const vec2 &point,
const vec2 &seg_start,
const vec2 &seg_end);
701 [[nodiscard]]
bool pointInPolygon(
const vec2 &point,
const std::vector<vec2> &polygon_verts);
715 timer_start = std::chrono::high_resolution_clock::now();
729 double toc(
const char *message)
const {
731 std::cerr <<
"ERROR (Timer): You must call `tic' before calling `toc'. Ignoring call to `toc'..." << std::endl;
735 auto timer_end = std::chrono::high_resolution_clock::now();
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;
746 std::chrono::high_resolution_clock::time_point timer_start;
769 ProgressBar(
size_t total,
int width = 50,
bool enable =
true,
const std::string &progress_message =
"Progress");
778 void update(
size_t step_number);
804 void wait(
float seconds);
810 [[nodiscard]]
bool PNGHasAlpha(
const char *filename);
817 [[nodiscard]] std::vector<std::vector<bool>>
readPNGAlpha(
const std::string &filename);
826 void readPNG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBAcolor> &pixel_data);
835 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBAcolor> &pixel_data);
845 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
854 void readJPEG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBcolor> &pixel_data);
869 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBcolor> &pixel_data);
879 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
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());
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());
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());
939 [[nodiscard]]
helios::vec3 spline_interp3(
float u,
const vec3 &x_start,
const vec3 &tan_start,
const vec3 &x_end,
const vec3 &tan_end);
946 [[nodiscard]]
float XMLloadfloat(pugi::xml_node node,
const char *field);
953 [[nodiscard]]
int XMLloadint(pugi::xml_node node,
const char *field);
960 [[nodiscard]] std::string XMLloadstring(pugi::xml_node node,
const char *field);
967 [[nodiscard]]
helios::vec2 XMLloadvec2(pugi::xml_node node,
const char *field);
974 [[nodiscard]]
helios::vec3 XMLloadvec3(pugi::xml_node node,
const char *field);
981 [[nodiscard]]
helios::vec4 XMLloadvec4(pugi::xml_node node,
const char *field);
988 [[nodiscard]]
helios::int2 XMLloadint2(pugi::xml_node node,
const char *field);
995 [[nodiscard]]
helios::int3 XMLloadint3(pugi::xml_node node,
const char *field);
1002 [[nodiscard]]
helios::int4 XMLloadint4(pugi::xml_node node,
const char *field);
1009 [[nodiscard]]
helios::RGBcolor XMLloadrgb(pugi::xml_node node,
const char *field);
1016 [[nodiscard]]
helios::RGBAcolor XMLloadrgba(pugi::xml_node node,
const char *field);
1019 class WarningAggregator;
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,
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);
1054 [[nodiscard]]
float interp1(
const std::vector<helios::vec2> &points,
float x);
1072 [[nodiscard]] std::vector<float>
linspace(
float start,
float end,
int num);
1082 [[nodiscard]] std::vector<vec2>
linspace(
const vec2 &start,
const vec2 &end,
int num);
1092 [[nodiscard]] std::vector<vec3>
linspace(
const vec3 &start,
const vec3 &end,
int num);
1102 [[nodiscard]] std::vector<vec4>
linspace(
const vec4 &start,
const vec4 &end,
int num);
1120 [[nodiscard]] std::string
getFileStem(
const std::string &filepath);
1129 [[nodiscard]] std::string
getFileName(
const std::string &filepath);
1139 [[nodiscard]] std::string
getFilePath(
const std::string &filepath,
bool trailingslash =
true);
1147 [[nodiscard]]
bool validateOutputPath(std::string &output_directory,
const std::vector<std::string> &allowable_file_extensions = {});
1155 [[nodiscard]]
bool isDirectoryPath(
const std::string &path);
1166 [[nodiscard]] std::filesystem::path
resolveAssetPath(
const std::string &relativePath);
1175 [[nodiscard]] std::filesystem::path
resolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1187 [[nodiscard]] std::filesystem::path
tryResolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1201 [[nodiscard]] std::filesystem::path
resolveFilePath(
const std::string &filename);
1220 [[nodiscard]] std::filesystem::path
resolveSpectraPath(
const std::string &spectraFile);
1237 [[nodiscard]] std::filesystem::path
findProjectRoot(
const std::filesystem::path &startPath = std::filesystem::current_path());
1247 [[nodiscard]] std::filesystem::path
resolveProjectFile(
const std::string &relativePath);
1254 [[nodiscard]] std::vector<float> importVectorFromFile(
const std::string &filepath);
1263 [[nodiscard]]
float sample_Beta_distribution(
float mu,
float nu, std::minstd_rand0 *generator);
1273 [[nodiscard]]
float sample_ellipsoidal_azimuth(
float e,
float phi0_degrees, std::minstd_rand0 *generator);
1275 inline std::vector<float> &operator+=(std::vector<float> &lhs,
const std::vector<float> &rhs) {
1277 if (lhs.size() != rhs.size()) {
1278 throw std::invalid_argument(
"Vector sizes must match for element-wise addition");
1282 for (
size_t i = 0; i < lhs.size(); ++i) {
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");
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];
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;
1323 inline std::vector<float> operator+(
float scalar,
const std::vector<float> &vec) {
1324 return vec + scalar;
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;
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];
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;
1379 inline std::vector<float>
operator*(
float scalar,
const std::vector<float> &vec) {
1380 return vec * scalar;
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;
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];
1420 std::vector<int> coords;
1421 bool operator==(
PixelUVKey const &o)
const noexcept {
1422 return coords == o.coords;
1432 size_t operator()(
PixelUVKey const &k)
const noexcept {
1433 uint64_t h = 146527;
1434 for (
int v: k.coords) {
1436 h ^= uint64_t(v) + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
1450 std::streambuf *old_buf;
1451 std::ostringstream captured_stream;
1454 old_buf = std::cerr.rdbuf(captured_stream.rdbuf());
1458 std::cerr.rdbuf(old_buf);
1463 return captured_stream.str();
1468 return !captured_stream.str().empty();
1473 captured_stream.str(
"");
1474 captured_stream.clear();
1479 return captured_stream.str().size();
1491 std::streambuf *old_buf;
1492 std::ostringstream captured_stream;
1495 old_buf = std::cout.rdbuf(captured_stream.rdbuf());
1499 std::cout.rdbuf(old_buf);
1504 return captured_stream.str();
1509 return !captured_stream.str().empty();
1514 captured_stream.str(
"");
1515 captured_stream.clear();
1520 return captured_stream.str().size();
1557 void addWarning(
const std::string &category,
const std::string &message);
1566 void report(std::ostream &stream = std::cerr,
bool compact =
false);
1573 [[nodiscard]]
size_t getCount(
const std::string &category)
const;
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;