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));
170 void matmult(
const float ML[16],
const float MR[16],
float (&T)[16]);
178 void vecmult(
const float M[16],
const float v[3],
float (&result)[3]);
201 [[nodiscard]]
float deg2rad(
float deg);
210 [[nodiscard]]
float rad2deg(
float rad);
217 [[nodiscard]]
float atan2_2pi(
float y,
float x);
226 [[nodiscard]] SphericalCoord
cart2sphere(
const vec3 &Cartesian);
235 [[nodiscard]] vec3
sphere2cart(
const SphericalCoord &Spherical);
279 bool parse_float(
const std::string &input_string,
float &converted_float);
287 bool parse_double(
const std::string &input_string,
double &converted_double);
295 bool parse_int(
const std::string &input_string,
int &converted_int);
303 bool parse_int2(
const std::string &input_string, int2 &converted_int2);
311 bool parse_int3(
const std::string &input_string, int3 &converted_int3);
319 bool parse_uint(
const std::string &input_string,
uint &converted_uint);
327 bool parse_vec2(
const std::string &input_string, vec2 &converted_vec2);
335 bool parse_vec3(
const std::string &input_string, vec3 &converted_vec3);
343 bool parse_RGBcolor(
const std::string &input_string, RGBcolor &converted_rgb);
352 bool open_xml_file(
const std::string &xml_file, pugi::xml_document &xmldoc, std::string &error_string);
361 [[nodiscard]]
int parse_xml_tag_int(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
370 [[nodiscard]]
float parse_xml_tag_float(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
379 [[nodiscard]] vec2
parse_xml_tag_vec2(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
388 [[nodiscard]] vec3
parse_xml_tag_vec3(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
397 [[nodiscard]] std::string
parse_xml_tag_string(
const pugi::xml_node &node,
const std::string &tag,
const std::string &calling_function);
409 [[nodiscard]] std::string
deblank(
const char *input);
415 [[nodiscard]] std::string
deblank(
const std::string &input);
439 template<
typename anytype>
440 [[nodiscard]] anytype
clamp(anytype value, anytype
min, anytype
max) {
441 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>,
442 "helios::clamp() was called with an unsupported type.");
445 }
else if (value >
max) {
456 [[nodiscard]]
float sum(
const std::vector<float> &vect);
463 [[nodiscard]]
float mean(
const std::vector<float> &vect);
470 [[nodiscard]]
float min(
const std::vector<float> &vect);
477 [[nodiscard]]
int min(
const std::vector<int> &vect);
484 [[nodiscard]] vec3
min(
const std::vector<vec3> &vect);
491 [[nodiscard]]
float max(
const std::vector<float> &vect);
498 [[nodiscard]]
int max(
const std::vector<int> &vect);
505 [[nodiscard]] vec3
max(
const std::vector<vec3> &vect);
512 [[nodiscard]]
float stdev(
const std::vector<float> &vect);
519 [[nodiscard]]
float median(std::vector<float> vect);
528 template<
typename anytype>
529 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) {
530 vec.assign(Ny, std::vector<anytype>(Nx));
541 template<
typename anytype>
542 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) {
543 vec.assign(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx)));
555 template<
typename anytype>
556 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) {
557 vec.assign(Nw, std::vector<std::vector<std::vector<anytype>>>(Nz, std::vector<std::vector<anytype>>(Ny, std::vector<anytype>(Nx))));
566 [[nodiscard]] RGBcolor blend(
const RGBcolor &color0,
const RGBcolor &color1,
float weight);
574 [[nodiscard]] RGBAcolor blend(
const RGBAcolor &color0,
const RGBAcolor &color1,
float weight);
582 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
const SphericalCoord &rotation);
591 [[nodiscard]] vec3
rotatePoint(
const vec3 &position,
float theta,
float phi);
600 [[nodiscard]] vec3 rotatePointAboutLine(
const vec3 &point,
const vec3 &line_base,
const vec3 &line_direction,
float theta);
619 [[nodiscard]] Date CalendarDay(
int Julian_day,
int year);
629 [[nodiscard]]
int JulianDay(
int day,
int month,
int year);
638 [[nodiscard]]
int JulianDay(
const Date &date);
642 [[nodiscard]]
float randu();
648 [[nodiscard]]
int randu(
int imin,
int imax);
668 T
powi(T base, std::size_t exp) {
669 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.");
670 T result =
static_cast<T
>(1);
688 [[nodiscard]]
bool lineIntersection(
const vec2 &p1,
const vec2 &q1,
const vec2 &p2,
const vec2 &q2);
697 [[nodiscard]]
bool pointOnSegment(
const vec2 &point,
const vec2 &seg_start,
const vec2 &seg_end);
703 [[nodiscard]]
bool pointInPolygon(
const vec2 &point,
const std::vector<vec2> &polygon_verts);
717 timer_start = std::chrono::high_resolution_clock::now();
731 double toc(
const char *message)
const {
733 std::cerr <<
"ERROR (Timer): You must call `tic' before calling `toc'. Ignoring call to `toc'..." << std::endl;
737 auto timer_end = std::chrono::high_resolution_clock::now();
739 double duration = std::chrono::duration<double>(timer_end - timer_start).count();
740 if (strcmp(message,
"mute") != 0) {
741 std::cout <<
"Elapsed time is " << duration <<
" seconds: " << message << std::endl;
748 std::chrono::high_resolution_clock::time_point timer_start;
762 std::function<void(
float,
const std::string&)> callback;
772 ProgressBar(
size_t total,
int width = 50,
bool enable =
true,
const std::string &progress_message =
"Progress");
781 void update(
size_t step_number);
805 void setCallback(std::function<
void(
float,
const std::string&)> cb);
813 void wait(
float seconds);
819 [[nodiscard]]
bool PNGHasAlpha(
const char *filename);
826 [[nodiscard]] std::vector<std::vector<bool>>
readPNGAlpha(
const std::string &filename);
835 void readPNG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBAcolor> &pixel_data);
844 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBAcolor> &pixel_data);
854 void writePNG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
863 void readJPEG(
const std::string &filename,
uint &width,
uint &height, std::vector<helios::RGBcolor> &pixel_data);
878 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<helios::RGBcolor> &pixel_data);
888 void writeJPEG(
const std::string &filename,
uint width,
uint height,
const std::vector<unsigned char> &pixel_data);
913 void writeEXR(
const std::string &filename,
uint width,
uint height,
const std::vector<float> &pixel_data,
const std::string &channel_name =
"Y");
923 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);
931 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<T>> &vec) {
932 std::vector<T> result;
933 for (
const auto &row: vec) {
934 result.insert(result.end(), row.begin(), row.end());
945 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<std::vector<T>>> &vec) {
946 std::vector<T> result;
947 for (
const auto &matrix: vec) {
948 for (
const auto &row: matrix) {
949 result.insert(result.end(), row.begin(), row.end());
961 [[nodiscard]] std::vector<T>
flatten(
const std::vector<std::vector<std::vector<std::vector<T>>>> &vec) {
962 std::vector<T> result;
963 for (
const auto &tensor: vec) {
964 for (
const auto &matrix: tensor) {
965 for (
const auto &row: matrix) {
966 result.insert(result.end(), row.begin(), row.end());
983 [[nodiscard]]
helios::vec3 spline_interp3(
float u,
const vec3 &x_start,
const vec3 &tan_start,
const vec3 &x_end,
const vec3 &tan_end);
990 [[nodiscard]]
float XMLloadfloat(pugi::xml_node node,
const char *field);
997 [[nodiscard]]
int XMLloadint(pugi::xml_node node,
const char *field);
1004 [[nodiscard]] std::string XMLloadstring(pugi::xml_node node,
const char *field);
1011 [[nodiscard]]
helios::vec2 XMLloadvec2(pugi::xml_node node,
const char *field);
1018 [[nodiscard]]
helios::vec3 XMLloadvec3(pugi::xml_node node,
const char *field);
1025 [[nodiscard]]
helios::vec4 XMLloadvec4(pugi::xml_node node,
const char *field);
1032 [[nodiscard]]
helios::int2 XMLloadint2(pugi::xml_node node,
const char *field);
1039 [[nodiscard]]
helios::int3 XMLloadint3(pugi::xml_node node,
const char *field);
1046 [[nodiscard]]
helios::int4 XMLloadint4(pugi::xml_node node,
const char *field);
1053 [[nodiscard]]
helios::RGBcolor XMLloadrgb(pugi::xml_node node,
const char *field);
1060 [[nodiscard]]
helios::RGBAcolor XMLloadrgba(pugi::xml_node node,
const char *field);
1063 class WarningAggregator;
1075 [[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,
1089 [[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,
1090 int max_iterations = 100);
1098 [[nodiscard]]
float interp1(
const std::vector<helios::vec2> &points,
float x);
1116 [[nodiscard]] std::vector<float>
linspace(
float start,
float end,
int num);
1126 [[nodiscard]] std::vector<vec2>
linspace(
const vec2 &start,
const vec2 &end,
int num);
1136 [[nodiscard]] std::vector<vec3>
linspace(
const vec3 &start,
const vec3 &end,
int num);
1146 [[nodiscard]] std::vector<vec4>
linspace(
const vec4 &start,
const vec4 &end,
int num);
1164 [[nodiscard]] std::string
getFileStem(
const std::string &filepath);
1173 [[nodiscard]] std::string
getFileName(
const std::string &filepath);
1183 [[nodiscard]] std::string
getFilePath(
const std::string &filepath,
bool trailingslash =
true);
1191 [[nodiscard]]
bool validateOutputPath(std::string &output_directory,
const std::vector<std::string> &allowable_file_extensions = {});
1199 [[nodiscard]]
bool isDirectoryPath(
const std::string &path);
1210 [[nodiscard]] std::filesystem::path
resolveAssetPath(
const std::string &relativePath);
1219 [[nodiscard]] std::filesystem::path
resolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1231 [[nodiscard]] std::filesystem::path
tryResolvePluginAsset(
const std::string &pluginName,
const std::string &assetPath);
1245 [[nodiscard]] std::filesystem::path
resolveFilePath(
const std::string &filename);
1264 [[nodiscard]] std::filesystem::path
resolveSpectraPath(
const std::string &spectraFile);
1281 [[nodiscard]] std::filesystem::path
findProjectRoot(
const std::filesystem::path &startPath = std::filesystem::current_path());
1291 [[nodiscard]] std::filesystem::path
resolveProjectFile(
const std::string &relativePath);
1298 [[nodiscard]] std::vector<float> importVectorFromFile(
const std::string &filepath);
1307 [[nodiscard]]
float sample_Beta_distribution(
float mu,
float nu, std::minstd_rand0 *generator);
1317 [[nodiscard]]
float sample_ellipsoidal_azimuth(
float e,
float phi0_degrees, std::minstd_rand0 *generator);
1319 inline std::vector<float> &operator+=(std::vector<float> &lhs,
const std::vector<float> &rhs) {
1321 if (lhs.size() != rhs.size()) {
1322 throw std::invalid_argument(
"Vector sizes must match for element-wise addition");
1326 for (
size_t i = 0; i < lhs.size(); ++i) {
1333 inline std::vector<float> operator+(
const std::vector<float> &vector1,
const std::vector<float> &vector2) {
1334 if (vector1.size() != vector2.size()) {
1335 throw std::invalid_argument(
"Vector sizes must match for element-wise addition");
1338 std::vector<float> result(vector1.size());
1339 for (std::size_t i = 0; i < vector1.size(); ++i) {
1340 result[i] = vector1[i] + vector2[i];
1352 inline std::vector<float> operator+(
const std::vector<float> &vec,
float scalar) {
1353 std::vector<float> result(vec.size());
1354 for (std::size_t i = 0; i < vec.size(); ++i) {
1355 result[i] = vec[i] + scalar;
1367 inline std::vector<float> operator+(
float scalar,
const std::vector<float> &vec) {
1368 return vec + scalar;
1378 inline std::vector<float>
operator-(
const std::vector<float> &vec,
float scalar) {
1379 std::vector<float> result(vec.size());
1380 for (std::size_t i = 0; i < vec.size(); ++i) {
1381 result[i] = vec[i] - scalar;
1393 inline std::vector<float>
operator-(
float scalar,
const std::vector<float> &vec) {
1394 std::vector<float> result(vec.size());
1395 for (std::size_t i = 0; i < vec.size(); ++i) {
1396 result[i] = scalar - vec[i];
1408 inline std::vector<float>
operator*(
const std::vector<float> &vec,
float scalar) {
1409 std::vector<float> result(vec.size());
1410 for (std::size_t i = 0; i < vec.size(); ++i) {
1411 result[i] = vec[i] * scalar;
1423 inline std::vector<float>
operator*(
float scalar,
const std::vector<float> &vec) {
1424 return vec * scalar;
1434 inline std::vector<float>
operator/(
const std::vector<float> &vec,
float scalar) {
1435 std::vector<float> result(vec.size());
1436 for (std::size_t i = 0; i < vec.size(); ++i) {
1437 result[i] = vec[i] / scalar;
1449 inline std::vector<float>
operator/(
float scalar,
const std::vector<float> &vec) {
1450 std::vector<float> result(vec.size());
1451 for (std::size_t i = 0; i < vec.size(); ++i) {
1452 result[i] = scalar / vec[i];
1464 std::vector<int> coords;
1465 bool operator==(
PixelUVKey const &o)
const noexcept {
1466 return coords == o.coords;
1476 size_t operator()(
PixelUVKey const &k)
const noexcept {
1477 uint64_t h = 146527;
1478 for (
int v: k.coords) {
1480 h ^= uint64_t(v) + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
1494 std::streambuf *old_buf;
1495 std::ostringstream captured_stream;
1498 old_buf = std::cerr.rdbuf(captured_stream.rdbuf());
1502 std::cerr.rdbuf(old_buf);
1507 return captured_stream.str();
1512 return !captured_stream.str().empty();
1517 captured_stream.str(
"");
1518 captured_stream.clear();
1523 return captured_stream.str().size();
1535 std::streambuf *old_buf;
1536 std::ostringstream captured_stream;
1539 old_buf = std::cout.rdbuf(captured_stream.rdbuf());
1543 std::cout.rdbuf(old_buf);
1548 return captured_stream.str();
1553 return !captured_stream.str().empty();
1558 captured_stream.str(
"");
1559 captured_stream.clear();
1564 return captured_stream.str().size();
1601 void addWarning(
const std::string &category,
const std::string &message);
1610 void report(std::ostream &stream = std::cerr,
bool compact =
false);
1617 [[nodiscard]]
size_t getCount(
const std::string &category)
const;
1636 mutable std::mutex mutex_;
1637 std::unordered_map<std::string, std::vector<std::string>> warnings_;
1638 std::unordered_map<std::string, size_t> counts_;
1639 bool enabled_ =
true;
1640 static constexpr size_t MAX_EXAMPLES = 100;