21#define M_PI 3.14159265358979323846
25constexpr float PI_F = 3.14159265358979323846f;
50#include <unordered_map>
61template<
typename To,
typename From>
62constexpr To
scast(From &&v)
noexcept {
63 return static_cast<To
>(std::forward<From>(v));
169 void matmult(
const float ML[16],
const float MR[16],
float (&T)[16]);
177 void vecmult(
const float M[16],
const float v[3],
float (&result)[3]);
200 [[nodiscard]]
float deg2rad(
float deg);
209 [[nodiscard]]
float rad2deg(
float rad);
216 [[nodiscard]]
float atan2_2pi(
float y,
float x);
225 [[nodiscard]] SphericalCoord
cart2sphere(
const vec3 &Cartesian);
234 [[nodiscard]] vec3
sphere2cart(
const SphericalCoord &Spherical);
278 bool parse_float(
const std::string &input_string,
float &converted_float);
286 bool parse_double(
const std::string &input_string,
double &converted_double);
294 bool parse_int(
const std::string &input_string,
int &converted_int);
302 bool parse_int2(
const std::string &input_string, int2 &converted_int2);
310 bool parse_int3(
const std::string &input_string, int3 &converted_int3);
318 bool parse_uint(
const std::string &input_string,
uint &converted_uint);
326 bool parse_vec2(
const std::string &input_string, vec2 &converted_vec2);
334 bool parse_vec3(
const std::string &input_string, vec3 &converted_vec3);
342 bool parse_RGBcolor(
const std::string &input_string, RGBcolor &converted_rgb);
351 bool open_xml_file(
const std::string &xml_file, pugi::xml_document &xmldoc, std::string &error_string);
360 [[nodiscard]]
int parse_xml_tag_int(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
369 [[nodiscard]]
float parse_xml_tag_float(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
378 [[nodiscard]] vec2
parse_xml_tag_vec2(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
387 [[nodiscard]] vec3
parse_xml_tag_vec3(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
396 [[nodiscard]] std::string
parse_xml_tag_string(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
408 [[nodiscard]] std::string
deblank(
const char *input);
414 [[nodiscard]] std::string
deblank(
const std::string &input);
438 template<
typename anytype>
439 [[nodiscard]] anytype
clamp(anytype value, anytype
min, anytype
max) {
440 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>,
441 "helios::clamp() was called with an unsupported type.");
444 }
else if (value >
max) {
455 [[nodiscard]]
float sum(
const std::vector<float> &vect);
462 [[nodiscard]]
float mean(
const std::vector<float> &vect);
469 [[nodiscard]]
float min(
const std::vector<float> &vect);
476 [[nodiscard]]
int min(
const std::vector<int> &vect);
483 [[nodiscard]] vec3
min(
const std::vector<vec3> &vect);
490 [[nodiscard]]
float max(
const std::vector<float> &vect);
497 [[nodiscard]]
int max(
const std::vector<int> &vect);
504 [[nodiscard]] vec3
max(
const std::vector<vec3> &vect);
511 [[nodiscard]]
float stdev(
const std::vector<float> &vect);
518 [[nodiscard]]
float median(std::vector<float> vect);
527 template<
typename anytype>
528 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) {
529 vec.assign(Ny, std::vector<anytype>(Nx));
540 template<
typename anytype>
541 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) {
542 vec.assign(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx)));
554 template<
typename anytype>
555 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) {
556 vec.assign(Nw, std::vector<std::vector<std::vector<anytype>>>(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx))));
565 [[nodiscard]] RGBcolor blend(
const RGBcolor &color0,
const RGBcolor &color1,
float weight);
573 [[nodiscard]] RGBAcolor blend(
const RGBAcolor &color0,
const RGBAcolor &color1,
float weight);
581 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
const SphericalCoord &rotation);
590 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
float theta,
float phi);
599 [[nodiscard]] vec3 rotatePointAboutLine(
const vec3 &point,
const vec3 &line_base,
const vec3 &line_direction,
float theta);
618 [[nodiscard]] Date CalendarDay(
int Julian_day,
int year);
628 [[nodiscard]]
int JulianDay(
int day,
int month,
int year);
637 [[nodiscard]]
int JulianDay(
const Date &date);
641 [[nodiscard]]
float randu();
647 [[nodiscard]]
int randu(
int imin,
int imax);
667 T
powi(T base, std::size_t exp) {
668 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.");
669 T result =
static_cast<T
>(1);
687 [[nodiscard]]
bool lineIntersection(
const vec2 &p1,
const vec2 &q1,
const vec2 &p2,
const vec2 &q2);
696 [[nodiscard]]
bool pointOnSegment(
const vec2 &point,
const vec2 &seg_start,
const vec2 &seg_end);
702 [[nodiscard]]
bool pointInPolygon(
const vec2 &point,
const std::vector<vec2> &polygon_verts);
716 timer_start = std::chrono::high_resolution_clock::now();
730 double toc(
const char *message)
const {
732 std::cerr <<
"ERROR (Timer): You must call `tic' before calling `toc'. Ignoring call to `toc'..." << std::endl;
736 auto timer_end = std::chrono::high_resolution_clock::now();
738 double duration = std::chrono::duration<double>(timer_end - timer_start).count();
739 if (strcmp(message,
"mute") != 0) {
740 std::cout <<
"Elapsed time is " << duration <<
" seconds: " << message << std::endl;
747 std::chrono::high_resolution_clock::time_point timer_start;
761 std::function<void(
float,
const std::string&)> callback;
771 ProgressBar(
size_t total,
int width = 50,
bool enable =
true,
const std::string &progress_message =
"Progress");
780 void update(
size_t step_number);
804 void setCallback(std::function<
void(
float,
const std::string&)> cb);
812 void wait(
float seconds);
818 [[nodiscard]]
bool PNGHasAlpha(
const char *filename);
825 [[nodiscard]] std::vector<std::vector<bool>>
readPNGAlpha(
const std::string &filename);
834 void readPNG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBAcolor> &pixel_data);
843 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBAcolor> &pixel_data);
853 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
862 void readJPEG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBcolor> &pixel_data);
877 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBcolor> &pixel_data);
887 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
897 void writeEXR(
const std::string &filename,
uint width,
uint height,
const std::vector<float> &pixel_data,
const std::string &channel_name =
"Y");
907 void writeEXR(
const std::string &filename,
uint width,
uint height,
const std::vector<std::vector<float>> &channel_data,
const std::vector<std::string> &channel_names);
915 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<T>> &vec) {
916 std::vector<T> result;
917 for (
const auto &row: vec) {
918 result.insert(result.end(), row.begin(), row.end());
929 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<std::vector<T>>> &vec) {
930 std::vector<T> result;
931 for (
const auto &matrix: vec) {
932 for (
const auto &row: matrix) {
933 result.insert(result.end(), row.begin(), row.end());
945 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<std::vector<std::vector<T>>>> &vec) {
946 std::vector<T> result;
947 for (
const auto &tensor: vec) {
948 for (
const auto &matrix: tensor) {
949 for (
const auto &row: matrix) {
950 result.insert(result.end(), row.begin(), row.end());
967 [[nodiscard]]
helios::vec3 spline_interp3(
float u,
const vec3 &x_start,
const vec3 &tan_start,
const vec3 &x_end,
const vec3 &tan_end);
974 [[nodiscard]]
float XMLloadfloat(pugi::xml_node node,
const char *field);
981 [[nodiscard]]
int XMLloadint(pugi::xml_node node,
const char *field);
988 [[nodiscard]] std::string XMLloadstring(pugi::xml_node node,
const char *field);
995 [[nodiscard]]
helios::vec2 XMLloadvec2(pugi::xml_node node,
const char *field);
1002 [[nodiscard]]
helios::vec3 XMLloadvec3(pugi::xml_node node,
const char *field);
1009 [[nodiscard]]
helios::vec4 XMLloadvec4(pugi::xml_node node,
const char *field);
1016 [[nodiscard]]
helios::int2 XMLloadint2(pugi::xml_node node,
const char *field);
1023 [[nodiscard]]
helios::int3 XMLloadint3(pugi::xml_node node,
const char *field);
1030 [[nodiscard]]
helios::int4 XMLloadint4(pugi::xml_node node,
const char *field);
1037 [[nodiscard]]
helios::RGBcolor XMLloadrgb(pugi::xml_node node,
const char *field);
1044 [[nodiscard]]
helios::RGBAcolor XMLloadrgba(pugi::xml_node node,
const char *field);
1047 class WarningAggregator;
1059 [[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,
1073 [[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,
1074 int max_iterations = 100);
1082 [[nodiscard]]
float interp1(
const std::vector<helios::vec2> &points,
float x);
1100 [[nodiscard]] std::vector<float>
linspace(
float start,
float end,
int num);
1110 [[nodiscard]] std::vector<vec2>
linspace(
const vec2 &start,
const vec2 &end,
int num);
1120 [[nodiscard]] std::vector<vec3>
linspace(
const vec3 &start,
const vec3 &end,
int num);
1130 [[nodiscard]] std::vector<vec4>
linspace(
const vec4 &start,
const vec4 &end,
int num);
1148 [[nodiscard]] std::string
getFileStem(
const std::string &filepath);
1157 [[nodiscard]] std::string
getFileName(
const std::string &filepath);
1167 [[nodiscard]] std::string
getFilePath(
const std::string &filepath,
bool trailingslash =
true);
1175 [[nodiscard]]
bool validateOutputPath(std::string &output_directory,
const std::vector<std::string> &allowable_file_extensions = {});
1183 [[nodiscard]]
bool isDirectoryPath(
const std::string &path);
1194 [[nodiscard]] std::filesystem::path
resolveAssetPath(
const std::string &relativePath);
1203 [[nodiscard]] std::filesystem::path
resolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1215 [[nodiscard]] std::filesystem::path
tryResolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1229 [[nodiscard]] std::filesystem::path
resolveFilePath(
const std::string &filename);
1248 [[nodiscard]] std::filesystem::path
resolveSpectraPath(
const std::string &spectraFile);
1265 [[nodiscard]] std::filesystem::path
findProjectRoot(
const std::filesystem::path &startPath = std::filesystem::current_path());
1275 [[nodiscard]] std::filesystem::path
resolveProjectFile(
const std::string &relativePath);
1282 [[nodiscard]] std::vector<float> importVectorFromFile(
const std::string &filepath);
1291 [[nodiscard]]
float sample_Beta_distribution(
float mu,
float nu, std::minstd_rand0 *generator);
1301 [[nodiscard]]
float sample_ellipsoidal_azimuth(
float e,
float phi0_degrees, std::minstd_rand0 *generator);
1303 inline std::vector<float> &operator+=(std::vector<float> &lhs,
const std::vector<float> &rhs) {
1305 if (lhs.size() != rhs.size()) {
1306 throw std::invalid_argument(
"Vector sizes must match for element-wise addition");
1310 for (
size_t i = 0; i < lhs.size(); ++i) {
1317 inline std::vector<float> operator+(
const std::vector<float> &vector1,
const std::vector<float> &vector2) {
1318 if (vector1.size() != vector2.size()) {
1319 throw std::invalid_argument(
"Vector sizes must match for element-wise addition");
1322 std::vector<float> result(vector1.size());
1323 for (std::size_t i = 0; i < vector1.size(); ++i) {
1324 result[i] = vector1[i] + vector2[i];
1336 inline std::vector<float> operator+(
const std::vector<float> &vec,
float scalar) {
1337 std::vector<float> result(vec.size());
1338 for (std::size_t i = 0; i < vec.size(); ++i) {
1339 result[i] = vec[i] + scalar;
1351 inline std::vector<float> operator+(
float scalar,
const std::vector<float> &vec) {
1352 return vec + scalar;
1362 inline std::vector<float>
operator-(
const std::vector<float> &vec,
float scalar) {
1363 std::vector<float> result(vec.size());
1364 for (std::size_t i = 0; i < vec.size(); ++i) {
1365 result[i] = vec[i] - scalar;
1377 inline std::vector<float>
operator-(
float scalar,
const std::vector<float> &vec) {
1378 std::vector<float> result(vec.size());
1379 for (std::size_t i = 0; i < vec.size(); ++i) {
1380 result[i] = scalar - vec[i];
1392 inline std::vector<float>
operator*(
const std::vector<float> &vec,
float scalar) {
1393 std::vector<float> result(vec.size());
1394 for (std::size_t i = 0; i < vec.size(); ++i) {
1395 result[i] = vec[i] * scalar;
1407 inline std::vector<float>
operator*(
float scalar,
const std::vector<float> &vec) {
1408 return vec * scalar;
1418 inline std::vector<float>
operator/(
const std::vector<float> &vec,
float scalar) {
1419 std::vector<float> result(vec.size());
1420 for (std::size_t i = 0; i < vec.size(); ++i) {
1421 result[i] = vec[i] / scalar;
1433 inline std::vector<float>
operator/(
float scalar,
const std::vector<float> &vec) {
1434 std::vector<float> result(vec.size());
1435 for (std::size_t i = 0; i < vec.size(); ++i) {
1436 result[i] = scalar / vec[i];
1448 std::vector<int> coords;
1449 bool operator==(
PixelUVKey const &o)
const noexcept {
1450 return coords == o.coords;
1460 size_t operator()(
PixelUVKey const &k)
const noexcept {
1461 uint64_t h = 146527;
1462 for (
int v: k.coords) {
1464 h ^= uint64_t(v) + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
1478 std::streambuf *old_buf;
1479 std::ostringstream captured_stream;
1482 old_buf = std::cerr.rdbuf(captured_stream.rdbuf());
1486 std::cerr.rdbuf(old_buf);
1491 return captured_stream.str();
1496 return !captured_stream.str().empty();
1501 captured_stream.str(
"");
1502 captured_stream.clear();
1507 return captured_stream.str().size();
1519 std::streambuf *old_buf;
1520 std::ostringstream captured_stream;
1523 old_buf = std::cout.rdbuf(captured_stream.rdbuf());
1527 std::cout.rdbuf(old_buf);
1532 return captured_stream.str();
1537 return !captured_stream.str().empty();
1542 captured_stream.str(
"");
1543 captured_stream.clear();
1548 return captured_stream.str().size();
1585 void addWarning(
const std::string &category,
const std::string &message);
1594 void report(std::ostream &stream = std::cerr,
bool compact =
false);
1601 [[nodiscard]]
size_t getCount(
const std::string &category)
const;
1620 mutable std::mutex mutex_;
1621 std::unordered_map<std::string, std::vector<std::string>> warnings_;
1622 std::unordered_map<std::string, size_t> counts_;
1623 bool enabled_ =
true;
1624 static constexpr size_t MAX_EXAMPLES = 100;