1.3.77
 
Loading...
Searching...
No Matches
Context.h
Go to the documentation of this file.
1
16#ifndef HELIOS_CONTEXT
17#define HELIOS_CONTEXT
18
19#include <set>
20#include <unordered_set>
21#include <utility>
22
23#include "global.h"
24
26
28namespace helios {
29
30 class Context; // forward declaration of Context class
31
41
71
73 class Texture {
74 public:
76 Texture() = default;
77
79
82 explicit Texture(const char *texture_file);
83
85 [[nodiscard]] std::string getTextureFile() const;
86
88 [[nodiscard]] int2 getImageResolution() const;
89
91 [[nodiscard]] bool hasTransparencyChannel() const;
92
94 [[nodiscard]] const std::vector<std::vector<bool>> *getTransparencyData() const;
95
97
101 [[nodiscard]] float getSolidFraction(const std::vector<vec2> &uvs);
102
103 private:
104 std::string filename;
105 bool hastransparencychannel{};
106 std::vector<std::vector<bool>> transparencydata;
107 int2 image_resolution;
108 float solidfraction{};
109 std::unordered_map<PixelUVKey, float, PixelUVKeyHash> solidFracCache;
110
117 [[nodiscard]] float computeSolidFraction(const std::vector<vec2> &uvs) const;
118 };
119
121
129 struct Material {
132
134 std::string label;
135
138
140 std::string texture_file;
141
143
145
147
150
152
154
156 std::map<std::string, HeliosDataType> material_data_types;
158 std::map<std::string, std::vector<int>> material_data_int;
160 std::map<std::string, std::vector<uint>> material_data_uint;
162 std::map<std::string, std::vector<float>> material_data_float;
164 std::map<std::string, std::vector<double>> material_data_double;
166 std::map<std::string, std::vector<vec2>> material_data_vec2;
168 std::map<std::string, std::vector<vec3>> material_data_vec3;
170 std::map<std::string, std::vector<vec4>> material_data_vec4;
172 std::map<std::string, std::vector<int2>> material_data_int2;
174 std::map<std::string, std::vector<int3>> material_data_int3;
176 std::map<std::string, std::vector<int4>> material_data_int4;
178 std::map<std::string, std::vector<std::string>> material_data_string;
180 std::map<std::string, std::vector<bool>> material_data_bool;
181
185
187 Material(uint ID, const std::string &lbl, const RGBAcolor &c, const std::string &tex, bool override, uint twosided = 1) :
188 materialID(ID), label(lbl), color(c), texture_file(tex), texture_color_overridden(override), twosided_flag(twosided), reference_count(0) {
189 }
190
191 //-------- Material Data Methods ---------- //
192
194
199 template<typename T>
200 void setMaterialData(const char *label, const T &data) {
201 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
202 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
203 "Material::setMaterialData() was called with an unsupported type.");
204
205 if constexpr (std::is_same_v<T, int>) {
206 material_data_int[label] = {data};
208 } else if constexpr (std::is_same_v<T, uint>) {
209 material_data_uint[label] = {data};
211 } else if constexpr (std::is_same_v<T, float>) {
212 material_data_float[label] = {data};
214 } else if constexpr (std::is_same_v<T, double>) {
215 material_data_double[label] = {data};
217 } else if constexpr (std::is_same_v<T, vec2>) {
218 material_data_vec2[label] = {data};
220 } else if constexpr (std::is_same_v<T, vec3>) {
221 material_data_vec3[label] = {data};
223 } else if constexpr (std::is_same_v<T, vec4>) {
224 material_data_vec4[label] = {data};
226 } else if constexpr (std::is_same_v<T, int2>) {
227 material_data_int2[label] = {data};
229 } else if constexpr (std::is_same_v<T, int3>) {
230 material_data_int3[label] = {data};
232 } else if constexpr (std::is_same_v<T, int4>) {
233 material_data_int4[label] = {data};
235 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
236 material_data_string[label] = {data};
238 }
239 }
240
242
247 template<typename T>
248 void setMaterialData(const char *label, const std::vector<T> &data) {
249 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
250 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
251 "Material::setMaterialData() was called with an unsupported type.");
252
253 if constexpr (std::is_same_v<T, int>) {
254 material_data_int[label] = data;
256 } else if constexpr (std::is_same_v<T, uint>) {
259 } else if constexpr (std::is_same_v<T, float>) {
262 } else if constexpr (std::is_same_v<T, double>) {
265 } else if constexpr (std::is_same_v<T, vec2>) {
268 } else if constexpr (std::is_same_v<T, vec3>) {
271 } else if constexpr (std::is_same_v<T, vec4>) {
274 } else if constexpr (std::is_same_v<T, int2>) {
277 } else if constexpr (std::is_same_v<T, int3>) {
280 } else if constexpr (std::is_same_v<T, int4>) {
283 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
286 }
287 }
288
290
295 template<typename T>
296 void getMaterialData(const char *label, T &data) const {
298 helios_runtime_error("ERROR (Material::getMaterialData): Material data " + std::string(label) + " does not exist for material " + std::to_string(materialID));
299 }
300 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
301 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
302 "Material::getMaterialData() was called with an unsupported type.");
303
305
306 if constexpr (std::is_same_v<T, int>) {
307 if (type == HELIOS_TYPE_INT) {
308 data = material_data_int.at(label).front();
309 } else {
310 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int.");
311 }
312 } else if constexpr (std::is_same_v<T, uint>) {
313 if (type == HELIOS_TYPE_UINT) {
314 data = material_data_uint.at(label).front();
315 } else {
316 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type uint, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type uint.");
317 }
318 } else if constexpr (std::is_same_v<T, float>) {
319 if (type == HELIOS_TYPE_FLOAT) {
320 data = material_data_float.at(label).front();
321 } else {
322 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type float, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type float.");
323 }
324 } else if constexpr (std::is_same_v<T, double>) {
325 if (type == HELIOS_TYPE_DOUBLE) {
326 data = material_data_double.at(label).front();
327 } else {
328 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type double, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type double.");
329 }
330 } else if constexpr (std::is_same_v<T, vec2>) {
331 if (type == HELIOS_TYPE_VEC2) {
332 data = material_data_vec2.at(label).front();
333 } else {
334 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec2, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec2.");
335 }
336 } else if constexpr (std::is_same_v<T, vec3>) {
337 if (type == HELIOS_TYPE_VEC3) {
338 data = material_data_vec3.at(label).front();
339 } else {
340 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec3, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec3.");
341 }
342 } else if constexpr (std::is_same_v<T, vec4>) {
343 if (type == HELIOS_TYPE_VEC4) {
344 data = material_data_vec4.at(label).front();
345 } else {
346 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec4, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec4.");
347 }
348 } else if constexpr (std::is_same_v<T, int2>) {
349 if (type == HELIOS_TYPE_INT2) {
350 data = material_data_int2.at(label).front();
351 } else {
352 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int2, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int2.");
353 }
354 } else if constexpr (std::is_same_v<T, int3>) {
355 if (type == HELIOS_TYPE_INT3) {
356 data = material_data_int3.at(label).front();
357 } else {
358 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int3, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int3.");
359 }
360 } else if constexpr (std::is_same_v<T, int4>) {
361 if (type == HELIOS_TYPE_INT4) {
362 data = material_data_int4.at(label).front();
363 } else {
364 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int4, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int4.");
365 }
366 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
367 if (type == HELIOS_TYPE_STRING) {
368 data = material_data_string.at(label).front();
369 } else {
370 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type string, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type string.");
371 }
372 }
373 }
374
376
381 template<typename T>
382 void getMaterialData(const char *label, std::vector<T> &data) const {
384 helios_runtime_error("ERROR (Material::getMaterialData): Material data " + std::string(label) + " does not exist for material " + std::to_string(materialID));
385 }
386 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
387 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
388 "Material::getMaterialData() was called with an unsupported type.");
389
391
392 if constexpr (std::is_same_v<T, int>) {
393 if (type == HELIOS_TYPE_INT) {
394 data = material_data_int.at(label);
395 } else {
396 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int.");
397 }
398 } else if constexpr (std::is_same_v<T, uint>) {
399 if (type == HELIOS_TYPE_UINT) {
400 data = material_data_uint.at(label);
401 } else {
402 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type uint, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type uint.");
403 }
404 } else if constexpr (std::is_same_v<T, float>) {
405 if (type == HELIOS_TYPE_FLOAT) {
406 data = material_data_float.at(label);
407 } else {
408 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type float, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type float.");
409 }
410 } else if constexpr (std::is_same_v<T, double>) {
411 if (type == HELIOS_TYPE_DOUBLE) {
412 data = material_data_double.at(label);
413 } else {
414 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type double, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type double.");
415 }
416 } else if constexpr (std::is_same_v<T, vec2>) {
417 if (type == HELIOS_TYPE_VEC2) {
418 data = material_data_vec2.at(label);
419 } else {
420 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec2, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec2.");
421 }
422 } else if constexpr (std::is_same_v<T, vec3>) {
423 if (type == HELIOS_TYPE_VEC3) {
424 data = material_data_vec3.at(label);
425 } else {
426 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec3, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec3.");
427 }
428 } else if constexpr (std::is_same_v<T, vec4>) {
429 if (type == HELIOS_TYPE_VEC4) {
430 data = material_data_vec4.at(label);
431 } else {
432 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type vec4, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type vec4.");
433 }
434 } else if constexpr (std::is_same_v<T, int2>) {
435 if (type == HELIOS_TYPE_INT2) {
436 data = material_data_int2.at(label);
437 } else {
438 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int2, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int2.");
439 }
440 } else if constexpr (std::is_same_v<T, int3>) {
441 if (type == HELIOS_TYPE_INT3) {
442 data = material_data_int3.at(label);
443 } else {
444 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int3, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int3.");
445 }
446 } else if constexpr (std::is_same_v<T, int4>) {
447 if (type == HELIOS_TYPE_INT4) {
448 data = material_data_int4.at(label);
449 } else {
450 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type int4, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type int4.");
451 }
452 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
453 if (type == HELIOS_TYPE_STRING) {
454 data = material_data_string.at(label);
455 } else {
456 helios_runtime_error("ERROR (Material::getMaterialData): Attempted to get data for type string, but data " + std::string(label) + " for material " + std::to_string(materialID) + " does not have type string.");
457 }
458 }
459 }
460
462
466 bool doesMaterialDataExist(const char *label) const {
467 return material_data_types.find(label) != material_data_types.end();
468 }
469
471
478 helios_runtime_error("ERROR (Material::getMaterialDataType): Material data " + std::string(label) + " does not exist for material " + std::to_string(materialID));
479 }
480 return material_data_types.at(label);
481 }
482
484
488 uint getMaterialDataSize(const char *label) const {
490 helios_runtime_error("ERROR (Material::getMaterialDataSize): Material data " + std::string(label) + " does not exist for material " + std::to_string(materialID));
491 }
493 if (type == HELIOS_TYPE_INT) {
494 return material_data_int.at(label).size();
495 } else if (type == HELIOS_TYPE_UINT) {
496 return material_data_uint.at(label).size();
497 } else if (type == HELIOS_TYPE_FLOAT) {
498 return material_data_float.at(label).size();
499 } else if (type == HELIOS_TYPE_DOUBLE) {
500 return material_data_double.at(label).size();
501 } else if (type == HELIOS_TYPE_VEC2) {
502 return material_data_vec2.at(label).size();
503 } else if (type == HELIOS_TYPE_VEC3) {
504 return material_data_vec3.at(label).size();
505 } else if (type == HELIOS_TYPE_VEC4) {
506 return material_data_vec4.at(label).size();
507 } else if (type == HELIOS_TYPE_INT2) {
508 return material_data_int2.at(label).size();
509 } else if (type == HELIOS_TYPE_INT3) {
510 return material_data_int3.at(label).size();
511 } else if (type == HELIOS_TYPE_INT4) {
512 return material_data_int4.at(label).size();
513 } else if (type == HELIOS_TYPE_STRING) {
514 return material_data_string.at(label).size();
515 }
516 return 0;
517 }
518
520
523 void clearMaterialData(const char *label) {
525 helios_runtime_error("ERROR (Material::clearMaterialData): Material data " + std::string(label) + " does not exist for material " + std::to_string(materialID));
526 }
528 if (type == HELIOS_TYPE_INT) {
530 } else if (type == HELIOS_TYPE_UINT) {
532 } else if (type == HELIOS_TYPE_FLOAT) {
534 } else if (type == HELIOS_TYPE_DOUBLE) {
536 } else if (type == HELIOS_TYPE_VEC2) {
538 } else if (type == HELIOS_TYPE_VEC3) {
540 } else if (type == HELIOS_TYPE_VEC4) {
542 } else if (type == HELIOS_TYPE_INT2) {
544 } else if (type == HELIOS_TYPE_INT3) {
546 } else if (type == HELIOS_TYPE_INT4) {
548 } else if (type == HELIOS_TYPE_STRING) {
550 }
552 }
553
555 std::vector<std::string> listMaterialData() const {
556 std::vector<std::string> data_labels;
557 data_labels.reserve(material_data_types.size());
558 for (const auto &data: material_data_types) {
559 data_labels.push_back(data.first);
560 }
561 return data_labels;
562 }
563 };
564
566 struct GlobalData {
567
569 std::vector<int> global_data_int;
571 std::vector<uint> global_data_uint;
573 std::vector<float> global_data_float;
575 std::vector<double> global_data_double;
577 std::vector<vec2> global_data_vec2;
579 std::vector<vec3> global_data_vec3;
581 std::vector<vec4> global_data_vec4;
583 std::vector<int2> global_data_int2;
585 std::vector<int3> global_data_int3;
587 std::vector<int4> global_data_int4;
589 std::vector<std::string> global_data_string;
591 std::vector<bool> global_data_bool;
592
594 size_t size;
598 uint64_t version = 0;
599 };
600
601 //--------------------- GEOMETRIC PRIMITIVES -----------------------------------//
602
603
604 //---------- COMPOUND OBJECTS ----------------//
605
625
627 public:
629
632 virtual ~CompoundObject() = 0;
633
635 [[nodiscard]] uint getObjectID() const;
636
638 [[nodiscard]] helios::ObjectType getObjectType() const;
639
641 [[nodiscard]] uint getPrimitiveCount() const;
642
644 [[nodiscard]] std::vector<uint> getPrimitiveUUIDs() const;
645
647 [[nodiscard]] bool doesObjectContainPrimitive(uint UUID);
648
650 [[nodiscard]] helios::vec3 getObjectCenter() const;
651
653 [[nodiscard]] float getArea() const;
654
656
659 void setColor(const helios::RGBcolor &color);
660
662
665 void setColor(const helios::RGBAcolor &color);
666
669
672 void useTextureColor();
673
675 [[nodiscard]] bool hasTexture() const;
676
678 [[nodiscard]] std::string getTextureFile() const;
679
681
684 void translate(const helios::vec3 &shift);
685
687
691 void rotate(float rotation_radians, const char *rotation_axis_xyz_string);
692
694
698 void rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector);
699
701
706 void rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector);
707
709
712 void scale(const helios::vec3 &scale);
713
715
719
721
725 void scaleAboutPoint(const helios::vec3 &scale, const helios::vec3 &point);
726
728
731 void getTransformationMatrix(float (&T)[16]) const;
732
734
737 void setTransformationMatrix(float (&T)[16]);
738
740
743 void setPrimitiveUUIDs(const std::vector<uint> &UUIDs);
744
746
749 void deleteChildPrimitive(uint UUID);
750
752
755 void deleteChildPrimitive(const std::vector<uint> &UUIDs);
756
758
761 [[nodiscard]] bool arePrimitivesComplete() const;
762
763 //-------- Object Data Methods ---------- //
764
766
771 template<typename T>
772 void setObjectData(const char *label, const T &data) {
773
774 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
775 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
776 "CompoundObject::setObjectData() was called with an unsupported type.");
777
778 if constexpr (std::is_same_v<T, int>) {
779 object_data_int[label] = {data};
780 object_data_types[label] = HELIOS_TYPE_INT;
781 } else if constexpr (std::is_same_v<T, uint>) {
782 object_data_uint[label] = {data};
783 object_data_types[label] = HELIOS_TYPE_UINT;
784 } else if constexpr (std::is_same_v<T, float>) {
785 object_data_float[label] = {data};
786 object_data_types[label] = HELIOS_TYPE_FLOAT;
787 } else if constexpr (std::is_same_v<T, double>) {
788 object_data_double[label] = {data};
789 object_data_types[label] = HELIOS_TYPE_DOUBLE;
790 } else if constexpr (std::is_same_v<T, vec2>) {
791 object_data_vec2[label] = {data};
792 object_data_types[label] = HELIOS_TYPE_VEC2;
793 } else if constexpr (std::is_same_v<T, vec3>) {
794 object_data_vec3[label] = {data};
795 object_data_types[label] = HELIOS_TYPE_VEC3;
796 } else if constexpr (std::is_same_v<T, vec4>) {
797 object_data_vec4[label] = {data};
798 object_data_types[label] = HELIOS_TYPE_VEC4;
799 } else if constexpr (std::is_same_v<T, int2>) {
800 object_data_int2[label] = {data};
801 object_data_types[label] = HELIOS_TYPE_INT2;
802 } else if constexpr (std::is_same_v<T, int3>) {
803 object_data_int3[label] = {data};
804 object_data_types[label] = HELIOS_TYPE_INT3;
805 } else if constexpr (std::is_same_v<T, int4>) {
806 object_data_int4[label] = {data};
807 object_data_types[label] = HELIOS_TYPE_INT4;
808 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
809 object_data_string[label] = {data};
810 object_data_types[label] = HELIOS_TYPE_STRING;
811 }
812 }
813
815
820 template<typename T>
821 void setObjectData(const char *label, const std::vector<T> &data) {
822
823 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
824 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
825 "CompoundObject::setObjectData() was called with an unsupported type.");
826
827 if constexpr (std::is_same_v<T, int>) {
828 object_data_int[label] = data;
829 object_data_types[label] = HELIOS_TYPE_INT;
830 } else if constexpr (std::is_same_v<T, uint>) {
831 object_data_uint[label] = data;
832 object_data_types[label] = HELIOS_TYPE_UINT;
833 } else if constexpr (std::is_same_v<T, float>) {
834 object_data_float[label] = data;
835 object_data_types[label] = HELIOS_TYPE_FLOAT;
836 } else if constexpr (std::is_same_v<T, double>) {
837 object_data_double[label] = data;
838 object_data_types[label] = HELIOS_TYPE_DOUBLE;
839 } else if constexpr (std::is_same_v<T, vec2>) {
840 object_data_vec2[label] = data;
841 object_data_types[label] = HELIOS_TYPE_VEC2;
842 } else if constexpr (std::is_same_v<T, vec3>) {
843 object_data_vec3[label] = data;
844 object_data_types[label] = HELIOS_TYPE_VEC3;
845 } else if constexpr (std::is_same_v<T, vec4>) {
846 object_data_vec4[label] = data;
847 object_data_types[label] = HELIOS_TYPE_VEC4;
848 } else if constexpr (std::is_same_v<T, int2>) {
849 object_data_int2[label] = data;
850 object_data_types[label] = HELIOS_TYPE_INT2;
851 } else if constexpr (std::is_same_v<T, int3>) {
852 object_data_int3[label] = data;
853 object_data_types[label] = HELIOS_TYPE_INT3;
854 } else if constexpr (std::is_same_v<T, int4>) {
855 object_data_int4[label] = data;
856 object_data_types[label] = HELIOS_TYPE_INT4;
857 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
858 object_data_string[label] = data;
859 object_data_types[label] = HELIOS_TYPE_STRING;
860 }
861 }
862
864
868 template<typename T>
869 void getObjectData(const char *label, T &data) const {
870#ifdef HELIOS_DEBUG
871 if (!doesObjectDataExist(label)) {
872 helios_runtime_error("ERROR (CompoundObject::getObjectData): Object data " + std::string(label) + " does not exist for primitive " + std::to_string(OID));
873 }
874#endif
875
876 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
877 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
878 "CompoundObject::getObjectData() was called with an unsupported type.");
879
880 HeliosDataType type = object_data_types.at(label);
881
882 if constexpr (std::is_same_v<T, int>) {
883 if (type == HELIOS_TYPE_INT) {
884 data = object_data_int.at(label).front();
885 } else {
886 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int.");
887 }
888 } else if constexpr (std::is_same_v<T, uint>) {
889 if (type == HELIOS_TYPE_UINT) {
890 data = object_data_uint.at(label).front();
891 } else {
892 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type uint, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type uint.");
893 }
894 } else if constexpr (std::is_same_v<T, float>) {
895 if (type == HELIOS_TYPE_FLOAT) {
896 data = object_data_float.at(label).front();
897 } else {
898 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type float, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type float.");
899 }
900 } else if constexpr (std::is_same_v<T, double>) {
901 if (type == HELIOS_TYPE_DOUBLE) {
902 data = object_data_double.at(label).front();
903 } else {
904 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type double, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type double.");
905 }
906 } else if constexpr (std::is_same_v<T, vec2>) {
907 if (type == HELIOS_TYPE_VEC2) {
908 data = object_data_vec2.at(label).front();
909 } else {
910 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec2, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec2.");
911 }
912 } else if constexpr (std::is_same_v<T, vec3>) {
913 if (type == HELIOS_TYPE_VEC3) {
914 data = object_data_vec3.at(label).front();
915 } else {
916 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec3, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec3.");
917 }
918 } else if constexpr (std::is_same_v<T, vec4>) {
919 if (type == HELIOS_TYPE_VEC4) {
920 data = object_data_vec4.at(label).front();
921 } else {
922 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec4, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec4.");
923 }
924 } else if constexpr (std::is_same_v<T, int2>) {
925 if (type == HELIOS_TYPE_INT2) {
926 data = object_data_int2.at(label).front();
927 } else {
928 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int2, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int2.");
929 }
930 } else if constexpr (std::is_same_v<T, int3>) {
931 if (type == HELIOS_TYPE_INT3) {
932 data = object_data_int3.at(label).front();
933 } else {
934 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int3, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int3.");
935 }
936 } else if constexpr (std::is_same_v<T, int4>) {
937 if (type == HELIOS_TYPE_INT4) {
938 data = object_data_int4.at(label).front();
939 } else {
940 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int4, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int4.");
941 }
942 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
943 if (type == HELIOS_TYPE_STRING) {
944 data = object_data_string.at(label).front();
945 } else {
946 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type string, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type string.");
947 }
948 }
949 }
950
952
957 template<typename T>
958 void getObjectData(const char *label, std::vector<T> &data) const {
959#ifdef HELIOS_DEBUG
960 if (!doesObjectDataExist(label)) {
961 helios_runtime_error("ERROR (CompoundObject::getObjectData): Object data " + std::string(label) + " does not exist for object " + std::to_string(OID));
962 }
963#endif
964
965 HeliosDataType type = object_data_types.at(label);
966
967 if constexpr (std::is_same_v<T, int>) {
968 if (type == HELIOS_TYPE_INT) {
969 data = object_data_int.at(label);
970 } else {
971 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int.");
972 }
973 } else if constexpr (std::is_same_v<T, uint>) {
974 if (type == HELIOS_TYPE_UINT) {
975 data = object_data_uint.at(label);
976 } else {
977 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type uint, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type uint.");
978 }
979 } else if constexpr (std::is_same_v<T, float>) {
980 if (type == HELIOS_TYPE_FLOAT) {
981 data = object_data_float.at(label);
982 } else {
983 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type float, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type float.");
984 }
985 } else if constexpr (std::is_same_v<T, double>) {
986 if (type == HELIOS_TYPE_DOUBLE) {
987 data = object_data_double.at(label);
988 } else {
989 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type double, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type double.");
990 }
991 } else if constexpr (std::is_same_v<T, vec2>) {
992 if (type == HELIOS_TYPE_VEC2) {
993 data = object_data_vec2.at(label);
994 } else {
995 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec2, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec2.");
996 }
997 } else if constexpr (std::is_same_v<T, vec3>) {
998 if (type == HELIOS_TYPE_VEC3) {
999 data = object_data_vec3.at(label);
1000 } else {
1001 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec3, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec3.");
1002 }
1003 } else if constexpr (std::is_same_v<T, vec4>) {
1004 if (type == HELIOS_TYPE_VEC4) {
1005 data = object_data_vec4.at(label);
1006 } else {
1007 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type vec4, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type vec4.");
1008 }
1009 } else if constexpr (std::is_same_v<T, int2>) {
1010 if (type == HELIOS_TYPE_INT2) {
1011 data = object_data_int2.at(label);
1012 } else {
1013 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int2, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int2.");
1014 }
1015 } else if constexpr (std::is_same_v<T, int3>) {
1016 if (type == HELIOS_TYPE_INT3) {
1017 data = object_data_int3.at(label);
1018 } else {
1019 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int3, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int3.");
1020 }
1021 } else if constexpr (std::is_same_v<T, int4>) {
1022 if (type == HELIOS_TYPE_INT4) {
1023 data = object_data_int4.at(label);
1024 } else {
1025 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type int4, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type int4.");
1026 }
1027 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
1028 if (type == HELIOS_TYPE_STRING) {
1029 data = object_data_string.at(label);
1030 } else {
1031 helios_runtime_error("ERROR (CompoundObject::getObjectData): Attempted to get data for type string, but data " + std::string(label) + " for object " + std::to_string(OID) + " does not have type string.");
1032 }
1033 }
1034 }
1035
1037
1042 HeliosDataType getObjectDataType(const char *label) const;
1043
1045
1049 uint getObjectDataSize(const char *label) const;
1050
1052
1056 bool doesObjectDataExist(const char *label) const;
1057
1059
1062 void clearObjectData(const char *label);
1063
1065 [[nodiscard]] std::vector<std::string> listObjectData() const;
1066
1067 protected:
1069 uint OID;
1070
1072 helios::ObjectType type;
1073
1075 std::vector<uint> UUIDs;
1076
1079
1081 std::string texturefile;
1082
1084 float transform[16];
1085
1087 helios::vec3 object_origin;
1088
1090 bool primitivesarecomplete = true;
1091
1092 std::map<std::string, HeliosDataType> object_data_types;
1093 std::map<std::string, std::vector<int>> object_data_int;
1094 std::map<std::string, std::vector<uint>> object_data_uint;
1095 std::map<std::string, std::vector<float>> object_data_float;
1096 std::map<std::string, std::vector<double>> object_data_double;
1097 std::map<std::string, std::vector<vec2>> object_data_vec2;
1098 std::map<std::string, std::vector<vec3>> object_data_vec3;
1099 std::map<std::string, std::vector<vec4>> object_data_vec4;
1100 std::map<std::string, std::vector<int2>> object_data_int2;
1101 std::map<std::string, std::vector<int3>> object_data_int3;
1102 std::map<std::string, std::vector<int4>> object_data_int4;
1103 std::map<std::string, std::vector<std::string>> object_data_string;
1104 std::map<std::string, std::vector<bool>> object_data_bool;
1105
1106 bool ishidden = false;
1107
1108 friend class Context;
1109 };
1110
1112 class Tile final : public CompoundObject {
1113 public:
1115 ~Tile() override = default;
1116
1118 [[nodiscard]] helios::vec2 getSize() const;
1119
1121 [[nodiscard]] helios::vec3 getCenter() const;
1122
1124 [[nodiscard]] helios::int2 getSubdivisionCount() const;
1125
1127
1131 void setSubdivisionCount(const helios::int2 &subdiv);
1132
1134 [[nodiscard]] std::vector<helios::vec3> getVertices() const;
1135
1137 [[nodiscard]] helios::vec3 getNormal() const;
1138
1140 [[nodiscard]] std::vector<helios::vec2> getTextureUV() const;
1141
1142 protected:
1144
1147 Tile(uint a_OID, const std::vector<uint> &a_UUIDs, const int2 &a_subdiv, const char *a_texturefile, helios::Context *a_context);
1148
1149 helios::int2 subdiv;
1150
1151 friend class CompoundObject;
1152 friend class Context;
1153 };
1154
1156 class Sphere final : public CompoundObject {
1157 public:
1159 ~Sphere() override = default;
1160
1162 [[nodiscard]] helios::vec3 getRadius() const;
1163
1165 [[nodiscard]] helios::vec3 getCenter() const;
1166
1168 [[nodiscard]] uint getSubdivisionCount() const;
1169
1171
1174 void setSubdivisionCount(uint subdiv);
1175
1177 [[nodiscard]] float getVolume() const;
1178
1179 protected:
1181
1184 Sphere(uint a_OID, const std::vector<uint> &a_UUIDs, uint a_subdiv, const char *a_texturefile, helios::Context *a_context);
1185
1186 uint subdiv;
1187
1188 friend class CompoundObject;
1189 friend class Context;
1190 };
1191
1193 class Tube final : public CompoundObject {
1194 public:
1196 ~Tube() override = default;
1197
1199 [[nodiscard]] std::vector<helios::vec3> getNodes() const;
1200
1202 [[nodiscard]] uint getNodeCount() const;
1203
1205 [[nodiscard]] std::vector<float> getNodeRadii() const;
1206
1208 [[nodiscard]] std::vector<helios::RGBcolor> getNodeColors() const;
1209
1211 [[nodiscard]] std::vector<std::vector<helios::vec3>> getTriangleVertices() const;
1212
1214 [[nodiscard]] uint getSubdivisionCount() const;
1215
1217 [[nodiscard]] float getLength() const;
1218
1220 [[nodiscard]] float getVolume() const;
1221
1223
1226 [[nodiscard]] float getSegmentVolume(uint segment_index) const;
1227
1229
1234 void appendTubeSegment(const helios::vec3 &node_position, float node_radius, const helios::RGBcolor &node_color);
1235
1237
1243 void appendTubeSegment(const helios::vec3 &node_position, float node_radius, const char *texturefile, const helios::vec2 &textureuv_ufrac);
1244
1246
1249 void scaleTubeGirth(float S);
1250
1252
1255 void setTubeRadii(const std::vector<float> &node_radii);
1256
1258
1261 void scaleTubeLength(float S);
1262
1264
1267 void setTubeNodes(const std::vector<helios::vec3> &node_xyz);
1268
1270
1273 void pruneTubeNodes(uint node_index);
1274
1275 protected:
1277
1280 Tube(uint a_OID, const std::vector<uint> &a_UUIDs, const std::vector<vec3> &a_nodes, const std::vector<float> &a_radius, const std::vector<helios::RGBcolor> &a_colors, const std::vector<std::vector<helios::vec3>> &a_triangle_vertices,
1281 uint a_subdiv, const char *a_texturefile, helios::Context *a_context);
1282
1283 std::vector<helios::vec3> nodes;
1284
1285 std::vector<std::vector<helios::vec3>> triangle_vertices; // first index is the tube segment ring, second index is vertex within segment ring, third index is the vertex within triangle
1286
1287 std::vector<float> radius;
1288
1289 std::vector<helios::RGBcolor> colors;
1290
1291 uint subdiv;
1292
1293 void updateTriangleVertices() const;
1294
1295 void recomputeCrossSections();
1296
1297 friend class CompoundObject;
1298 friend class Context;
1299 };
1300
1302 class Box final : public CompoundObject {
1303 public:
1305 ~Box() override = default;
1306
1308 [[nodiscard]] helios::vec3 getSize() const;
1309
1311 [[nodiscard]] helios::vec3 getCenter() const;
1312
1314 [[nodiscard]] helios::int3 getSubdivisionCount() const;
1315
1317
1320 void setSubdivisionCount(const helios::int3 &subdiv);
1321
1323 [[nodiscard]] float getVolume() const;
1324
1325 protected:
1327
1330 Box(uint a_OID, const std::vector<uint> &a_UUIDs, const int3 &a_subdiv, const char *a_texturefile, helios::Context *a_context);
1331
1332 helios::int3 subdiv;
1333
1334 friend class CompoundObject;
1335 friend class Context;
1336 };
1337
1339 class Disk final : public CompoundObject {
1340 public:
1342 ~Disk() override = default;
1343
1345 [[nodiscard]] helios::vec2 getSize() const;
1346
1348 [[nodiscard]] helios::vec3 getCenter() const;
1349
1351 [[nodiscard]] helios::int2 getSubdivisionCount() const;
1352
1354
1357 void setSubdivisionCount(const helios::int2 &subdiv);
1358
1359 protected:
1361
1364 Disk(uint a_OID, const std::vector<uint> &a_UUIDs, int2 a_subdiv, const char *a_texturefile, helios::Context *a_context);
1365
1366 int2 subdiv;
1367
1368 friend class CompoundObject;
1369 friend class Context;
1370 };
1371
1373 class Polymesh final : public CompoundObject {
1374 public:
1376 ~Polymesh() override = default;
1377
1379 [[nodiscard]] float getVolume() const;
1380
1381 protected:
1383
1386 Polymesh(uint a_OID, const std::vector<uint> &a_UUIDs, const char *a_texturefile, helios::Context *a_context);
1387
1388 friend class CompoundObject;
1389 friend class Context;
1390 };
1391
1393 class Cone final : public CompoundObject {
1394 public:
1396 ~Cone() override = default;
1397
1399
1402 [[nodiscard]] std::vector<helios::vec3> getNodeCoordinates() const;
1403
1405
1409 [[nodiscard]] helios::vec3 getNodeCoordinate(int node_index) const;
1410
1412
1415 [[nodiscard]] std::vector<float> getNodeRadii() const;
1416
1418
1422 [[nodiscard]] float getNodeRadius(int node_index) const;
1423
1425 [[nodiscard]] uint getSubdivisionCount() const;
1426
1428
1431 void setSubdivisionCount(uint subdiv);
1432
1434 [[nodiscard]] helios::vec3 getAxisUnitVector() const;
1435
1437 [[nodiscard]] float getLength() const;
1438
1440
1443 void scaleLength(float S);
1444
1446
1449 void scaleGirth(float S);
1450
1452 [[nodiscard]] float getVolume() const;
1453
1454 protected:
1456
1459 Cone(uint a_OID, const std::vector<uint> &a_UUIDs, const vec3 &a_node0, const vec3 &a_node1, float a_radius0, float a_radius1, uint a_subdiv, const char *a_texturefile, helios::Context *a_context);
1460
1461 std::vector<helios::vec3> nodes;
1462 std::vector<float> radii;
1463
1464 uint subdiv;
1465
1466 friend class CompoundObject;
1467 friend class Context;
1468 };
1469
1470
1472
1476 class Primitive {
1477 public:
1479
1482 virtual ~Primitive() = 0;
1483
1485
1488 [[nodiscard]] uint getUUID() const;
1489
1491
1494 [[nodiscard]] PrimitiveType getType() const;
1495
1497
1500 void setParentObjectID(uint objID);
1501
1503 [[nodiscard]] uint getParentObjectID() const;
1504
1506 [[nodiscard]] virtual float getArea() const = 0;
1507
1509 [[nodiscard]] virtual helios::vec3 getNormal() const = 0;
1510
1512
1515 void getTransformationMatrix(float (&T)[16]) const;
1516
1518
1521 void setTransformationMatrix(float (&T)[16]);
1522
1524 [[nodiscard]] virtual std::vector<helios::vec3> getVertices() const = 0;
1525
1527 [[nodiscard]] virtual helios::vec3 getCenter() const = 0;
1528
1530 [[nodiscard]] helios::RGBcolor getColor() const;
1531
1533 [[nodiscard]] helios::RGBcolor getColorRGB() const;
1534
1536 [[nodiscard]] helios::RGBAcolor getColorRGBA() const;
1537
1539
1542 void setColor(const helios::RGBcolor &color);
1543
1545
1548 void setColor(const helios::RGBAcolor &color);
1549
1551 [[nodiscard]] bool hasTexture() const;
1552
1554 [[nodiscard]] std::string getTextureFile() const;
1555
1557
1560 void setTextureFile(const char *texture);
1561
1563
1566 [[nodiscard]] std::vector<vec2> getTextureUV();
1567
1568
1570
1573 void setTextureUV(const std::vector<vec2> &uv);
1574
1576 void overrideTextureColor();
1577
1579 void useTextureColor();
1580
1582 [[nodiscard]] bool isTextureColorOverridden() const;
1583
1585
1588 [[nodiscard]] float getSolidFraction() const;
1589
1591
1594 void setSolidFraction(float solidFraction);
1595
1597
1600 void applyTransform(float (&T)[16]);
1601
1603
1606 void translate(const helios::vec3 &shift);
1607
1609
1613 virtual void rotate(float rotation_radians, const char *rotation_axis_xyz_string) = 0;
1614
1616
1620 virtual void rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) = 0;
1621
1623
1628 virtual void rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) = 0;
1629
1631
1634 void scale(const helios::vec3 &S);
1635
1637
1641 void scale(const helios::vec3 &S, const helios::vec3 &point);
1642
1643 //-------- Primitive Data Methods ---------- //
1644
1646
1651 template<typename T>
1652 void setPrimitiveData(const char *label, const T &data) {
1653 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
1654 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
1655 "Primitive::setPrimitiveData() was called with an unsupported type.");
1656
1657 if constexpr (std::is_same_v<T, int>) {
1658 primitive_data_int[label] = {data};
1659 primitive_data_types[label] = HELIOS_TYPE_INT;
1660 } else if constexpr (std::is_same_v<T, uint>) {
1661 primitive_data_uint[label] = {data};
1662 primitive_data_types[label] = HELIOS_TYPE_UINT;
1663 } else if constexpr (std::is_same_v<T, float>) {
1664 primitive_data_float[label] = {data};
1665 primitive_data_types[label] = HELIOS_TYPE_FLOAT;
1666 } else if constexpr (std::is_same_v<T, double>) {
1667 primitive_data_double[label] = {data};
1668 primitive_data_types[label] = HELIOS_TYPE_DOUBLE;
1669 } else if constexpr (std::is_same_v<T, vec2>) {
1670 primitive_data_vec2[label] = {data};
1671 primitive_data_types[label] = HELIOS_TYPE_VEC2;
1672 } else if constexpr (std::is_same_v<T, vec3>) {
1673 primitive_data_vec3[label] = {data};
1674 primitive_data_types[label] = HELIOS_TYPE_VEC3;
1675 } else if constexpr (std::is_same_v<T, vec4>) {
1676 primitive_data_vec4[label] = {data};
1677 primitive_data_types[label] = HELIOS_TYPE_VEC4;
1678 } else if constexpr (std::is_same_v<T, int2>) {
1679 primitive_data_int2[label] = {data};
1680 primitive_data_types[label] = HELIOS_TYPE_INT2;
1681 } else if constexpr (std::is_same_v<T, int3>) {
1682 primitive_data_int3[label] = {data};
1683 primitive_data_types[label] = HELIOS_TYPE_INT3;
1684 } else if constexpr (std::is_same_v<T, int4>) {
1685 primitive_data_int4[label] = {data};
1686 primitive_data_types[label] = HELIOS_TYPE_INT4;
1687 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
1688 primitive_data_string[label] = {data};
1689 primitive_data_types[label] = HELIOS_TYPE_STRING;
1690 }
1691 dirty_flag = true;
1692 }
1693
1695
1700 template<typename T>
1701 void setPrimitiveData(const char *label, const std::vector<T> &data) {
1702 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
1703 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
1704 "Primitive::setPrimitiveData() was called with an unsupported type.");
1705
1706 if constexpr (std::is_same_v<T, int>) {
1707 primitive_data_int[label] = data;
1708 primitive_data_types[label] = HELIOS_TYPE_INT;
1709 } else if constexpr (std::is_same_v<T, uint>) {
1710 primitive_data_uint[label] = data;
1711 primitive_data_types[label] = HELIOS_TYPE_UINT;
1712 } else if constexpr (std::is_same_v<T, float>) {
1713 primitive_data_float[label] = data;
1714 primitive_data_types[label] = HELIOS_TYPE_FLOAT;
1715 } else if constexpr (std::is_same_v<T, double>) {
1716 primitive_data_double[label] = data;
1717 primitive_data_types[label] = HELIOS_TYPE_DOUBLE;
1718 } else if constexpr (std::is_same_v<T, vec2>) {
1719 primitive_data_vec2[label] = data;
1720 primitive_data_types[label] = HELIOS_TYPE_VEC2;
1721 } else if constexpr (std::is_same_v<T, vec3>) {
1722 primitive_data_vec3[label] = data;
1723 primitive_data_types[label] = HELIOS_TYPE_VEC3;
1724 } else if constexpr (std::is_same_v<T, vec4>) {
1725 primitive_data_vec4[label] = data;
1726 primitive_data_types[label] = HELIOS_TYPE_VEC4;
1727 } else if constexpr (std::is_same_v<T, int2>) {
1728 primitive_data_int2[label] = data;
1729 primitive_data_types[label] = HELIOS_TYPE_INT2;
1730 } else if constexpr (std::is_same_v<T, int3>) {
1731 primitive_data_int3[label] = data;
1732 primitive_data_types[label] = HELIOS_TYPE_INT3;
1733 } else if constexpr (std::is_same_v<T, int4>) {
1734 primitive_data_int4[label] = data;
1735 primitive_data_types[label] = HELIOS_TYPE_INT4;
1736 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
1737 primitive_data_string[label] = data;
1738 primitive_data_types[label] = HELIOS_TYPE_STRING;
1739 }
1740 dirty_flag = true;
1741 }
1742
1744
1749 template<typename T>
1750 void getPrimitiveData(const char *label, T &data) const {
1751#ifdef HELIOS_DEBUG
1752 if (!doesPrimitiveDataExist(label)) {
1753 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Primitive data " + std::string(label) + " does not exist for primitive " + std::to_string(UUID));
1754 }
1755#endif
1756 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
1757 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
1758 "Primitive::getPrimitiveData() was called with an unsupported type.");
1759
1760 HeliosDataType type = primitive_data_types.at(label);
1761
1762 if constexpr (std::is_same_v<T, int>) {
1763 if (type == HELIOS_TYPE_INT) {
1764 data = primitive_data_int.at(label).front();
1765 } else {
1766 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int.");
1767 }
1768 } else if constexpr (std::is_same_v<T, uint>) {
1769 if (type == HELIOS_TYPE_UINT) {
1770 data = primitive_data_uint.at(label).front();
1771 } else {
1772 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type uint, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type uint.");
1773 }
1774 } else if constexpr (std::is_same_v<T, float>) {
1775 if (type == HELIOS_TYPE_FLOAT) {
1776 data = primitive_data_float.at(label).front();
1777 } else {
1778 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type float, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type float.");
1779 }
1780 } else if constexpr (std::is_same_v<T, double>) {
1781 if (type == HELIOS_TYPE_DOUBLE) {
1782 data = primitive_data_double.at(label).front();
1783 } else {
1784 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type double, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type double.");
1785 }
1786 } else if constexpr (std::is_same_v<T, vec2>) {
1787 if (type == HELIOS_TYPE_VEC2) {
1788 data = primitive_data_vec2.at(label).front();
1789 } else {
1790 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec2, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec2.");
1791 }
1792 } else if constexpr (std::is_same_v<T, vec3>) {
1793 if (type == HELIOS_TYPE_VEC3) {
1794 data = primitive_data_vec3.at(label).front();
1795 } else {
1796 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec3, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec3.");
1797 }
1798 } else if constexpr (std::is_same_v<T, vec4>) {
1799 if (type == HELIOS_TYPE_VEC4) {
1800 data = primitive_data_vec4.at(label).front();
1801 } else {
1802 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec4, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec4.");
1803 }
1804 } else if constexpr (std::is_same_v<T, int2>) {
1805 if (type == HELIOS_TYPE_INT2) {
1806 data = primitive_data_int2.at(label).front();
1807 } else {
1808 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int2, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int2.");
1809 }
1810 } else if constexpr (std::is_same_v<T, int3>) {
1811 if (type == HELIOS_TYPE_INT3) {
1812 data = primitive_data_int3.at(label).front();
1813 } else {
1814 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int3, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int3.");
1815 }
1816 } else if constexpr (std::is_same_v<T, int4>) {
1817 if (type == HELIOS_TYPE_INT4) {
1818 data = primitive_data_int4.at(label).front();
1819 } else {
1820 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int4, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int4.");
1821 }
1822 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
1823 if (type == HELIOS_TYPE_STRING) {
1824 data = primitive_data_string.at(label).front();
1825 } else {
1826 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type string, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type string.");
1827 }
1828 }
1829 }
1830
1832
1837 template<typename T>
1838 void getPrimitiveData(const char *label, std::vector<T> &data) const {
1839#ifdef HELIOS_DEBUG
1840 if (!doesPrimitiveDataExist(label)) {
1841 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Primitive data " + std::string(label) + " does not exist for primitive " + std::to_string(UUID));
1842 }
1843#endif
1844 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
1845 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
1846 "Primitive::getPrimitiveData() was called with an unsupported type.");
1847
1848 HeliosDataType type = primitive_data_types.at(label);
1849
1850 if constexpr (std::is_same_v<T, int>) {
1851 if (type == HELIOS_TYPE_INT) {
1852 data = primitive_data_int.at(label);
1853 } else {
1854 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int.");
1855 }
1856 } else if constexpr (std::is_same_v<T, uint>) {
1857 if (type == HELIOS_TYPE_UINT) {
1858 data = primitive_data_uint.at(label);
1859 } else {
1860 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type uint, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type uint.");
1861 }
1862 } else if constexpr (std::is_same_v<T, float>) {
1863 if (type == HELIOS_TYPE_FLOAT) {
1864 data = primitive_data_float.at(label);
1865 } else {
1866 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type float, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type float.");
1867 }
1868 } else if constexpr (std::is_same_v<T, double>) {
1869 if (type == HELIOS_TYPE_DOUBLE) {
1870 data = primitive_data_double.at(label);
1871 } else {
1872 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type double, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type double.");
1873 }
1874 } else if constexpr (std::is_same_v<T, vec2>) {
1875 if (type == HELIOS_TYPE_VEC2) {
1876 data = primitive_data_vec2.at(label);
1877 } else {
1878 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec2, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec2.");
1879 }
1880 } else if constexpr (std::is_same_v<T, vec3>) {
1881 if (type == HELIOS_TYPE_VEC3) {
1882 data = primitive_data_vec3.at(label);
1883 } else {
1884 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec3, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec3.");
1885 }
1886 } else if constexpr (std::is_same_v<T, vec4>) {
1887 if (type == HELIOS_TYPE_VEC4) {
1888 data = primitive_data_vec4.at(label);
1889 } else {
1890 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type vec4, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type vec4.");
1891 }
1892 } else if constexpr (std::is_same_v<T, int2>) {
1893 if (type == HELIOS_TYPE_INT2) {
1894 data = primitive_data_int2.at(label);
1895 } else {
1896 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int2, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int2.");
1897 }
1898 } else if constexpr (std::is_same_v<T, int3>) {
1899 if (type == HELIOS_TYPE_INT3) {
1900 data = primitive_data_int3.at(label);
1901 } else {
1902 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int3, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int3.");
1903 }
1904 } else if constexpr (std::is_same_v<T, int4>) {
1905 if (type == HELIOS_TYPE_INT4) {
1906 data = primitive_data_int4.at(label);
1907 } else {
1908 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type int4, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type int4.");
1909 }
1910 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
1911 if (type == HELIOS_TYPE_STRING) {
1912 data = primitive_data_string.at(label);
1913 } else {
1914 helios_runtime_error("ERROR (Primitive::getPrimitiveData): Attempted to get data for type string, but data " + std::string(label) + " for primitive " + std::to_string(UUID) + " does not have type string.");
1915 }
1916 }
1917 }
1918
1920
1925 HeliosDataType getPrimitiveDataType(const char *label) const;
1926
1928
1932 uint getPrimitiveDataSize(const char *label) const;
1933
1935
1939 bool doesPrimitiveDataExist(const char *label) const;
1940
1942
1945 void clearPrimitiveData(const char *label);
1946
1948 [[nodiscard]] std::vector<std::string> listPrimitiveData() const;
1949
1950 protected:
1952 uint UUID;
1953
1955 PrimitiveType prim_type;
1956
1958 uint parent_object_ID;
1959
1961
1962 uint materialID;
1963
1965 Context *context_ptr;
1966
1968 float transform[16];
1969
1971 std::vector<vec2> uv;
1972
1974 float solid_fraction;
1975
1977 bool dirty_flag;
1978
1979 std::map<std::string, HeliosDataType> primitive_data_types;
1980 std::map<std::string, std::vector<int>> primitive_data_int;
1981 std::map<std::string, std::vector<uint>> primitive_data_uint;
1982 std::map<std::string, std::vector<float>> primitive_data_float;
1983 std::map<std::string, std::vector<double>> primitive_data_double;
1984 std::map<std::string, std::vector<vec2>> primitive_data_vec2;
1985 std::map<std::string, std::vector<vec3>> primitive_data_vec3;
1986 std::map<std::string, std::vector<vec4>> primitive_data_vec4;
1987 std::map<std::string, std::vector<int2>> primitive_data_int2;
1988 std::map<std::string, std::vector<int3>> primitive_data_int3;
1989 std::map<std::string, std::vector<int4>> primitive_data_int4;
1990 std::map<std::string, std::vector<std::string>> primitive_data_string;
1991 std::map<std::string, std::vector<bool>> primitive_data_bool;
1992
1993 bool ishidden = false;
1994
1995 friend class Context;
1996 };
1997
1998
2000
2005 class Patch : public Primitive {
2006 public:
2008 ~Patch() override = default;
2009
2011
2014 [[nodiscard]] float getArea() const override;
2015
2017
2020 [[nodiscard]] helios::vec3 getNormal() const override;
2021
2023
2026 [[nodiscard]] std::vector<helios::vec3> getVertices() const override;
2027
2029
2032 [[nodiscard]] helios::vec2 getSize() const;
2033
2035
2038 [[nodiscard]] helios::vec3 getCenter() const override;
2039
2041
2045 void rotate(float rotation_radians, const char *rotation_axis_xyz_string) override;
2046
2048
2052 void rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) override;
2053
2055
2060 void rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) override;
2061
2062 protected:
2064
2070 Patch(const helios::RGBAcolor &color, uint parent_objID, uint UUID);
2071
2073
2080 Patch(const char *texturefile, float solid_fraction, uint parent_objID, uint UUID);
2081
2083
2091 Patch(const char *texturefile, const std::vector<helios::vec2> &uv, std::map<std::string, Texture> &textures, uint parent_objID, uint UUID);
2092
2093
2094 friend class Primitive;
2095 friend class Context;
2096 };
2097
2099
2104 class Triangle : public Primitive {
2105 public:
2107 ~Triangle() override = default;
2108
2110
2113 [[nodiscard]] float getArea() const override;
2114
2116
2119 [[nodiscard]] helios::vec3 getNormal() const override;
2120
2122
2125 [[nodiscard]] std::vector<helios::vec3> getVertices() const override;
2126
2128
2132 [[nodiscard]] helios::vec3 getVertex(int vertex_index) const;
2133
2135
2138 [[nodiscard]] helios::vec3 getCenter() const override;
2139
2141
2145 void rotate(float rotation_radians, const char *rotation_axis_xyz_string) override;
2146
2148
2152 void rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) override;
2153
2155
2160 void rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) override;
2161
2163
2168 void setVertices(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2);
2169
2170 private:
2172
2181 Triangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const helios::RGBAcolor &color, uint parent_objID, uint UUID);
2182
2184
2195 Triangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const char *texturefile, const std::vector<helios::vec2> &uv, float solid_fraction, uint parent_objID, uint UUID);
2196
2198
2209 Triangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const char *texturefile, const std::vector<helios::vec2> &uv, std::map<std::string, Texture> &textures, uint parent_objID, uint UUID);
2210
2211 void makeTransformationMatrix(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2);
2212
2213 bool edgeFunction(const helios::vec2 &a, const helios::vec2 &b, const helios::vec2 &c);
2214
2215 friend class Primitive;
2216 friend class Context;
2217 };
2218
2220
2223 class Voxel : public Primitive {
2224 public:
2226 ~Voxel() override = default;
2227
2229
2232 [[nodiscard]] float getArea() const override;
2233
2235 [[nodiscard]] helios::vec3 getNormal() const override;
2236
2238
2241 [[nodiscard]] std::vector<helios::vec3> getVertices() const override;
2242
2244
2247 float getVolume();
2248
2250
2253 [[nodiscard]] helios::vec3 getCenter() const override;
2254
2256
2259 [[nodiscard]] helios::vec3 getSize() const;
2260
2262
2266 void rotate(float rotation_radians, const char *rotation_axis_xyz_string) override;
2267
2269
2273 void rotate(float rotation_radians, const helios::vec3 &rotation_axis_vector) override;
2274
2275
2277
2282 void rotate(float rotation_radians, const helios::vec3 &origin, const helios::vec3 &rotation_axis_vector) override;
2283
2284 protected:
2286
2292 Voxel(const helios::RGBAcolor &color, uint parent_objID, uint UUID);
2293
2294 friend class Primitive;
2295 friend class Context;
2296 };
2297
2300 public:
2302
2307 static int parse_data_float(const pugi::xml_node &node_data, std::vector<float> &data);
2308
2310
2315 static int parse_data_double(const pugi::xml_node &node_data, std::vector<double> &data);
2316
2318
2323 static int parse_data_int(const pugi::xml_node &node_data, std::vector<int> &data);
2324
2326
2331 static int parse_data_uint(const pugi::xml_node &node_data, std::vector<uint> &data);
2332
2334
2339 static int parse_data_string(const pugi::xml_node &node_data, std::vector<std::string> &data);
2340
2342
2347 static int parse_data_vec2(const pugi::xml_node &node_data, std::vector<vec2> &data);
2348
2350
2355 static int parse_data_vec3(const pugi::xml_node &node_data, std::vector<vec3> &data);
2356
2358
2363 static int parse_data_vec4(const pugi::xml_node &node_data, std::vector<vec4> &data);
2364
2366
2371 static int parse_data_int2(const pugi::xml_node &node_data, std::vector<int2> &data);
2372
2374
2379 static int parse_data_int3(const pugi::xml_node &node_data, std::vector<int3> &data);
2380
2382
2387 static int parse_data_int4(const pugi::xml_node &node_data, std::vector<int4> &data);
2388
2390
2395 static int parse_objID(const pugi::xml_node &node_data, uint &objID);
2396
2398
2403 static int parse_transform(const pugi::xml_node &node_data, float (&transform)[16]);
2404
2406
2411 static int parse_texture(const pugi::xml_node &node_data, std::string &texture);
2412
2414
2419 static int parse_textureUV(const pugi::xml_node &node_data, std::vector<vec2> &uvs);
2420
2422
2427 static int parse_solid_fraction(const pugi::xml_node &node_data, float &solid_fraction);
2428
2430
2435 static int parse_vertices(const pugi::xml_node &node_data, std::vector<float> &vertices);
2436
2438
2443 static int parse_subdivisions(const pugi::xml_node &node_data, uint &subdivisions);
2444
2446
2451 static int parse_subdivisions(const pugi::xml_node &node_data, int2 &subdivisions);
2452
2454
2459 static int parse_subdivisions(const pugi::xml_node &node_data, int3 &subdivisions);
2460
2462
2467 static int parse_nodes(const pugi::xml_node &node_data, std::vector<vec3> &nodes);
2468
2470
2475 static int parse_radius(const pugi::xml_node &node_data, std::vector<float> &radius);
2476 };
2477
2479
2482 class Context {
2483 friend class Primitive; // Allow Primitive methods to access private material data
2484
2485 private:
2486 //---------- PRIMITIVE/OBJECT HELIOS::VECTORS ----------------//
2487
2489
2493 [[nodiscard]] Primitive *getPrimitivePointer_private(uint UUID) const;
2494
2496
2500 [[nodiscard]] Patch *getPatchPointer_private(uint UUID) const;
2501
2502
2504
2508 [[nodiscard]] Triangle *getTrianglePointer_private(uint UUID) const;
2509
2511
2515 [[nodiscard]] Voxel *getVoxelPointer_private(uint UUID) const;
2516
2518
2522 [[nodiscard]] CompoundObject *getObjectPointer_private(uint ObjID) const;
2523
2525
2529 [[nodiscard]] Tile *getTileObjectPointer_private(uint ObjID) const;
2530
2532
2544 void regenerateTileObjectSubpatches(uint ObjID, const int2 &new_subdiv);
2545
2547
2551 [[nodiscard]] Sphere *getSphereObjectPointer_private(uint ObjID) const;
2552
2554
2558 [[nodiscard]] Tube *getTubeObjectPointer_private(uint ObjID) const;
2559
2561
2565 [[nodiscard]] Box *getBoxObjectPointer_private(uint ObjID) const;
2566
2568
2572 [[nodiscard]] Disk *getDiskObjectPointer_private(uint ObjID) const;
2573
2575
2579 [[nodiscard]] Polymesh *getPolymeshObjectPointer_private(uint ObjID) const;
2580
2582
2586 [[nodiscard]] Cone *getConeObjectPointer_private(uint ObjID) const;
2587
2589 void invalidateAllUUIDsCache() const {
2590 all_uuids_cache_valid = false;
2591 }
2592
2594
2597 std::unordered_map<uint, Primitive *> primitives;
2598
2600 mutable std::vector<uint> cached_all_uuids;
2602 mutable bool all_uuids_cache_valid = false;
2603
2605 mutable WarningAggregator api_warnings;
2606
2608 std::vector<uint> dirty_deleted_primitives;
2609
2611 std::unordered_map<uint, CompoundObject *> objects;
2612
2614 std::map<std::string, std::vector<float>> timeseries_data;
2615
2617
2618 std::map<std::string, std::vector<double>> timeseries_datevalue;
2619
2620 //------------ TEXTURES ----------------//
2621
2622 std::map<std::string, Texture> textures;
2623
2624 void addTexture(const char *texture_file);
2625
2626
2627 bool doesTextureFileExist(const char *texture_file) const;
2628
2629 bool validateTextureFileExtenstion(const char *texture_file) const;
2630
2631 //------------ MATERIALS ----------------//
2632
2634 static constexpr const char *DEFAULT_MATERIAL_LABEL = "__default__";
2635
2637 std::map<uint, Material> materials;
2638
2640 std::map<std::string, uint> material_label_to_id;
2641
2643 uint currentMaterialID;
2644
2646
2653 uint addMaterial_internal(const std::string &label, const RGBAcolor &color, const std::string &texture = "");
2654
2656 std::string generateMaterialLabel(const RGBAcolor &color, const std::string &texture, bool texture_override) const;
2657
2659 bool isMaterialShared(uint materialID) const;
2660
2662 uint copyMaterialForPrimitive(uint primitiveUUID);
2663
2664 //----------- GLOBAL DATA -------------//
2665
2666 std::map<std::string, GlobalData> globaldata;
2667
2668 std::unordered_map<std::string, size_t> primitive_data_label_counts;
2669 std::unordered_map<std::string, size_t> object_data_label_counts;
2670
2671 //----------- DATA TYPE CONSISTENCY REGISTRIES -------------//
2672
2674 std::unordered_map<std::string, HeliosDataType> primitive_data_type_registry;
2675
2677 std::unordered_map<std::string, HeliosDataType> object_data_type_registry;
2678
2679 //----------- VALUE-LEVEL CACHING REGISTRIES -------------//
2680
2682 std::unordered_map<std::string, std::unordered_map<std::string, size_t>> primitive_string_value_registry;
2683 std::unordered_map<std::string, std::unordered_map<int, size_t>> primitive_int_value_registry;
2684 std::unordered_map<std::string, std::unordered_map<uint, size_t>> primitive_uint_value_registry;
2685
2687 std::unordered_map<std::string, std::unordered_map<std::string, size_t>> object_string_value_registry;
2688 std::unordered_map<std::string, std::unordered_map<int, size_t>> object_int_value_registry;
2689 std::unordered_map<std::string, std::unordered_map<uint, size_t>> object_uint_value_registry;
2690
2692 std::unordered_set<std::string> cached_primitive_data_labels;
2693 std::unordered_set<std::string> cached_object_data_labels;
2694
2696
2699 void incrementPrimitiveDataLabelCounter(const std::string &primitive_data_label);
2700
2702
2705 void decrementPrimitiveDataLabelCounter(const std::string &primitive_data_label);
2706
2707
2709
2712 void incrementObjectDataLabelCounter(const std::string &object_data_label);
2713
2715
2718 void decrementObjectDataLabelCounter(const std::string &object_data_label);
2719
2721 std::string dataTypeToString(HeliosDataType type) const;
2722
2724 bool isTypeCastingSupported(HeliosDataType from_type, HeliosDataType to_type) const;
2725
2726 //---------- CONTEXT PRIVATE MEMBER VARIABLES ---------//
2727
2729
2732 helios::Date sim_date;
2733
2735
2738 helios::Time sim_time;
2739
2741 helios::Location sim_location;
2742
2744 std::minstd_rand0 generator;
2745
2747 std::uniform_real_distribution<float> unif_distribution;
2748
2750 std::normal_distribution<float> norm_distribution;
2751
2752 //---------- CONTEXT I/O ---------//
2753
2754 std::vector<std::string> XMLfiles;
2755
2756 struct OBJmaterial {
2757
2758 RGBcolor color;
2759 std::string texture;
2760 uint materialID;
2761 bool textureHasTransparency = false;
2762 bool textureColorIsOverridden = false;
2763
2764 OBJmaterial(const RGBcolor &a_color, std::string a_texture, uint a_materialID) : color{a_color}, texture{std::move(a_texture)}, materialID{a_materialID} {};
2765 };
2766
2767 std::map<std::string, OBJmaterial> loadMTL(const std::string &filebase, const std::string &material_file, const RGBcolor &default_color);
2768
2769 void loadMaterialData(pugi::xml_node mat_node, const std::string &material_label);
2770
2771 void loadPData(pugi::xml_node p, uint UUID);
2772
2773 void loadOData(pugi::xml_node p, uint ID);
2774
2775 void loadOsubPData(pugi::xml_node p, uint ID, helios::WarningAggregator &warnings);
2776
2777 void writeDataToXMLstream(const char *data_group, const std::vector<std::string> &data_labels, void *ptr, std::ofstream &outfile) const;
2778
2779
2780 //---------- CONTEXT INITIALIZATION FLAGS ---------//
2781
2782 uint currentUUID;
2783
2784 uint currentObjectID;
2785
2786 //---------- MEMORY HANDLING -----------------//
2787
2789
2793 static void out_of_memory_handler();
2794
2796
2799 static void install_out_of_memory_handler();
2800
2801 public:
2803 Context();
2804
2806 ~Context();
2807
2809 Context(const Context &) = delete;
2810
2812 void operator=(const Context &) = delete;
2813
2815
2820 static int selfTest(int argc, char **argv);
2821
2823
2826 void seedRandomGenerator(uint seed);
2827
2829
2832 std::minstd_rand0 *getRandomGenerator();
2833
2835
2839 void markGeometryClean();
2840
2842
2846 void markGeometryDirty();
2847
2849
2853 [[nodiscard]] bool isGeometryDirty() const;
2854
2856
2859 void markPrimitiveDirty(uint UUID) const;
2860
2862
2865 void markPrimitiveDirty(const std::vector<uint> &UUIDs) const;
2866
2868
2871 void markPrimitiveClean(uint UUID) const;
2872
2874
2877 void markPrimitiveClean(const std::vector<uint> &UUIDs) const;
2878
2880
2884 [[nodiscard]] bool isPrimitiveDirty(uint UUID) const;
2885
2887
2890 uint addPatch();
2891
2893
2900 uint addPatch(const helios::vec3 &center, const helios::vec2 &size);
2901
2903
2911 uint addPatch(const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation);
2912
2914
2921 uint addPatch(const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBcolor &color);
2922
2924
2930 uint addPatch(const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBAcolor &color);
2931
2933
2941 uint addPatch(const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texture_file);
2942
2944
2954 uint addPatch(const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texture_file, const helios::vec2 &uv_center, const helios::vec2 &uv_size);
2955
2957
2964 uint addTriangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2);
2965
2967
2975 uint addTriangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const helios::RGBcolor &color);
2976
2978
2986 uint addTriangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const helios::RGBAcolor &color);
2987
2989
3001 uint addTriangle(const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2, const char *texture_file, const helios::vec2 &uv0, const helios::vec2 &uv1, const helios::vec2 &uv2);
3002
3004
3011 uint addVoxel(const helios::vec3 &center, const helios::vec3 &size);
3012
3014
3022 uint addVoxel(const helios::vec3 &center, const helios::vec3 &size, const float &rotation);
3023
3025
3032 uint addVoxel(const helios::vec3 &center, const helios::vec3 &size, const float &rotation, const helios::RGBcolor &color);
3033
3035
3042 uint addVoxel(const helios::vec3 &center, const helios::vec3 &size, const float &rotation, const helios::RGBAcolor &color);
3043
3045
3049 void translatePrimitive(uint UUID, const vec3 &shift);
3050
3052
3056 void translatePrimitive(const std::vector<uint> &UUIDs, const vec3 &shift);
3057
3059
3064 void rotatePrimitive(uint UUID, float rotation_rad, const char *axis);
3065
3067
3072 void rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const char *axis);
3073
3075
3080 void rotatePrimitive(uint UUID, float rotation_rad, const helios::vec3 &axis);
3081
3083
3088 void rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const vec3 &axis);
3089
3091
3097 void rotatePrimitive(uint UUID, float rotation_rad, const helios::vec3 &origin, const helios::vec3 &axis);
3098
3100
3106 void rotatePrimitive(const std::vector<uint> &UUIDs, float rotation_rad, const helios::vec3 &origin, const vec3 &axis);
3107
3109
3114 void setPrimitiveNormal(uint UUID, const helios::vec3 &origin, const helios::vec3 &new_normal);
3115
3117
3122 void setPrimitiveNormal(const std::vector<uint> &UUIDs, const helios::vec3 &origin, const vec3 &new_normal);
3123
3125
3130 void setPrimitiveElevation(uint UUID, const helios::vec3 &origin, float new_elevation);
3131
3133
3138 void setPrimitiveAzimuth(uint UUID, const helios::vec3 &origin, float new_azimuth);
3139
3141
3145 void scalePrimitive(uint UUID, const helios::vec3 &S);
3146
3148
3152 void scalePrimitive(const std::vector<uint> &UUIDs, const helios::vec3 &S);
3153
3155
3160 void scalePrimitiveAboutPoint(uint UUID, const helios::vec3 &S, const helios::vec3 &point);
3161
3163
3168 void scalePrimitiveAboutPoint(const std::vector<uint> &UUIDs, const helios::vec3 &S, const helios::vec3 &point);
3169
3171
3174 void deletePrimitive(uint UUID);
3175
3177
3180 void deletePrimitive(const std::vector<uint> &UUIDs);
3181
3183
3187 uint copyPrimitive(uint UUID);
3188
3190
3194 std::vector<uint> copyPrimitive(const std::vector<uint> &UUIDs);
3195
3197
3201 void copyPrimitiveData(uint sourceUUID, uint destinationUUID);
3202
3204
3209 void renamePrimitiveData(uint UUID, const char *old_label, const char *new_label);
3210
3212
3217 void duplicatePrimitiveData(uint UUID, const char *old_label, const char *new_label);
3218
3220
3223 [[nodiscard]] bool doesPrimitiveExist(uint UUID) const;
3224
3226
3230 [[nodiscard]] bool doesPrimitiveExist(const std::vector<uint> &UUIDs) const;
3231
3233
3238 [[nodiscard]] helios::vec2 getPatchSize(uint UUID) const;
3239
3241
3246 [[nodiscard]] helios::vec3 getPatchCenter(uint UUID) const;
3247
3249
3255 [[nodiscard]] helios::vec3 getTriangleVertex(uint UUID, uint number) const;
3256
3258
3264 void setTriangleVertices(uint UUID, const helios::vec3 &vertex0, const helios::vec3 &vertex1, const helios::vec3 &vertex2);
3265
3267
3272 [[nodiscard]] helios::vec3 getVoxelCenter(uint UUID) const;
3273
3275
3280 [[nodiscard]] helios::vec3 getVoxelSize(uint UUID) const;
3281
3283
3287 [[nodiscard]] size_t getPrimitiveCount(bool include_hidden_primitives = true) const;
3288
3290
3294 [[nodiscard]] size_t getTriangleCount(bool include_hidden_primitives = true) const;
3295
3297
3301 [[nodiscard]] size_t getPatchCount(bool include_hidden_primitives = true) const;
3302
3304 [[nodiscard]] std::vector<uint> getAllUUIDs() const;
3305
3307
3311 [[nodiscard]] std::vector<uint> getDirtyUUIDs(bool include_deleted_UUIDs = false) const;
3312
3314
3317 [[nodiscard]] std::vector<uint> getDeletedUUIDs() const;
3318
3320
3323 void hidePrimitive(uint UUID) const;
3324
3326
3329 void hidePrimitive(const std::vector<uint> &UUIDs) const;
3330
3332
3335 void showPrimitive(uint UUID) const;
3336
3338
3341 void showPrimitive(const std::vector<uint> &UUIDs) const;
3342
3344
3348 [[nodiscard]] bool isPrimitiveHidden(uint UUID) const;
3349
3351
3354 void cleanDeletedUUIDs(std::vector<uint> &UUIDs) const;
3355
3357
3360 void cleanDeletedUUIDs(std::vector<std::vector<uint>> &UUIDs) const;
3361
3363
3366 void cleanDeletedUUIDs(std::vector<std::vector<std::vector<uint>>> &UUIDs) const;
3367
3368 //-------- Primitive Data Methods ---------- //
3369
3371
3377 template<typename T>
3378 void setPrimitiveData(uint UUID, const char *label, const T &data) {
3379#ifdef HELIOS_DEBUG
3380 if (primitives.find(UUID) == primitives.end()) {
3381 helios_runtime_error("ERROR (Context::setPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3382 }
3383#endif
3384 // Check if this primitive already has cached data for registry maintenance
3385 std::string label_str = std::string(label);
3386 bool had_cached_value = false;
3387
3388 // Handle caching only for supported types, and avoid character arrays
3389 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
3390 if (isPrimitiveDataValueCachingEnabled(label_str) && primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3391 T old_cached_value{};
3392 primitives.at(UUID)->getPrimitiveData(label, old_cached_value);
3393 decrementPrimitiveValueRegistry(label_str, old_cached_value);
3394 had_cached_value = true;
3395 }
3396 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3397 if (isPrimitiveDataValueCachingEnabled(label_str) && primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3398 std::string old_cached_value;
3399 primitives.at(UUID)->getPrimitiveData(label, old_cached_value);
3400 decrementPrimitiveValueRegistry(label_str, old_cached_value);
3401 had_cached_value = true;
3402 }
3403 }
3404
3405 if (!primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3406 incrementPrimitiveDataLabelCounter(label);
3407 }
3408
3409 // Validate data type consistency and register/validate type
3410 HeliosDataType data_type;
3411 if constexpr (std::is_same_v<T, int>) {
3412 data_type = HELIOS_TYPE_INT;
3413 } else if constexpr (std::is_same_v<T, uint>) {
3414 data_type = HELIOS_TYPE_UINT;
3415 } else if constexpr (std::is_same_v<T, float>) {
3416 data_type = HELIOS_TYPE_FLOAT;
3417 } else if constexpr (std::is_same_v<T, double>) {
3418 data_type = HELIOS_TYPE_DOUBLE;
3419 } else if constexpr (std::is_same_v<T, vec2>) {
3420 data_type = HELIOS_TYPE_VEC2;
3421 } else if constexpr (std::is_same_v<T, vec3>) {
3422 data_type = HELIOS_TYPE_VEC3;
3423 } else if constexpr (std::is_same_v<T, vec4>) {
3424 data_type = HELIOS_TYPE_VEC4;
3425 } else if constexpr (std::is_same_v<T, int2>) {
3426 data_type = HELIOS_TYPE_INT2;
3427 } else if constexpr (std::is_same_v<T, int3>) {
3428 data_type = HELIOS_TYPE_INT3;
3429 } else if constexpr (std::is_same_v<T, int4>) {
3430 data_type = HELIOS_TYPE_INT4;
3431 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3432 data_type = HELIOS_TYPE_STRING;
3433 }
3434 HeliosDataType target_type = registerOrValidatePrimitiveDataType<T>(label, data_type);
3435
3436 // Store data with type casting if needed
3437 if (target_type != data_type && isTypeCastingSupported(data_type, target_type)) {
3438 storeDataWithTypeCasting(UUID, label, data, target_type);
3439 } else {
3440 primitives.at(UUID)->setPrimitiveData(label, data);
3441 }
3442
3443 // Update value registry if caching is enabled for this label (only for new data)
3444 if (isPrimitiveDataValueCachingEnabled(label_str) && !had_cached_value) {
3445 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
3446 incrementPrimitiveValueRegistry(label_str, data);
3447 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3448 incrementPrimitiveValueRegistry(label_str, std::string(data));
3449 }
3450 } else if (isPrimitiveDataValueCachingEnabled(label_str) && had_cached_value) {
3451 // For updates, just add the new value (old value was already decremented)
3452 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
3453 incrementPrimitiveValueRegistry(label_str, data);
3454 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3455 incrementPrimitiveValueRegistry(label_str, std::string(data));
3456 }
3457 }
3458 }
3459
3461
3467 template<typename T>
3468 void setPrimitiveData(uint UUID, const char *label, const std::vector<T> &data) {
3469#ifdef HELIOS_DEBUG
3470 if (primitives.find(UUID) == primitives.end()) {
3471 helios_runtime_error("ERROR (Context::setPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3472 }
3473#endif
3474 if (!primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3475 incrementPrimitiveDataLabelCounter(label);
3476 }
3477
3478 // For vector data, register the base type (not vector type)
3479 HeliosDataType data_type;
3480 if constexpr (std::is_same_v<T, int>) {
3481 data_type = HELIOS_TYPE_INT;
3482 } else if constexpr (std::is_same_v<T, uint>) {
3483 data_type = HELIOS_TYPE_UINT;
3484 } else if constexpr (std::is_same_v<T, float>) {
3485 data_type = HELIOS_TYPE_FLOAT;
3486 } else if constexpr (std::is_same_v<T, double>) {
3487 data_type = HELIOS_TYPE_DOUBLE;
3488 } else if constexpr (std::is_same_v<T, vec2>) {
3489 data_type = HELIOS_TYPE_VEC2;
3490 } else if constexpr (std::is_same_v<T, vec3>) {
3491 data_type = HELIOS_TYPE_VEC3;
3492 } else if constexpr (std::is_same_v<T, vec4>) {
3493 data_type = HELIOS_TYPE_VEC4;
3494 } else if constexpr (std::is_same_v<T, int2>) {
3495 data_type = HELIOS_TYPE_INT2;
3496 } else if constexpr (std::is_same_v<T, int3>) {
3497 data_type = HELIOS_TYPE_INT3;
3498 } else if constexpr (std::is_same_v<T, int4>) {
3499 data_type = HELIOS_TYPE_INT4;
3500 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3501 data_type = HELIOS_TYPE_STRING;
3502 }
3503 registerOrValidatePrimitiveDataType<T>(label, data_type);
3504
3505 primitives.at(UUID)->setPrimitiveData(label, data);
3506 }
3507
3509
3516 template<typename T>
3517 void setPrimitiveData(const std::vector<uint> &UUIDs, const char *label, const std::vector<T> &data) {
3518#ifdef HELIOS_DEBUG
3519 if (UUIDs.size() != data.size()) {
3520 helios_runtime_error("ERROR (Context::setPrimitiveData): UUIDs and data vectors must be the same size.");
3521 }
3522#endif
3523
3524 // Validate data type consistency and register/validate type (only once for all elements)
3525 HeliosDataType target_type = HELIOS_TYPE_UNKNOWN;
3526 if (!UUIDs.empty()) {
3527 HeliosDataType data_type;
3528 if constexpr (std::is_same_v<T, int>) {
3529 data_type = HELIOS_TYPE_INT;
3530 } else if constexpr (std::is_same_v<T, uint>) {
3531 data_type = HELIOS_TYPE_UINT;
3532 } else if constexpr (std::is_same_v<T, float>) {
3533 data_type = HELIOS_TYPE_FLOAT;
3534 } else if constexpr (std::is_same_v<T, double>) {
3535 data_type = HELIOS_TYPE_DOUBLE;
3536 } else if constexpr (std::is_same_v<T, vec2>) {
3537 data_type = HELIOS_TYPE_VEC2;
3538 } else if constexpr (std::is_same_v<T, vec3>) {
3539 data_type = HELIOS_TYPE_VEC3;
3540 } else if constexpr (std::is_same_v<T, vec4>) {
3541 data_type = HELIOS_TYPE_VEC4;
3542 } else if constexpr (std::is_same_v<T, int2>) {
3543 data_type = HELIOS_TYPE_INT2;
3544 } else if constexpr (std::is_same_v<T, int3>) {
3545 data_type = HELIOS_TYPE_INT3;
3546 } else if constexpr (std::is_same_v<T, int4>) {
3547 data_type = HELIOS_TYPE_INT4;
3548 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3549 data_type = HELIOS_TYPE_STRING;
3550 }
3551 target_type = registerOrValidatePrimitiveDataType<T>(label, data_type);
3552 }
3553
3554 for (uint UUID: UUIDs) {
3555#ifdef HELIOS_DEBUG
3556 if (primitives.find(UUID) == primitives.end()) {
3557 helios_runtime_error("ERROR (Context::setPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3558 }
3559#endif
3560 if (!primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3561 incrementPrimitiveDataLabelCounter(label);
3562 }
3563 }
3564
3565#ifdef USE_OPENMP
3566#pragma omp parallel for
3567#endif
3568 for (int i = 0; i < (int) UUIDs.size(); ++i) {
3569 primitives.at(UUIDs[i])->setPrimitiveData(label, data[i]);
3570 }
3571 }
3572
3574
3580 template<typename T>
3581 void setPrimitiveData(const std::vector<uint> &UUIDs, const char *label, const T &data) {
3582
3583 // Validate data type consistency and register/validate type
3584 if (!UUIDs.empty()) {
3585 HeliosDataType data_type;
3586 if constexpr (std::is_same_v<T, int>) {
3587 data_type = HELIOS_TYPE_INT;
3588 } else if constexpr (std::is_same_v<T, uint>) {
3589 data_type = HELIOS_TYPE_UINT;
3590 } else if constexpr (std::is_same_v<T, float>) {
3591 data_type = HELIOS_TYPE_FLOAT;
3592 } else if constexpr (std::is_same_v<T, double>) {
3593 data_type = HELIOS_TYPE_DOUBLE;
3594 } else if constexpr (std::is_same_v<T, vec2>) {
3595 data_type = HELIOS_TYPE_VEC2;
3596 } else if constexpr (std::is_same_v<T, vec3>) {
3597 data_type = HELIOS_TYPE_VEC3;
3598 } else if constexpr (std::is_same_v<T, vec4>) {
3599 data_type = HELIOS_TYPE_VEC4;
3600 } else if constexpr (std::is_same_v<T, int2>) {
3601 data_type = HELIOS_TYPE_INT2;
3602 } else if constexpr (std::is_same_v<T, int3>) {
3603 data_type = HELIOS_TYPE_INT3;
3604 } else if constexpr (std::is_same_v<T, int4>) {
3605 data_type = HELIOS_TYPE_INT4;
3606 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
3607 data_type = HELIOS_TYPE_STRING;
3608 }
3609 registerOrValidatePrimitiveDataType<T>(label, data_type);
3610 }
3611
3612 for (uint UUID: UUIDs) {
3613#ifdef HELIOS_DEBUG
3614 if (primitives.find(UUID) == primitives.end()) {
3615 helios_runtime_error("ERROR (Context::setPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3616 }
3617#endif
3618 if (!primitives.at(UUID)->doesPrimitiveDataExist(label)) {
3619 incrementPrimitiveDataLabelCounter(label);
3620 }
3621 }
3622
3623#ifdef USE_OPENMP
3624#pragma omp parallel for
3625#endif
3626 for (int i = 0; i < (int) UUIDs.size(); ++i) {
3627 primitives.at(UUIDs[i])->setPrimitiveData(label, data);
3628 }
3629 }
3630
3632
3638 template<typename T>
3639 void getPrimitiveData(uint UUID, const char *label, T &data) const {
3640#ifdef HELIOS_DEBUG
3641 if (primitives.find(UUID) == primitives.end()) {
3642 helios_runtime_error("ERROR (Context::getPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3643 }
3644#endif
3645 primitives.at(UUID)->getPrimitiveData(label, data);
3646 }
3647
3649
3656 template<typename T>
3657 void getPrimitiveData(uint UUID, const char *label, std::vector<T> &data) const {
3658#ifdef HELIOS_DEBUG
3659 if (primitives.find(UUID) == primitives.end()) {
3660 helios_runtime_error("ERROR (Context::getPrimitiveData): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3661 }
3662#endif
3663 primitives.at(UUID)->getPrimitiveData(label, data);
3664 }
3665
3667
3673 [[deprecated]]
3674 HeliosDataType getPrimitiveDataType(uint UUID, const char *label) const;
3675
3677
3683 HeliosDataType getPrimitiveDataType(const char *label) const;
3684
3686
3691 uint getPrimitiveDataSize(uint UUID, const char *label) const;
3692
3694
3699 bool doesPrimitiveDataExist(uint UUID, const char *label) const;
3700
3702
3706 void clearPrimitiveData(uint UUID, const char *label);
3707
3709
3713 void clearPrimitiveData(const std::vector<uint> &UUIDs, const char *label);
3714
3716
3720 void clearPrimitiveData(const char *label);
3721
3723
3726 [[nodiscard]] std::vector<std::string> listAllPrimitiveDataLabels() const;
3727
3728
3729 //----------- VALUE-LEVEL CACHING CONFIGURATION ----------//
3730
3732
3735 void enablePrimitiveDataValueCaching(const std::string &label);
3736
3738
3741 void disablePrimitiveDataValueCaching(const std::string &label);
3742
3744
3748 [[nodiscard]] bool isPrimitiveDataValueCachingEnabled(const std::string &label) const;
3749
3751
3754 void enableObjectDataValueCaching(const std::string &label);
3755
3757
3760 void disableObjectDataValueCaching(const std::string &label);
3761
3763
3767 [[nodiscard]] bool isObjectDataValueCachingEnabled(const std::string &label) const;
3768
3769 //----------- UNIQUE VALUE QUERY METHODS ----------//
3770
3771
3773
3777 [[nodiscard]] PrimitiveType getPrimitiveType(uint UUID) const;
3778
3780
3784 void setPrimitiveParentObjectID(uint UUID, uint objID);
3785
3787
3791 void setPrimitiveParentObjectID(const std::vector<uint> &UUIDs, uint objID);
3792
3794
3797 [[nodiscard]] uint getPrimitiveParentObjectID(uint UUID) const;
3798
3800
3803 [[nodiscard]] std::vector<uint> getPrimitiveParentObjectID(const std::vector<uint> &UUIDs) const;
3804
3806
3809 [[nodiscard]] std::vector<uint> getUniquePrimitiveParentObjectIDs(const std::vector<uint> &UUIDs) const;
3810
3812
3816 [[nodiscard]] std::vector<uint> getUniquePrimitiveParentObjectIDs(const std::vector<uint> &UUIDs, bool include_ObjID_zero) const;
3817
3819
3822 [[nodiscard]] float getPrimitiveArea(uint UUID) const;
3823
3825
3830 void getPrimitiveBoundingBox(uint UUID, vec3 &min_corner, vec3 &max_corner) const;
3831
3833
3838 void getPrimitiveBoundingBox(const std::vector<uint> &UUIDs, vec3 &min_corner, vec3 &max_corner) const;
3839
3841
3844 void hideObject(uint ObjID);
3845
3847
3850 void hideObject(const std::vector<uint> &ObjIDs);
3851
3853
3856 void showObject(uint ObjID);
3857
3859
3862 void showObject(const std::vector<uint> &ObjIDs);
3863
3865
3869 [[nodiscard]] bool isObjectHidden(uint ObjID) const;
3870
3872
3875 [[nodiscard]] float getObjectArea(uint ObjID) const;
3876
3878
3881 [[nodiscard]] helios::vec3 getObjectAverageNormal(uint ObjID) const;
3882
3884
3887 [[nodiscard]] uint getObjectPrimitiveCount(uint ObjID) const;
3888
3890
3893 [[nodiscard]] helios::vec3 getObjectCenter(uint ObjID) const;
3894
3896
3900 void setObjectColor(uint ObjID, const helios::RGBcolor &color) const;
3901
3903
3907 void setObjectColor(const std::vector<uint> &ObjIDs, const helios::RGBcolor &color) const;
3908
3910
3914 void setObjectColor(uint ObjID, const helios::RGBAcolor &color) const;
3915
3917
3921 void setObjectColor(const std::vector<uint> &ObjIDs, const helios::RGBAcolor &color) const;
3922
3924
3927 [[nodiscard]] std::string getObjectTextureFile(uint ObjID) const;
3928
3930
3934 void getObjectTransformationMatrix(uint ObjID, float (&T)[16]) const;
3935
3937
3941 void setObjectTransformationMatrix(uint ObjID, float (&T)[16]) const;
3942
3944
3948 void setObjectTransformationMatrix(const std::vector<uint> &ObjIDs, float (&T)[16]) const;
3949
3951
3959 void setObjectAverageNormal(uint ObjID, const vec3 &origin, const vec3 &new_normal) const;
3960
3962
3968 void setObjectOrigin(uint ObjID, const vec3 &origin) const;
3969
3971
3974 [[nodiscard]] bool objectHasTexture(uint ObjID) const;
3975
3977
3981 [[nodiscard]] bool doesObjectContainPrimitive(uint ObjID, uint UUID) const;
3982
3984
3987 void overrideObjectTextureColor(uint ObjID) const;
3988
3990
3993 void overrideObjectTextureColor(const std::vector<uint> &ObjIDs) const;
3994
3997
4000 void useObjectTextureColor(uint ObjID) const;
4001
4004
4007 void useObjectTextureColor(const std::vector<uint> &ObjIDs);
4008
4010
4015 void getObjectBoundingBox(uint ObjID, vec3 &min_corner, vec3 &max_corner) const;
4016
4018
4023 void getObjectBoundingBox(const std::vector<uint> &ObjIDs, vec3 &min_corner, vec3 &max_corner) const;
4024
4026
4029 void printObjectInfo(uint ObjID) const;
4030
4032 [[nodiscard]] std::vector<std::string> listObjectData(uint ObjID) const;
4033
4035 [[nodiscard]] std::vector<std::string> listPrimitiveData(uint UUID) const;
4036
4038
4042 [[nodiscard]] float getPrimitiveSolidFraction(uint UUID) const;
4043
4045
4048 [[nodiscard]] helios::vec3 getPrimitiveNormal(uint UUID) const;
4049
4051
4055 void getPrimitiveTransformationMatrix(uint UUID, float (&T)[16]) const;
4056
4058
4062 void setPrimitiveTransformationMatrix(uint UUID, float (&T)[16]);
4063
4065
4069 void setPrimitiveTransformationMatrix(const std::vector<uint> &UUIDs, float (&T)[16]);
4070
4072
4075 [[nodiscard]] std::vector<helios::vec3> getPrimitiveVertices(uint UUID) const;
4076
4078
4081 [[nodiscard]] helios::RGBcolor getPrimitiveColor(uint UUID) const;
4082
4084
4087 [[nodiscard]] helios::RGBcolor getPrimitiveColorRGB(uint UUID) const;
4088
4090
4093 [[nodiscard]] helios::RGBAcolor getPrimitiveColorRGBA(uint UUID) const;
4094
4096
4100 void setPrimitiveColor(uint UUID, const helios::RGBcolor &color) const;
4101
4103
4107 void setPrimitiveColor(const std::vector<uint> &UUIDs, const helios::RGBcolor &color) const;
4108
4110
4114 void setPrimitiveColor(uint UUID, const helios::RGBAcolor &color) const;
4115
4117
4121 void setPrimitiveColor(const std::vector<uint> &UUIDs, const helios::RGBAcolor &color) const;
4122
4124
4128 [[nodiscard]] std::string getPrimitiveTextureFile(uint UUID) const;
4129
4131
4135 void setPrimitiveTextureFile(uint UUID, const std::string &texturefile) const;
4136
4138
4142 [[nodiscard]] helios::int2 getPrimitiveTextureSize(uint UUID) const;
4143
4145
4148 [[nodiscard]] std::vector<vec2> getPrimitiveTextureUV(uint UUID) const;
4149
4151
4155 [[nodiscard]] bool primitiveTextureHasTransparencyChannel(uint UUID) const;
4156
4158
4162 [[nodiscard]] const std::vector<std::vector<bool>> *getPrimitiveTextureTransparencyData(uint UUID) const;
4163
4165
4168 void overridePrimitiveTextureColor(uint UUID) const;
4169
4171
4174 void overridePrimitiveTextureColor(const std::vector<uint> &UUIDs) const;
4175
4177
4180 void usePrimitiveTextureColor(uint UUID) const;
4181
4183
4186 void usePrimitiveTextureColor(const std::vector<uint> &UUIDs) const;
4187
4189
4192 [[nodiscard]] bool isPrimitiveTextureColorOverridden(uint UUID) const;
4193
4195
4198 void printPrimitiveInfo(uint UUID) const;
4199
4201
4205 void reportAPIWarnings() const;
4206
4207 //-------- Material Management Methods ---------- //
4208
4210
4215 void addMaterial(const std::string &material_label);
4216
4218
4223 void renameMaterial(const std::string &old_label, const std::string &new_label);
4224
4226
4230 [[nodiscard]] bool doesMaterialExist(const std::string &material_label) const;
4231
4233
4236 [[nodiscard]] std::vector<std::string> listMaterials() const;
4237
4239
4243 [[nodiscard]] RGBAcolor getMaterialColor(const std::string &material_label) const;
4244
4246
4250 [[nodiscard]] std::string getMaterialTexture(const std::string &material_label) const;
4251
4253
4257 [[nodiscard]] bool isMaterialTextureColorOverridden(const std::string &material_label) const;
4258
4260
4265 void setMaterialColor(const std::string &material_label, const RGBAcolor &color);
4266
4268
4273 void setMaterialTexture(const std::string &material_label, const std::string &texture_file);
4274
4276
4281 void setMaterialTextureColorOverride(const std::string &material_label, bool override);
4282
4284
4288 [[nodiscard]] uint getMaterialTwosidedFlag(const std::string &material_label) const;
4289
4291
4296 void setMaterialTwosidedFlag(const std::string &material_label, uint twosided_flag);
4297
4299
4306 [[nodiscard]] uint getPrimitiveTwosidedFlag(uint UUID, uint default_value = 1) const;
4307
4309
4314 void assignMaterialToPrimitive(uint UUID, const std::string &material_label);
4315
4317
4322 void assignMaterialToPrimitive(const std::vector<uint> &UUIDs, const std::string &material_label);
4323
4325
4330 void assignMaterialToObject(uint ObjID, const std::string &material_label);
4331
4333
4338 void assignMaterialToObject(const std::vector<uint> &ObjIDs, const std::string &material_label);
4339
4341
4345 [[nodiscard]] std::string getPrimitiveMaterialLabel(uint UUID) const;
4346
4348
4352 [[nodiscard]] std::vector<uint> getPrimitivesUsingMaterial(const std::string &material_label) const;
4353
4355
4360 void deleteMaterial(const std::string &material_label);
4361
4363
4370 template<typename T>
4371 void setMaterialData(const std::string &material_label, const char *data_label, const T &data) {
4372 uint matID = getMaterialIDFromLabel(material_label);
4373 materials[matID].setMaterialData(data_label, data);
4374 }
4375
4377
4384 template<typename T>
4385 void setMaterialData(const std::string &material_label, const char *data_label, const std::vector<T> &data) {
4386 uint matID = getMaterialIDFromLabel(material_label);
4387 materials[matID].setMaterialData(data_label, data);
4388 }
4389
4391
4397 template<typename T>
4398 void getMaterialData(const std::string &material_label, const char *data_label, T &data) const {
4399 uint matID = getMaterialIDFromLabel(material_label);
4400 materials.at(matID).getMaterialData(data_label, data);
4401 }
4402
4404
4410 template<typename T>
4411 void getMaterialData(const std::string &material_label, const char *data_label, std::vector<T> &data) const {
4412 uint matID = getMaterialIDFromLabel(material_label);
4413 materials.at(matID).getMaterialData(data_label, data);
4414 }
4415
4417
4422 [[nodiscard]] bool doesMaterialDataExist(const std::string &material_label, const char *data_label) const;
4423
4425
4430 [[nodiscard]] HeliosDataType getMaterialDataType(const std::string &material_label, const char *data_label) const;
4431
4433
4437 void clearMaterialData(const std::string &material_label, const char *data_label);
4438
4440
4450 template<typename T>
4451 void getDataWithMaterialFallback(uint UUID, const char *data_label, T &data) const {
4452 Primitive *prim = getPrimitivePointer_private(UUID);
4453 uint materialID = prim->materialID;
4454
4455 // First check if material has this data
4456 if (materials.find(materialID) != materials.end() && materials.at(materialID).doesMaterialDataExist(data_label)) {
4457 materials.at(materialID).getMaterialData(data_label, data);
4458 }
4459 // Otherwise check if primitive has this data
4460 else if (prim->doesPrimitiveDataExist(data_label)) {
4461 prim->getPrimitiveData(data_label, data);
4462 }
4463 // If neither has the data, throw error
4464 else {
4465 helios_runtime_error("ERROR (Context::getDataWithMaterialFallback): Data " + std::string(data_label) + " does not exist for primitive " + std::to_string(UUID) + " (neither in its material nor as primitive data).");
4466 }
4467 }
4468
4470
4474 [[nodiscard]] uint getPrimitiveMaterialID(uint UUID) const;
4475
4477
4481 [[nodiscard]] const Material &getMaterial(uint materialID) const;
4482
4484
4488 [[nodiscard]] uint getMaterialIDFromLabel(const std::string &material_label) const;
4489
4491
4494 [[nodiscard]] uint getMaterialCount() const;
4495
4496 //-------- Compound Object Data Methods ---------- //
4497
4499
4505 template<typename T>
4506 void setObjectData(uint objID, const char *label, const T &data) {
4507#ifdef HELIOS_DEBUG
4508 if (objects.find(objID) == objects.end()) {
4509 helios_runtime_error("ERROR (Context::setObjectData): Object ID of " + std::to_string(objID) + " does not exist in the Context.");
4510 }
4511#endif
4512 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4513 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4514 "Context::setObjectData() was called with an unsupported type.");
4515
4516 // Validate data type consistency and register/validate type
4517 HeliosDataType data_type;
4518 if constexpr (std::is_same_v<T, int>) {
4519 data_type = HELIOS_TYPE_INT;
4520 } else if constexpr (std::is_same_v<T, uint>) {
4521 data_type = HELIOS_TYPE_UINT;
4522 } else if constexpr (std::is_same_v<T, float>) {
4523 data_type = HELIOS_TYPE_FLOAT;
4524 } else if constexpr (std::is_same_v<T, double>) {
4525 data_type = HELIOS_TYPE_DOUBLE;
4526 } else if constexpr (std::is_same_v<T, vec2>) {
4527 data_type = HELIOS_TYPE_VEC2;
4528 } else if constexpr (std::is_same_v<T, vec3>) {
4529 data_type = HELIOS_TYPE_VEC3;
4530 } else if constexpr (std::is_same_v<T, vec4>) {
4531 data_type = HELIOS_TYPE_VEC4;
4532 } else if constexpr (std::is_same_v<T, int2>) {
4533 data_type = HELIOS_TYPE_INT2;
4534 } else if constexpr (std::is_same_v<T, int3>) {
4535 data_type = HELIOS_TYPE_INT3;
4536 } else if constexpr (std::is_same_v<T, int4>) {
4537 data_type = HELIOS_TYPE_INT4;
4538 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4539 data_type = HELIOS_TYPE_STRING;
4540 }
4541 registerOrValidateObjectDataType<T>(label, data_type);
4542
4543 // Check if this object already has cached data for registry maintenance
4544 std::string label_str = std::string(label);
4545 bool had_cached_value = false;
4546
4547 // Handle caching only for supported types, and avoid character arrays
4548 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4549 if (isObjectDataValueCachingEnabled(label_str) && objects.at(objID)->doesObjectDataExist(label)) {
4550 T old_cached_value{};
4551 objects.at(objID)->getObjectData(label, old_cached_value);
4552 decrementObjectValueRegistry(label_str, old_cached_value);
4553 had_cached_value = true;
4554 }
4555 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4556 if (isObjectDataValueCachingEnabled(label_str) && objects.at(objID)->doesObjectDataExist(label)) {
4557 std::string old_cached_value;
4558 objects.at(objID)->getObjectData(label, old_cached_value);
4559 decrementObjectValueRegistry(label_str, old_cached_value);
4560 had_cached_value = true;
4561 }
4562 }
4563
4564 if (!objects.at(objID)->doesObjectDataExist(label)) {
4565 incrementObjectDataLabelCounter(label);
4566 }
4567 objects.at(objID)->setObjectData(label, data);
4568
4569 // Update value registry if caching is enabled for this label (only for new data)
4570 if (isObjectDataValueCachingEnabled(label_str) && !had_cached_value) {
4571 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4572 incrementObjectValueRegistry(label_str, data);
4573 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4574 incrementObjectValueRegistry(label_str, std::string(data));
4575 }
4576 } else if (isObjectDataValueCachingEnabled(label_str) && had_cached_value) {
4577 // For updates, just add the new value (old value was already decremented)
4578 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4579 incrementObjectValueRegistry(label_str, data);
4580 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4581 incrementObjectValueRegistry(label_str, std::string(data));
4582 }
4583 }
4584 }
4585
4587
4593 template<typename T>
4594 void setObjectData(const std::vector<uint> &objIDs, const char *label, const T &data) {
4595 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4596 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4597 "Context::setObjectData() was called with an unsupported type.");
4598
4599 // Validate data type consistency and register/validate type
4600 if (!objIDs.empty()) {
4601 HeliosDataType data_type;
4602 if constexpr (std::is_same_v<T, int>) {
4603 data_type = HELIOS_TYPE_INT;
4604 } else if constexpr (std::is_same_v<T, uint>) {
4605 data_type = HELIOS_TYPE_UINT;
4606 } else if constexpr (std::is_same_v<T, float>) {
4607 data_type = HELIOS_TYPE_FLOAT;
4608 } else if constexpr (std::is_same_v<T, double>) {
4609 data_type = HELIOS_TYPE_DOUBLE;
4610 } else if constexpr (std::is_same_v<T, vec2>) {
4611 data_type = HELIOS_TYPE_VEC2;
4612 } else if constexpr (std::is_same_v<T, vec3>) {
4613 data_type = HELIOS_TYPE_VEC3;
4614 } else if constexpr (std::is_same_v<T, vec4>) {
4615 data_type = HELIOS_TYPE_VEC4;
4616 } else if constexpr (std::is_same_v<T, int2>) {
4617 data_type = HELIOS_TYPE_INT2;
4618 } else if constexpr (std::is_same_v<T, int3>) {
4619 data_type = HELIOS_TYPE_INT3;
4620 } else if constexpr (std::is_same_v<T, int4>) {
4621 data_type = HELIOS_TYPE_INT4;
4622 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4623 data_type = HELIOS_TYPE_STRING;
4624 }
4625 registerOrValidateObjectDataType<T>(label, data_type);
4626 }
4627
4628 // Check if caching is enabled for this label
4629 std::string label_str = std::string(label);
4630 bool caching_enabled = isObjectDataValueCachingEnabled(label_str);
4631
4632 // For caching, we need to handle old values before setting new ones (cannot parallelize this part)
4633 if (caching_enabled) {
4634 // Handle caching only for supported types
4635 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4636 for (uint objID: objIDs) {
4637 if (objects.at(objID)->doesObjectDataExist(label)) {
4638 T old_cached_value{};
4639 objects.at(objID)->getObjectData(label, old_cached_value);
4640 decrementObjectValueRegistry(label_str, old_cached_value);
4641 }
4642 }
4643 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4644 for (uint objID: objIDs) {
4645 if (objects.at(objID)->doesObjectDataExist(label)) {
4646 std::string old_cached_value;
4647 objects.at(objID)->getObjectData(label, old_cached_value);
4648 decrementObjectValueRegistry(label_str, old_cached_value);
4649 }
4650 }
4651 }
4652 }
4653
4654 // Count new data labels
4655 for (uint objID: objIDs) {
4656 if (!objects.at(objID)->doesObjectDataExist(label)) {
4657 incrementObjectDataLabelCounter(label);
4658 }
4659 }
4660
4661#ifdef USE_OPENMP
4662#pragma omp parallel for
4663#endif
4664 for (int i = 0; i < (int) objIDs.size(); ++i) {
4665 objects.at(objIDs[i])->setObjectData(label, data);
4666 }
4667
4668 // Update value registry if caching is enabled (increment the new value for all objects)
4669 if (caching_enabled) {
4670 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4671 // Increment the new value once for each object that received it
4672 for (size_t i = 0; i < objIDs.size(); ++i) {
4673 incrementObjectValueRegistry(label_str, data);
4674 }
4675 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4676 // Increment the new value once for each object that received it
4677 for (size_t i = 0; i < objIDs.size(); ++i) {
4678 incrementObjectValueRegistry(label_str, std::string(data));
4679 }
4680 }
4681 }
4682 }
4683
4685
4691 template<typename T>
4692 void setObjectData(const std::vector<std::vector<uint>> &objIDs, const char *label, const T &data) {
4693 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4694 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4695 "Context::setObjectData() was called with an unsupported type.");
4696
4697 // Validate data type consistency and register/validate type
4698 if (!objIDs.empty() && !objIDs[0].empty()) {
4699 HeliosDataType data_type;
4700 if constexpr (std::is_same_v<T, int>) {
4701 data_type = HELIOS_TYPE_INT;
4702 } else if constexpr (std::is_same_v<T, uint>) {
4703 data_type = HELIOS_TYPE_UINT;
4704 } else if constexpr (std::is_same_v<T, float>) {
4705 data_type = HELIOS_TYPE_FLOAT;
4706 } else if constexpr (std::is_same_v<T, double>) {
4707 data_type = HELIOS_TYPE_DOUBLE;
4708 } else if constexpr (std::is_same_v<T, vec2>) {
4709 data_type = HELIOS_TYPE_VEC2;
4710 } else if constexpr (std::is_same_v<T, vec3>) {
4711 data_type = HELIOS_TYPE_VEC3;
4712 } else if constexpr (std::is_same_v<T, vec4>) {
4713 data_type = HELIOS_TYPE_VEC4;
4714 } else if constexpr (std::is_same_v<T, int2>) {
4715 data_type = HELIOS_TYPE_INT2;
4716 } else if constexpr (std::is_same_v<T, int3>) {
4717 data_type = HELIOS_TYPE_INT3;
4718 } else if constexpr (std::is_same_v<T, int4>) {
4719 data_type = HELIOS_TYPE_INT4;
4720 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4721 data_type = HELIOS_TYPE_STRING;
4722 }
4723 registerOrValidateObjectDataType<T>(label, data_type);
4724 }
4725
4726 // Check if caching is enabled for this label
4727 std::string label_str = std::string(label);
4728 bool caching_enabled = isObjectDataValueCachingEnabled(label_str);
4729 size_t total_objects = 0;
4730
4731 // For caching, we need to handle old values before setting new ones (cannot parallelize this part)
4732 if (caching_enabled) {
4733 // Handle caching only for supported types
4734 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4735 for (const auto &j: objIDs) {
4736 for (uint objID: j) {
4737 total_objects++;
4738 if (objects.at(objID)->doesObjectDataExist(label)) {
4739 T old_cached_value{};
4740 objects.at(objID)->getObjectData(label, old_cached_value);
4741 decrementObjectValueRegistry(label_str, old_cached_value);
4742 }
4743 }
4744 }
4745 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4746 for (const auto &j: objIDs) {
4747 for (uint objID: j) {
4748 total_objects++;
4749 if (objects.at(objID)->doesObjectDataExist(label)) {
4750 std::string old_cached_value;
4751 objects.at(objID)->getObjectData(label, old_cached_value);
4752 decrementObjectValueRegistry(label_str, old_cached_value);
4753 }
4754 }
4755 }
4756 }
4757 } else {
4758 // Count total objects for later cache increment
4759 for (const auto &j: objIDs) {
4760 total_objects += j.size();
4761 }
4762 }
4763
4764 for (const auto &j: objIDs) {
4765 for (uint objID: j) {
4766 if (!objects.at(objID)->doesObjectDataExist(label)) {
4767 incrementObjectDataLabelCounter(label);
4768 }
4769 }
4770 }
4771
4772#ifdef USE_OPENMP
4773#pragma omp parallel for
4774#endif
4775 for (int j = 0; j < (int) objIDs.size(); ++j) {
4776 for (size_t i = 0; i < objIDs[j].size(); ++i) {
4777 objects.at(objIDs[j][i])->setObjectData(label, data);
4778 }
4779 }
4780
4781 // Update value registry if caching is enabled (increment the new value for all objects)
4782 if (caching_enabled) {
4783 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4784 // Increment the new value once for each object that received it
4785 for (size_t i = 0; i < total_objects; ++i) {
4786 incrementObjectValueRegistry(label_str, data);
4787 }
4788 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4789 // Increment the new value once for each object that received it
4790 for (size_t i = 0; i < total_objects; ++i) {
4791 incrementObjectValueRegistry(label_str, std::string(data));
4792 }
4793 }
4794 }
4795 }
4796
4798
4804 template<typename T>
4805 void setObjectData(const std::vector<std::vector<std::vector<uint>>> &objIDs, const char *label, const T &data) {
4806 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4807 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4808 "Context::setObjectData() was called with an unsupported type.");
4809
4810 // Validate data type consistency and register/validate type
4811 if (!objIDs.empty() && !objIDs[0].empty() && !objIDs[0][0].empty()) {
4812 HeliosDataType data_type;
4813 if constexpr (std::is_same_v<T, int>) {
4814 data_type = HELIOS_TYPE_INT;
4815 } else if constexpr (std::is_same_v<T, uint>) {
4816 data_type = HELIOS_TYPE_UINT;
4817 } else if constexpr (std::is_same_v<T, float>) {
4818 data_type = HELIOS_TYPE_FLOAT;
4819 } else if constexpr (std::is_same_v<T, double>) {
4820 data_type = HELIOS_TYPE_DOUBLE;
4821 } else if constexpr (std::is_same_v<T, vec2>) {
4822 data_type = HELIOS_TYPE_VEC2;
4823 } else if constexpr (std::is_same_v<T, vec3>) {
4824 data_type = HELIOS_TYPE_VEC3;
4825 } else if constexpr (std::is_same_v<T, vec4>) {
4826 data_type = HELIOS_TYPE_VEC4;
4827 } else if constexpr (std::is_same_v<T, int2>) {
4828 data_type = HELIOS_TYPE_INT2;
4829 } else if constexpr (std::is_same_v<T, int3>) {
4830 data_type = HELIOS_TYPE_INT3;
4831 } else if constexpr (std::is_same_v<T, int4>) {
4832 data_type = HELIOS_TYPE_INT4;
4833 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4834 data_type = HELIOS_TYPE_STRING;
4835 }
4836 registerOrValidateObjectDataType<T>(label, data_type);
4837 }
4838
4839 // Check if caching is enabled for this label
4840 std::string label_str = std::string(label);
4841 bool caching_enabled = isObjectDataValueCachingEnabled(label_str);
4842 size_t total_objects = 0;
4843
4844 // For caching, we need to handle old values before setting new ones (cannot parallelize this part)
4845 if (caching_enabled) {
4846 // Handle caching only for supported types
4847 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4848 for (const auto &k: objIDs) {
4849 for (const auto &j: k) {
4850 for (uint objID: j) {
4851 total_objects++;
4852 if (objects.at(objID)->doesObjectDataExist(label)) {
4853 T old_cached_value{};
4854 objects.at(objID)->getObjectData(label, old_cached_value);
4855 decrementObjectValueRegistry(label_str, old_cached_value);
4856 }
4857 }
4858 }
4859 }
4860 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4861 for (const auto &k: objIDs) {
4862 for (const auto &j: k) {
4863 for (uint objID: j) {
4864 total_objects++;
4865 if (objects.at(objID)->doesObjectDataExist(label)) {
4866 std::string old_cached_value;
4867 objects.at(objID)->getObjectData(label, old_cached_value);
4868 decrementObjectValueRegistry(label_str, old_cached_value);
4869 }
4870 }
4871 }
4872 }
4873 }
4874 } else {
4875 // Count total objects for later cache increment
4876 for (const auto &k: objIDs) {
4877 for (const auto &j: k) {
4878 total_objects += j.size();
4879 }
4880 }
4881 }
4882
4883 for (const auto &k: objIDs) {
4884 for (const auto &j: k) {
4885 for (uint objID: j) {
4886 if (!objects.at(objID)->doesObjectDataExist(label)) {
4887 incrementObjectDataLabelCounter(label);
4888 }
4889 }
4890 }
4891 }
4892
4893#ifdef USE_OPENMP
4894#pragma omp parallel for
4895#endif
4896 for (int k = 0; k < (int) objIDs.size(); ++k) {
4897 for (size_t j = 0; j < objIDs[k].size(); ++j) {
4898 for (size_t i = 0; i < objIDs[k][j].size(); ++i) {
4899 uint objID = objIDs[k][j][i];
4900 objects.at(objID)->setObjectData(label, data);
4901 }
4902 }
4903 }
4904
4905 // Update value registry if caching is enabled (increment the new value for all objects)
4906 if (caching_enabled) {
4907 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>) {
4908 // Increment the new value once for each object that received it
4909 for (size_t i = 0; i < total_objects; ++i) {
4910 incrementObjectValueRegistry(label_str, data);
4911 }
4912 } else if constexpr (std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4913 // Increment the new value once for each object that received it
4914 for (size_t i = 0; i < total_objects; ++i) {
4915 incrementObjectValueRegistry(label_str, std::string(data));
4916 }
4917 }
4918 }
4919 }
4920
4922
4929 template<typename T>
4930 void setObjectData(uint objID, const char *label, const std::vector<T> &data) {
4931#ifdef HELIOS_DEBUG
4932 if (objects.find(objID) == objects.end()) {
4933 helios_runtime_error("ERROR (Context::setObjectData): Object ID of " + std::to_string(objID) + " does not exist in the Context.");
4934 }
4935#endif
4936 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4937 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4938 "Context::setObjectData() was called with an unsupported type.");
4939
4940 // Validate data type consistency and register/validate type
4941 HeliosDataType data_type;
4942 if constexpr (std::is_same_v<T, int>) {
4943 data_type = HELIOS_TYPE_INT;
4944 } else if constexpr (std::is_same_v<T, uint>) {
4945 data_type = HELIOS_TYPE_UINT;
4946 } else if constexpr (std::is_same_v<T, float>) {
4947 data_type = HELIOS_TYPE_FLOAT;
4948 } else if constexpr (std::is_same_v<T, double>) {
4949 data_type = HELIOS_TYPE_DOUBLE;
4950 } else if constexpr (std::is_same_v<T, vec2>) {
4951 data_type = HELIOS_TYPE_VEC2;
4952 } else if constexpr (std::is_same_v<T, vec3>) {
4953 data_type = HELIOS_TYPE_VEC3;
4954 } else if constexpr (std::is_same_v<T, vec4>) {
4955 data_type = HELIOS_TYPE_VEC4;
4956 } else if constexpr (std::is_same_v<T, int2>) {
4957 data_type = HELIOS_TYPE_INT2;
4958 } else if constexpr (std::is_same_v<T, int3>) {
4959 data_type = HELIOS_TYPE_INT3;
4960 } else if constexpr (std::is_same_v<T, int4>) {
4961 data_type = HELIOS_TYPE_INT4;
4962 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
4963 data_type = HELIOS_TYPE_STRING;
4964 }
4965 registerOrValidateObjectDataType<T>(label, data_type);
4966
4967 objects.at(objID)->setObjectData(label, data);
4968 }
4969
4971
4978 template<typename T>
4979 void setObjectData(const std::vector<uint> &objIDs, const char *label, const std::vector<T> &data) {
4980#ifdef HELIOS_DEBUG
4981 if (objIDs.size() != data.size()) {
4982 helios_runtime_error("ERROR (Context::setObjectData): Object IDs and data vectors must be the same size.");
4983 }
4984#endif
4985 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
4986 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
4987 "Context::setObjectData() was called with an unsupported type.");
4988
4989 // Validate data type consistency and register/validate type
4990 if (!objIDs.empty()) {
4991 HeliosDataType data_type;
4992 if constexpr (std::is_same_v<T, int>) {
4993 data_type = HELIOS_TYPE_INT;
4994 } else if constexpr (std::is_same_v<T, uint>) {
4995 data_type = HELIOS_TYPE_UINT;
4996 } else if constexpr (std::is_same_v<T, float>) {
4997 data_type = HELIOS_TYPE_FLOAT;
4998 } else if constexpr (std::is_same_v<T, double>) {
4999 data_type = HELIOS_TYPE_DOUBLE;
5000 } else if constexpr (std::is_same_v<T, vec2>) {
5001 data_type = HELIOS_TYPE_VEC2;
5002 } else if constexpr (std::is_same_v<T, vec3>) {
5003 data_type = HELIOS_TYPE_VEC3;
5004 } else if constexpr (std::is_same_v<T, vec4>) {
5005 data_type = HELIOS_TYPE_VEC4;
5006 } else if constexpr (std::is_same_v<T, int2>) {
5007 data_type = HELIOS_TYPE_INT2;
5008 } else if constexpr (std::is_same_v<T, int3>) {
5009 data_type = HELIOS_TYPE_INT3;
5010 } else if constexpr (std::is_same_v<T, int4>) {
5011 data_type = HELIOS_TYPE_INT4;
5012 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
5013 data_type = HELIOS_TYPE_STRING;
5014 }
5015 registerOrValidateObjectDataType<T>(label, data_type);
5016 }
5017
5018 for (uint objID: objIDs) {
5019 if (!objects.at(objID)->doesObjectDataExist(label)) {
5020 incrementObjectDataLabelCounter(label);
5021 }
5022 }
5023
5024#ifdef USE_OPENMP
5025#pragma omp parallel for
5026#endif
5027 for (int i = 0; i < (int) objIDs.size(); ++i) {
5028 objects.at(objIDs[i])->setObjectData(label, data[i]);
5029 }
5030 }
5031
5033
5040 void setObjectDataFromPrimitiveDataMean(uint objID, const std::string &label);
5041
5043
5049 template<typename T>
5050 void getObjectData(uint objID, const char *label, T &data) const {
5051#ifdef HELIOS_DEBUG
5052 if (objects.find(objID) == objects.end()) {
5053 helios_runtime_error("ERROR (Context::getObjectData): Object ID of " + std::to_string(objID) + " does not exist in the Context.");
5054 }
5055#endif
5056 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
5057 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
5058 "Context::getObjectData() was called with an unsupported type.");
5059 objects.at(objID)->getObjectData(label, data);
5060 }
5061
5063
5070 template<typename T>
5071 void getObjectData(uint objID, const char *label, std::vector<T> &data) const {
5072#ifdef HELIOS_DEBUG
5073 if (objects.find(objID) == objects.end()) {
5074 helios_runtime_error("ERROR (Context::getObjectData): Object ID of " + std::to_string(objID) + " does not exist in the Context.");
5075 }
5076#endif
5077 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
5078 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>,
5079 "Context::getObjectData() was called with an unsupported type.");
5080 objects.at(objID)->getObjectData(label, data);
5081 }
5082
5084
5090 [[deprecated]]
5091 HeliosDataType getObjectDataType(uint objID, const char *label) const;
5092
5094
5100 HeliosDataType getObjectDataType(const char *label) const;
5101
5103
5108 uint getObjectDataSize(uint objID, const char *label) const;
5109
5111
5116 bool doesObjectDataExist(uint objID, const char *label) const;
5117
5119
5123 void clearObjectData(uint objID, const char *label);
5124
5126
5130 void clearObjectData(const std::vector<uint> &objIDs, const char *label);
5131
5133
5137 void clearObjectData(const char *label);
5138
5140
5143 [[nodiscard]] std::vector<std::string> listAllObjectDataLabels() const;
5144
5146
5150 [[nodiscard]] bool areObjectPrimitivesComplete(uint objID) const;
5151
5153
5156 void cleanDeletedObjectIDs(std::vector<uint> &objIDs) const;
5157
5159
5162 void cleanDeletedObjectIDs(std::vector<std::vector<uint>> &objIDs) const;
5163
5165
5168 void cleanDeletedObjectIDs(std::vector<std::vector<std::vector<uint>>> &objIDs) const;
5169
5170 //-------- Global Data Methods ---------- //
5171
5173
5177 template<typename T>
5178 void setGlobalData(const char *label, const T &data) {
5179 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
5180 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *> || std::is_same_v<T, bool>,
5181 "Context::setGlobalData() was called with an unsupported type.");
5182
5183 globaldata[label].size = 1;
5184 if constexpr (std::is_same_v<T, int>) {
5185 globaldata[label].global_data_int = {data};
5186 globaldata[label].type = HELIOS_TYPE_INT;
5187 } else if constexpr (std::is_same_v<T, uint>) {
5188 globaldata[label].global_data_uint = {data};
5189 globaldata[label].type = HELIOS_TYPE_UINT;
5190 } else if constexpr (std::is_same_v<T, float>) {
5191 globaldata[label].global_data_float = {data};
5192 globaldata[label].type = HELIOS_TYPE_FLOAT;
5193 } else if constexpr (std::is_same_v<T, double>) {
5194 globaldata[label].global_data_double = {data};
5195 globaldata[label].type = HELIOS_TYPE_DOUBLE;
5196 } else if constexpr (std::is_same_v<T, vec2>) {
5197 globaldata[label].global_data_vec2 = {data};
5198 globaldata[label].type = HELIOS_TYPE_VEC2;
5199 } else if constexpr (std::is_same_v<T, vec3>) {
5200 globaldata[label].global_data_vec3 = {data};
5201 globaldata[label].type = HELIOS_TYPE_VEC3;
5202 } else if constexpr (std::is_same_v<T, vec4>) {
5203 globaldata[label].global_data_vec4 = {data};
5204 globaldata[label].type = HELIOS_TYPE_VEC4;
5205 } else if constexpr (std::is_same_v<T, int2>) {
5206 globaldata[label].global_data_int2 = {data};
5207 globaldata[label].type = HELIOS_TYPE_INT2;
5208 } else if constexpr (std::is_same_v<T, int3>) {
5209 globaldata[label].global_data_int3 = {data};
5210 globaldata[label].type = HELIOS_TYPE_INT3;
5211 } else if constexpr (std::is_same_v<T, int4>) {
5212 globaldata[label].global_data_int4 = {data};
5213 globaldata[label].type = HELIOS_TYPE_INT4;
5214 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
5215 globaldata[label].global_data_string = {data};
5216 globaldata[label].type = HELIOS_TYPE_STRING;
5217 } else if constexpr (std::is_same_v<T, bool>) {
5218 globaldata[label].global_data_bool = {data};
5219 globaldata[label].type = HELIOS_TYPE_BOOL;
5220 }
5221 globaldata[label].version++;
5222 }
5223
5225
5229 template<typename T>
5230 void setGlobalData(const char *label, const std::vector<T> &data) {
5231 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
5232 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *> || std::is_same_v<T, bool>,
5233 "Context::setGlobalData() was called with an unsupported type.");
5234
5235 globaldata[label].size = data.size();
5236 if constexpr (std::is_same_v<T, int>) {
5237 globaldata[label].global_data_int = data;
5238 globaldata[label].type = HELIOS_TYPE_INT;
5239 } else if constexpr (std::is_same_v<T, uint>) {
5240 globaldata[label].global_data_uint = data;
5241 globaldata[label].type = HELIOS_TYPE_UINT;
5242 } else if constexpr (std::is_same_v<T, float>) {
5243 globaldata[label].global_data_float = data;
5244 globaldata[label].type = HELIOS_TYPE_FLOAT;
5245 } else if constexpr (std::is_same_v<T, double>) {
5246 globaldata[label].global_data_double = data;
5247 globaldata[label].type = HELIOS_TYPE_DOUBLE;
5248 } else if constexpr (std::is_same_v<T, vec2>) {
5249 globaldata[label].global_data_vec2 = data;
5250 globaldata[label].type = HELIOS_TYPE_VEC2;
5251 } else if constexpr (std::is_same_v<T, vec3>) {
5252 globaldata[label].global_data_vec3 = data;
5253 globaldata[label].type = HELIOS_TYPE_VEC3;
5254 } else if constexpr (std::is_same_v<T, vec4>) {
5255 globaldata[label].global_data_vec4 = data;
5256 globaldata[label].type = HELIOS_TYPE_VEC4;
5257 } else if constexpr (std::is_same_v<T, int2>) {
5258 globaldata[label].global_data_int2 = data;
5259 globaldata[label].type = HELIOS_TYPE_INT2;
5260 } else if constexpr (std::is_same_v<T, int3>) {
5261 globaldata[label].global_data_int3 = data;
5262 globaldata[label].type = HELIOS_TYPE_INT3;
5263 } else if constexpr (std::is_same_v<T, int4>) {
5264 globaldata[label].global_data_int4 = data;
5265 globaldata[label].type = HELIOS_TYPE_INT4;
5266 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
5267 globaldata[label].global_data_string = data;
5268 globaldata[label].type = HELIOS_TYPE_STRING;
5269 } else if constexpr (std::is_same_v<T, bool>) {
5270 globaldata[label].global_data_bool = data;
5271 globaldata[label].type = HELIOS_TYPE_BOOL;
5272 }
5273 globaldata[label].version++;
5274 }
5275
5277
5281 template<typename T>
5282 void getGlobalData(const char *label, T &data) const {
5283 // Use SFINAE to detect if T is std::vector<U> for some U
5284 if constexpr (std::is_same_v<T, std::vector<int>>) {
5285 // Vector case for int
5286 if (globaldata.at(label).type == HELIOS_TYPE_INT) {
5287 data = globaldata.at(label).global_data_int;
5288 } else {
5289 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int, but data " + std::string(label) + " does not have type int.");
5290 }
5291 } else if constexpr (std::is_same_v<T, std::vector<uint>>) {
5292 // Vector case for uint
5293 if (globaldata.at(label).type == HELIOS_TYPE_UINT) {
5294 data = globaldata.at(label).global_data_uint;
5295 } else {
5296 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type uint, but data " + std::string(label) + " does not have type uint.");
5297 }
5298 } else if constexpr (std::is_same_v<T, std::vector<float>>) {
5299 // Vector case for float
5300 if (globaldata.at(label).type == HELIOS_TYPE_FLOAT) {
5301 data = globaldata.at(label).global_data_float;
5302 } else {
5303 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type float, but data " + std::string(label) + " does not have type float.");
5304 }
5305 } else if constexpr (std::is_same_v<T, std::vector<double>>) {
5306 // Vector case for double
5307 if (globaldata.at(label).type == HELIOS_TYPE_DOUBLE) {
5308 data = globaldata.at(label).global_data_double;
5309 } else {
5310 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type double, but data " + std::string(label) + " does not have type double.");
5311 }
5312 } else if constexpr (std::is_same_v<T, std::vector<vec2>>) {
5313 // Vector case for vec2
5314 if (globaldata.at(label).type == HELIOS_TYPE_VEC2) {
5315 data = globaldata.at(label).global_data_vec2;
5316 } else {
5317 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec2, but data " + std::string(label) + " does not have type vec2.");
5318 }
5319 } else if constexpr (std::is_same_v<T, std::vector<vec3>>) {
5320 // Vector case for vec3
5321 if (globaldata.at(label).type == HELIOS_TYPE_VEC3) {
5322 data = globaldata.at(label).global_data_vec3;
5323 } else {
5324 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec3, but data " + std::string(label) + " does not have type vec3.");
5325 }
5326 } else if constexpr (std::is_same_v<T, std::vector<vec4>>) {
5327 // Vector case for vec4
5328 if (globaldata.at(label).type == HELIOS_TYPE_VEC4) {
5329 data = globaldata.at(label).global_data_vec4;
5330 } else {
5331 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec4, but data " + std::string(label) + " does not have type vec4.");
5332 }
5333 } else if constexpr (std::is_same_v<T, std::vector<int2>>) {
5334 // Vector case for int2
5335 if (globaldata.at(label).type == HELIOS_TYPE_INT2) {
5336 data = globaldata.at(label).global_data_int2;
5337 } else {
5338 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int2, but data " + std::string(label) + " does not have type int2.");
5339 }
5340 } else if constexpr (std::is_same_v<T, std::vector<int3>>) {
5341 // Vector case for int3
5342 if (globaldata.at(label).type == HELIOS_TYPE_INT3) {
5343 data = globaldata.at(label).global_data_int3;
5344 } else {
5345 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int3, but data " + std::string(label) + " does not have type int3.");
5346 }
5347 } else if constexpr (std::is_same_v<T, std::vector<int4>>) {
5348 // Vector case for int4
5349 if (globaldata.at(label).type == HELIOS_TYPE_INT4) {
5350 data = globaldata.at(label).global_data_int4;
5351 } else {
5352 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int4, but data " + std::string(label) + " does not have type int4.");
5353 }
5354 } else if constexpr (std::is_same_v<T, std::vector<std::string>>) {
5355 // Vector case for string
5356 if (globaldata.at(label).type == HELIOS_TYPE_STRING) {
5357 data = globaldata.at(label).global_data_string;
5358 } else {
5359 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type string, but data " + std::string(label) + " does not have type string.");
5360 }
5361 } else if constexpr (std::is_same_v<T, int>) {
5362 // Scalar case for int
5363 if (globaldata.at(label).type == HELIOS_TYPE_INT) {
5364 data = globaldata.at(label).global_data_int.front();
5365 } else {
5366 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int, but data " + std::string(label) + " does not have type int.");
5367 }
5368 } else if constexpr (std::is_same_v<T, uint>) {
5369 // Scalar case for uint
5370 if (globaldata.at(label).type == HELIOS_TYPE_UINT) {
5371 data = globaldata.at(label).global_data_uint.front();
5372 } else {
5373 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type uint, but data " + std::string(label) + " does not have type uint.");
5374 }
5375 } else if constexpr (std::is_same_v<T, float>) {
5376 // Scalar case for float
5377 if (globaldata.at(label).type == HELIOS_TYPE_FLOAT) {
5378 data = globaldata.at(label).global_data_float.front();
5379 } else {
5380 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type float, but data " + std::string(label) + " does not have type float.");
5381 }
5382 } else if constexpr (std::is_same_v<T, double>) {
5383 // Scalar case for double
5384 if (globaldata.at(label).type == HELIOS_TYPE_DOUBLE) {
5385 data = globaldata.at(label).global_data_double.front();
5386 } else {
5387 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type double, but data " + std::string(label) + " does not have type double.");
5388 }
5389 } else if constexpr (std::is_same_v<T, vec2>) {
5390 // Scalar case for vec2
5391 if (globaldata.at(label).type == HELIOS_TYPE_VEC2) {
5392 data = globaldata.at(label).global_data_vec2.front();
5393 } else {
5394 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec2, but data " + std::string(label) + " does not have type vec2.");
5395 }
5396 } else if constexpr (std::is_same_v<T, vec3>) {
5397 // Scalar case for vec3
5398 if (globaldata.at(label).type == HELIOS_TYPE_VEC3) {
5399 data = globaldata.at(label).global_data_vec3.front();
5400 } else {
5401 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec3, but data " + std::string(label) + " does not have type vec3.");
5402 }
5403 } else if constexpr (std::is_same_v<T, vec4>) {
5404 // Scalar case for vec4
5405 if (globaldata.at(label).type == HELIOS_TYPE_VEC4) {
5406 data = globaldata.at(label).global_data_vec4.front();
5407 } else {
5408 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type vec4, but data " + std::string(label) + " does not have type vec4.");
5409 }
5410 } else if constexpr (std::is_same_v<T, int2>) {
5411 // Scalar case for int2
5412 if (globaldata.at(label).type == HELIOS_TYPE_INT2) {
5413 data = globaldata.at(label).global_data_int2.front();
5414 } else {
5415 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int2, but data " + std::string(label) + " does not have type int2.");
5416 }
5417 } else if constexpr (std::is_same_v<T, int3>) {
5418 // Scalar case for int3
5419 if (globaldata.at(label).type == HELIOS_TYPE_INT3) {
5420 data = globaldata.at(label).global_data_int3.front();
5421 } else {
5422 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int3, but data " + std::string(label) + " does not have type int3.");
5423 }
5424 } else if constexpr (std::is_same_v<T, int4>) {
5425 // Scalar case for int4
5426 if (globaldata.at(label).type == HELIOS_TYPE_INT4) {
5427 data = globaldata.at(label).global_data_int4.front();
5428 } else {
5429 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type int4, but data " + std::string(label) + " does not have type int4.");
5430 }
5431 } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
5432 // Scalar case for string
5433 if (globaldata.at(label).type == HELIOS_TYPE_STRING) {
5434 data = globaldata.at(label).global_data_string.front();
5435 } else {
5436 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type string, but data " + std::string(label) + " does not have type string.");
5437 }
5438 } else if constexpr (std::is_same_v<T, bool>) {
5439 // Scalar case for bool
5440 if (globaldata.at(label).type == HELIOS_TYPE_BOOL) {
5441 data = globaldata.at(label).global_data_bool.front();
5442 } else {
5443 helios_runtime_error("ERROR (Context::getGlobalData): Attempted to get data for type bool, but data " + std::string(label) + " does not have type bool.");
5444 }
5445 } else {
5446 static_assert(std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, vec2> || std::is_same_v<T, vec3> || std::is_same_v<T, vec4> || std::is_same_v<T, int2> ||
5447 std::is_same_v<T, int3> || std::is_same_v<T, int4> || std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *> ||
5448 std::is_same_v<T, std::vector<int>> || std::is_same_v<T, std::vector<uint>> || std::is_same_v<T, std::vector<float>> || std::is_same_v<T, std::vector<double>> || std::is_same_v<T, std::vector<vec2>> ||
5449 std::is_same_v<T, std::vector<vec3>> || std::is_same_v<T, std::vector<vec4>> || std::is_same_v<T, std::vector<int2>> || std::is_same_v<T, std::vector<int3>> || std::is_same_v<T, std::vector<int4>> ||
5450 std::is_same_v<T, std::vector<std::string>> || std::is_same_v<T, std::vector<bool>>,
5451 "CompoundObject::getGlobalData() was called with an unsupported type.");
5452 }
5453 }
5454
5456
5460 void renameGlobalData(const char *old_label, const char *new_label);
5461
5463
5467 void duplicateGlobalData(const char *old_label, const char *new_label);
5468
5470
5473 void clearGlobalData(const char *label);
5474
5476
5480 HeliosDataType getGlobalDataType(const char *label) const;
5481
5483
5487 size_t getGlobalDataSize(const char *label) const;
5488
5490
5495 uint64_t getGlobalDataVersion(const char *label) const;
5496
5498
5501 [[nodiscard]] std::vector<std::string> listGlobalData() const;
5502
5504
5508 bool doesGlobalDataExist(const char *label) const;
5509
5511
5516 void incrementGlobalData(const char *label, int increment);
5517
5519
5524 void incrementGlobalData(const char *label, uint increment);
5525
5527
5532 void incrementGlobalData(const char *label, float increment);
5533
5535
5540 void incrementGlobalData(const char *label, double increment);
5541
5542 //--------- Compound Objects Methods -------------//
5543
5545
5548 [[nodiscard]] uint getObjectCount() const;
5549
5551
5554 [[nodiscard]] bool doesObjectExist(uint ObjID) const;
5555
5557
5560 [[nodiscard]] std::vector<uint> getAllObjectIDs() const;
5561
5563
5566 void deleteObject(uint ObjID);
5567
5569
5572 void deleteObject(const std::vector<uint> &ObjIDs);
5573
5575
5579 uint copyObject(uint ObjID);
5580
5582
5586 std::vector<uint> copyObject(const std::vector<uint> &ObjIDs);
5587
5589
5593 void copyObjectData(uint source_objID, uint destination_objID);
5594
5596
5601 void duplicateObjectData(uint objID, const char *old_label, const char *new_label);
5602
5604
5609 void renameObjectData(uint objID, const char *old_label, const char *new_label);
5610
5612
5618 std::vector<uint> filterObjectsByData(const std::vector<uint> &ObjIDs, const char *object_data, float threshold, const char *comparator) const;
5619
5621
5625 void translateObject(uint ObjID, const vec3 &shift) const;
5626
5628
5632 void translateObject(const std::vector<uint> &ObjIDs, const vec3 &shift) const;
5633
5635
5640 void rotateObject(uint ObjID, float rotation_radians, const char *rotation_axis_xyz) const;
5641
5643
5648 void rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const char *rotation_axis_xyz) const;
5649
5651
5656 void rotateObject(uint ObjID, float rotation_radians, const vec3 &rotation_axis_vector) const;
5657
5659
5664 void rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_axis_vector) const;
5665
5667
5673 void rotateObject(uint ObjID, float rotation_radians, const vec3 &rotation_origin, const vec3 &rotation_axis_vector) const;
5674
5676
5682 void rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_origin, const vec3 &rotation_axis_vector) const;
5683
5685
5690 void rotateObjectAboutOrigin(uint ObjID, float rotation_radians, const vec3 &rotation_axis_vector) const;
5691
5693
5698 void rotateObjectAboutOrigin(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_axis_vector) const;
5699
5701
5705 void scaleObject(uint ObjID, const helios::vec3 &scalefact) const;
5706
5708
5712 void scaleObject(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const;
5713
5715
5719 void scaleObjectAboutCenter(uint ObjID, const helios::vec3 &scalefact) const;
5720
5722
5726 void scaleObjectAboutCenter(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const;
5727
5729
5734 void scaleObjectAboutPoint(uint ObjID, const helios::vec3 &scalefact, const helios::vec3 &point) const;
5735
5737
5742 void scaleObjectAboutPoint(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact, const helios::vec3 &point) const;
5743
5745
5750 void scaleObjectAboutOrigin(uint ObjID, const helios::vec3 &scalefact) const;
5751
5753
5758 void scaleObjectAboutOrigin(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const;
5759
5761
5764 [[nodiscard]] std::vector<uint> getObjectPrimitiveUUIDs(uint ObjID) const;
5765
5767
5770 [[nodiscard]] std::vector<uint> getObjectPrimitiveUUIDs(const std::vector<uint> &ObjIDs) const;
5771
5773
5776 [[nodiscard]] std::vector<uint> getObjectPrimitiveUUIDs(const std::vector<std::vector<uint>> &ObjIDs) const;
5777
5779
5782 [[nodiscard]] helios::ObjectType getObjectType(uint ObjID) const;
5783
5785
5788 [[nodiscard]] float getTileObjectAreaRatio(uint ObjID) const;
5789
5791
5794 [[nodiscard]] std::vector<float> getTileObjectAreaRatio(const std::vector<uint> &ObjIDs) const;
5795
5797
5802 void setTileObjectSubdivisionCount(const std::vector<uint> &ObjIDs, const int2 &new_subdiv);
5803
5805
5810 void setTileObjectSubdivisionCount(const std::vector<uint> &ObjIDs, float area_ratio);
5811
5813
5818 [[nodiscard]] helios::vec3 getTileObjectCenter(uint ObjID) const;
5819
5821
5824 [[nodiscard]] helios::vec2 getTileObjectSize(uint ObjID) const;
5825
5827
5830 [[nodiscard]] helios::int2 getTileObjectSubdivisionCount(uint ObjID) const;
5831
5833
5836 [[nodiscard]] helios::vec3 getTileObjectNormal(uint ObjID) const;
5837
5839
5842 [[nodiscard]] std::vector<helios::vec2> getTileObjectTextureUV(uint ObjID) const;
5843
5845
5848 [[nodiscard]] std::vector<helios::vec3> getTileObjectVertices(uint ObjID) const;
5849
5851
5854 [[nodiscard]] helios::vec3 getSphereObjectCenter(uint ObjID) const;
5855
5857
5860 [[nodiscard]] helios::vec3 getSphereObjectRadius(uint ObjID) const;
5861
5863
5866 [[nodiscard]] uint getSphereObjectSubdivisionCount(uint ObjID) const;
5867
5869
5872 [[nodiscard]] float getSphereObjectVolume(uint ObjID) const;
5873
5875
5878 [[nodiscard]] uint getTubeObjectSubdivisionCount(uint ObjID) const;
5879
5881
5884 [[nodiscard]] std::vector<helios::vec3> getTubeObjectNodes(uint ObjID) const;
5885
5887
5890 [[nodiscard]] uint getTubeObjectNodeCount(uint ObjID) const;
5891
5893
5896 [[nodiscard]] std::vector<float> getTubeObjectNodeRadii(uint ObjID) const;
5897
5899
5902 [[nodiscard]] std::vector<RGBcolor> getTubeObjectNodeColors(uint ObjID) const;
5903
5905
5908 [[nodiscard]] float getTubeObjectVolume(uint ObjID) const;
5909
5911
5915 [[nodiscard]] float getTubeObjectSegmentVolume(uint ObjID, uint segment_index) const;
5916
5918
5924 void appendTubeSegment(uint ObjID, const helios::vec3 &node_position, float radius, const RGBcolor &color);
5925
5927
5934 void appendTubeSegment(uint ObjID, const helios::vec3 &node_position, float node_radius, const char *texturefile, const helios::vec2 &textureuv_ufrac);
5935
5937
5941 void scaleTubeGirth(uint ObjID, float scale_factor);
5942
5944
5948 void setTubeRadii(uint ObjID, const std::vector<float> &node_radii);
5949
5951
5955 void scaleTubeLength(uint ObjID, float scale_factor);
5956
5958
5962 void pruneTubeNodes(uint ObjID, uint node_index);
5963
5965
5969 void setTubeNodes(uint ObjID, const std::vector<helios::vec3> &node_xyz);
5970
5972
5975 [[nodiscard]] helios::vec3 getBoxObjectCenter(uint ObjID) const;
5976
5978
5981 [[nodiscard]] helios::vec3 getBoxObjectSize(uint ObjID) const;
5982
5984
5987 [[nodiscard]] helios::int3 getBoxObjectSubdivisionCount(uint ObjID) const;
5988
5990
5993 [[nodiscard]] float getBoxObjectVolume(uint ObjID) const;
5994
5996
5999 [[nodiscard]] helios::vec3 getDiskObjectCenter(uint ObjID) const;
6000
6002
6005 [[nodiscard]] helios::vec2 getDiskObjectSize(uint ObjID) const;
6006
6008
6011 [[nodiscard]] uint getDiskObjectSubdivisionCount(uint ObjID) const;
6012
6014
6017 [[nodiscard]] float getPolymeshObjectVolume(uint ObjID) const;
6018
6020
6023 [[nodiscard]] uint getConeObjectSubdivisionCount(uint ObjID) const;
6024
6026
6029 [[nodiscard]] std::vector<helios::vec3> getConeObjectNodes(uint ObjID) const;
6030
6032
6035 [[nodiscard]] std::vector<float> getConeObjectNodeRadii(uint ObjID) const;
6036
6038
6042 [[nodiscard]] helios::vec3 getConeObjectNode(uint ObjID, int number) const;
6043
6045
6049 [[nodiscard]] float getConeObjectNodeRadius(uint ObjID, int number) const;
6050
6052
6055 [[nodiscard]] helios::vec3 getConeObjectAxisUnitVector(uint ObjID) const;
6056
6058
6062 [[nodiscard]] float getConeObjectLength(uint ObjID) const;
6063
6065
6068 [[nodiscard]] float getConeObjectVolume(uint ObjID) const;
6069
6071
6075 void scaleConeObjectLength(uint ObjID, float scale_factor);
6076
6078
6082 void scaleConeObjectGirth(uint ObjID, float scale_factor);
6083
6085
6094 uint addTileObject(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv);
6095
6097
6106 uint addTileObject(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const RGBcolor &color);
6107
6109
6118 uint addTileObject(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile);
6119
6121
6131 uint addTileObject(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile, const int2 &texture_repeat);
6132
6134
6142 uint addSphereObject(uint Ndivs, const vec3 &center, float radius);
6143
6145
6153 uint addSphereObject(uint Ndivs, const vec3 &center, float radius, const RGBcolor &color);
6154
6156
6164 uint addSphereObject(uint Ndivs, const vec3 &center, float radius, const char *texturefile);
6165
6167
6175 uint addSphereObject(uint Ndivs, const vec3 &center, const vec3 &radius);
6176
6178
6186 uint addSphereObject(uint Ndivs, const vec3 &center, const vec3 &radius, const RGBcolor &color);
6187
6189
6197 uint addSphereObject(uint Ndivs, const vec3 &center, const vec3 &radius, const char *texturefile);
6198
6200
6209 uint addTubeObject(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius);
6210
6212
6221 uint addTubeObject(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const std::vector<RGBcolor> &color);
6222
6224
6234 uint addTubeObject(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const char *texturefile);
6235
6237
6248 uint addTubeObject(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const char *texturefile, const std::vector<float> &textureuv_ufrac);
6249
6251
6260 uint addBoxObject(const vec3 &center, const vec3 &size, const int3 &subdiv);
6261
6263
6272 uint addBoxObject(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color);
6273
6275
6284 uint addBoxObject(const vec3 &center, const vec3 &size, const int3 &subdiv, const char *texturefile);
6285
6287
6297 uint addBoxObject(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color, bool reverse_normals);
6298
6300
6310 uint addBoxObject(vec3 center, const vec3 &size, const int3 &subdiv, const char *texturefile, bool reverse_normals);
6311
6313
6322 uint addDiskObject(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size);
6323
6325
6334 uint addDiskObject(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation);
6335
6337
6346 uint addDiskObject(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBcolor &color);
6347
6349
6358 uint addDiskObject(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBAcolor &color);
6359
6361
6371 uint addDiskObject(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texture_file);
6372
6374
6383 uint addDiskObject(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBcolor &color);
6384
6386
6395 uint addDiskObject(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBAcolor &color);
6396
6398
6408 uint addDiskObject(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texturefile);
6409
6411
6416 uint addPolymeshObject(const std::vector<uint> &UUIDs);
6417
6419
6430 uint addConeObject(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1);
6431
6433
6444 uint addConeObject(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, const RGBcolor &color);
6445
6447
6458 uint addConeObject(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, const char *texturefile);
6459
6461
6469 std::vector<uint> addSphere(uint Ndivs, const vec3 &center, float radius);
6470
6472
6480 std::vector<uint> addSphere(uint Ndivs, const vec3 &center, float radius, const RGBcolor &color);
6481
6483
6491 std::vector<uint> addSphere(uint Ndivs, const vec3 &center, float radius, const char *texturefile);
6492
6494
6503 std::vector<uint> addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv);
6504
6506
6515 std::vector<uint> addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const RGBcolor &color);
6516
6518
6527 std::vector<uint> addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile);
6528
6530
6540 std::vector<uint> addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile, const int2 &texture_repeat);
6541
6543
6552 std::vector<uint> addTube(uint Ndivs, const std::vector<vec3> &nodes, const std::vector<float> &radius);
6553
6555
6564 std::vector<uint> addTube(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const std::vector<RGBcolor> &color);
6565
6567
6576 std::vector<uint> addTube(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const char *texturefile);
6577
6579
6588 std::vector<uint> addBox(const vec3 &center, const vec3 &size, const int3 &subdiv);
6589
6591
6600 std::vector<uint> addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color);
6601
6603
6612 std::vector<uint> addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const char *texturefile);
6613
6615
6625 std::vector<uint> addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color, bool reverse_normals);
6626
6628
6638 std::vector<uint> addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const char *texturefile, bool reverse_normals);
6639
6641
6650 std::vector<uint> addDisk(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size);
6651
6653
6662 std::vector<uint> addDisk(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation);
6663
6665
6674 std::vector<uint> addDisk(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBcolor &color);
6675
6677
6686 std::vector<uint> addDisk(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBAcolor &color);
6687
6689
6699 std::vector<uint> addDisk(uint Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texture_file);
6700
6702
6711 std::vector<uint> addDisk(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBcolor &color);
6712
6714
6723 std::vector<uint> addDisk(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const helios::RGBAcolor &color);
6724
6726
6736 std::vector<uint> addDisk(const int2 &Ndivs, const helios::vec3 &center, const helios::vec2 &size, const helios::SphericalCoord &rotation, const char *texturefile);
6737
6739
6749 std::vector<uint> addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1);
6750
6752
6762 std::vector<uint> addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, RGBcolor &color);
6763
6765
6775 std::vector<uint> addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, const char *texturefile);
6776
6778
6785 void addTimeseriesData(const char *label, float value, const Date &date, const Time &time);
6786
6788
6796 void updateTimeseriesData(const char *label, const Date &date, const Time &time, float new_value);
6797
6799
6804 void setCurrentTimeseriesPoint(const char *label, uint index);
6805
6807
6814 float queryTimeseriesData(const char *label, const Date &date, const Time &time) const;
6815
6817
6822 float queryTimeseriesData(const char *label) const;
6823
6825
6831 float queryTimeseriesData(const char *label, uint index) const;
6832
6834
6840 Time queryTimeseriesTime(const char *label, uint index) const;
6841
6843
6849 Date queryTimeseriesDate(const char *label, uint index) const;
6850
6852
6856 uint getTimeseriesLength(const char *label) const;
6857
6859
6863 bool doesTimeseriesVariableExist(const char *label) const;
6864
6866 [[nodiscard]] std::vector<std::string> listTimeseriesVariables() const;
6867
6869
6870 void clearTimeseriesData();
6871
6873
6878 void deleteTimeseriesVariable(const char *label);
6879
6881
6888 void deleteTimeseriesDataPoint(const char *label, const Date &date, const Time &time);
6889
6891
6897 void deleteTimeseriesDataPoint(const Date &date, const Time &time);
6898
6900
6925 void loadTabularTimeseriesData(const std::string &data_file, const std::vector<std::string> &column_labels, const std::string &delimiter, const std::string &date_string_format = "YYYYMMDD", uint headerlines = 0);
6926
6928
6933 void getDomainBoundingBox(helios::vec2 &xbounds, helios::vec2 &ybounds, helios::vec2 &zbounds) const;
6934
6936
6942 void getDomainBoundingBox(const std::vector<uint> &UUIDs, helios::vec2 &xbounds, helios::vec2 &ybounds, helios::vec2 &zbounds) const;
6943
6945
6949 void getDomainBoundingSphere(helios::vec3 &center, float &radius) const;
6950
6952
6957 void getDomainBoundingSphere(const std::vector<uint> &UUIDs, helios::vec3 &center, float &radius) const;
6958
6960
6963 void cropDomainX(const vec2 &xbounds);
6964
6966
6969 void cropDomainY(const vec2 &ybounds);
6970
6972
6975 void cropDomainZ(const vec2 &zbounds);
6976
6978
6984 void cropDomain(std::vector<uint> &UUIDs, const vec2 &xbounds, const vec2 &ybounds, const vec2 &zbounds);
6985
6987
6992 void cropDomain(const vec2 &xbounds, const vec2 &ybounds, const vec2 &zbounds);
6993
6995
7001 void colorPrimitiveByDataPseudocolor(const std::vector<uint> &UUIDs, const std::string &primitive_data, const std::string &colormap, uint Ncolors);
7002
7004
7012 void colorPrimitiveByDataPseudocolor(const std::vector<uint> &UUIDs, const std::string &primitive_data, const std::string &colormap, uint Ncolors, float data_min, float data_max);
7013
7015
7021 std::vector<uint> loadXML(const char *filename, bool quiet = false);
7022
7024
7027 [[nodiscard]] std::vector<std::string> getLoadedXMLFiles();
7028
7030
7036 static bool scanXMLForTag(const std::string &filename, const std::string &tag, const std::string &label = "");
7037
7039
7043 void writeXML(const char *filename, bool quiet = false) const;
7044
7046
7051 void writeXML(const char *filename, const std::vector<uint> &UUIDs, bool quiet = false) const;
7052
7054
7059 void writeXML_byobject(const char *filename, const std::vector<uint> &UUIDs, bool quiet = false) const;
7060
7062
7067 void writePrimitiveData(const std::string &filename, const std::vector<std::string> &column_format, bool print_header = false) const;
7068
7070
7076 void writePrimitiveData(const std::string &filename, const std::vector<std::string> &column_format, const std::vector<uint> &UUIDs, bool print_header = false) const;
7077
7079
7085 std::vector<uint> loadPLY(const char *filename, bool silent = false);
7086
7088
7097 std::vector<uint> loadPLY(const char *filename, const vec3 &origin, float height, const std::string &upaxis = "YUP", bool silent = false);
7098
7100
7110 std::vector<uint> loadPLY(const char *filename, const vec3 &origin, float height, const SphericalCoord &rotation, const std::string &upaxis = "YUP", bool silent = false);
7111
7113
7122 std::vector<uint> loadPLY(const char *filename, const vec3 &origin, float height, const RGBcolor &default_color, const std::string &upaxis = "YUP", bool silent = false);
7123
7125
7135 std::vector<uint> loadPLY(const char *filename, const vec3 &origin, float height, const SphericalCoord &rotation, const RGBcolor &default_color, const std::string &upaxis = "YUP", bool silent = false);
7136
7138
7141 void writePLY(const char *filename) const;
7142
7144
7148 void writePLY(const char *filename, const std::vector<uint> &UUIDs) const;
7149
7150 // Asset directory registration removed - now using HELIOS_BUILD resolution
7151
7153
7157 std::vector<uint> loadOBJ(const char *filename, bool silent = false);
7158
7160
7169 std::vector<uint> loadOBJ(const char *filename, const vec3 &origin, float height, const SphericalCoord &rotation, const RGBcolor &default_color, bool silent = false);
7170
7172
7182 std::vector<uint> loadOBJ(const char *filename, const vec3 &origin, float height, const SphericalCoord &rotation, const RGBcolor &default_color, const char *upaxis, bool silent = false);
7183
7184
7186
7196 std::vector<uint> loadOBJ(const char *filename, const vec3 &origin, const helios::vec3 &scale, const SphericalCoord &rotation, const RGBcolor &default_color, const char *upaxis, bool silent = false);
7197
7199
7204 void writeOBJ(const std::string &filename, bool write_normals = false, bool silent = false) const;
7205
7207
7213 void writeOBJ(const std::string &filename, const std::vector<uint> &UUIDs, bool write_normals = false, bool silent = false) const;
7214
7216
7223 void writeOBJ(const std::string &filename, const std::vector<uint> &UUIDs, const std::vector<std::string> &primitive_dat_fields, bool write_normals = false, bool silent = false) const;
7224
7225 //------------ FILE PATH RESOLUTION ----------------//
7226
7228
7233 std::filesystem::path resolveFilePath(const std::string &filename) const;
7234
7236
7242 void setDate(int day, int month, int year);
7243
7245
7249 void setDate(const Date &date);
7250
7252
7257 void setDate(int Julian_day, int year);
7258
7260
7264 [[nodiscard]] helios::Date getDate() const;
7265
7267
7271 [[nodiscard]] const char *getMonthString() const;
7272
7274
7278 [[nodiscard]] int getJulianDate() const;
7279
7281
7287 void setTime(int minute, int hour);
7288
7290
7297 void setTime(int second, int minute, int hour);
7298
7300
7305 void setTime(const Time &time);
7306
7308
7312 [[nodiscard]] helios::Time getTime() const;
7313
7315
7318 void setLocation(const helios::Location &location);
7319
7321
7324 [[nodiscard]] helios::Location getLocation() const;
7325
7327
7330 float randu();
7331
7333
7338 float randu(float min, float max);
7339
7341
7346 int randu(int min, int max);
7347
7349
7352 float randn();
7353
7355
7360 float randn(float mean, float stddev);
7361
7363
7367 void duplicatePrimitiveData(const char *existing_data_label, const char *copy_data_label);
7368
7370
7375 void calculatePrimitiveDataMean(const std::vector<uint> &UUIDs, const std::string &label, float &mean) const;
7376
7378
7383 void calculatePrimitiveDataMean(const std::vector<uint> &UUIDs, const std::string &label, double &mean) const;
7384
7386
7391 void calculatePrimitiveDataMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec2 &mean) const;
7392
7394
7399 void calculatePrimitiveDataMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec3 &mean) const;
7400
7402
7407 void calculatePrimitiveDataMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec4 &mean) const;
7408
7410
7415 void calculatePrimitiveDataAreaWeightedMean(const std::vector<uint> &UUIDs, const std::string &label, float &awt_mean) const;
7416
7418
7423 void calculatePrimitiveDataAreaWeightedMean(const std::vector<uint> &UUIDs, const std::string &label, double &awt_mean) const;
7424
7426
7431 void calculatePrimitiveDataAreaWeightedMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec2 &awt_mean) const;
7432
7434
7439 void calculatePrimitiveDataAreaWeightedMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec3 &awt_mean) const;
7440
7442
7447 void calculatePrimitiveDataAreaWeightedMean(const std::vector<uint> &UUIDs, const std::string &label, helios::vec4 &awt_mean) const;
7448
7450
7455 void calculatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::string &label, float &sum) const;
7456
7458
7463 void calculatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::string &label, double &sum) const;
7464
7466
7471 void calculatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec2 &sum) const;
7472
7474
7479 void calculatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec3 &sum) const;
7480
7482
7487 void calculatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec4 &sum) const;
7488
7490
7495 void calculatePrimitiveDataAreaWeightedSum(const std::vector<uint> &UUIDs, const std::string &label, float &awt_sum) const;
7496
7498
7503 void calculatePrimitiveDataAreaWeightedSum(const std::vector<uint> &UUIDs, const std::string &label, double &sum) const;
7504
7506
7511 void calculatePrimitiveDataAreaWeightedSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec2 &sum) const;
7512
7514
7519 void calculatePrimitiveDataAreaWeightedSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec3 &sum) const;
7520
7522
7527 void calculatePrimitiveDataAreaWeightedSum(const std::vector<uint> &UUIDs, const std::string &label, helios::vec4 &sum) const;
7528
7530
7536 void scalePrimitiveData(const std::vector<uint> &UUIDs, const std::string &label, float scaling_factor);
7537
7539
7544 void scalePrimitiveData(const std::string &label, float scaling_factor);
7545
7547
7553 void incrementPrimitiveData(const std::vector<uint> &UUIDs, const char *label, int increment);
7554
7556
7562 void incrementPrimitiveData(const std::vector<uint> &UUIDs, const char *label, uint increment);
7563
7565
7571 void incrementPrimitiveData(const std::vector<uint> &UUIDs, const char *label, float increment);
7572
7574
7580 void incrementPrimitiveData(const std::vector<uint> &UUIDs, const char *label, double increment);
7581
7583
7589 void aggregatePrimitiveDataSum(const std::vector<uint> &UUIDs, const std::vector<std::string> &primitive_data_labels, const std::string &result_primitive_data_label);
7590
7592
7598 void aggregatePrimitiveDataProduct(const std::vector<uint> &UUIDs, const std::vector<std::string> &primitive_data_labels, const std::string &result_primitive_data_label);
7599
7601
7605 [[nodiscard]] float sumPrimitiveSurfaceArea(const std::vector<uint> &UUIDs) const;
7606
7608
7616 [[nodiscard]] std::vector<uint> filterPrimitivesByData(const std::vector<uint> &UUIDs, const std::string &primitive_data_label, float filter_value, const std::string &comparator) const;
7617
7619
7627 [[nodiscard]] std::vector<uint> filterPrimitivesByData(const std::vector<uint> &UUIDs, const std::string &primitive_data_label, double filter_value, const std::string &comparator) const;
7628
7630
7638 [[nodiscard]] std::vector<uint> filterPrimitivesByData(const std::vector<uint> &UUIDs, const std::string &primitive_data_label, int filter_value, const std::string &comparator) const;
7639
7641
7649 [[nodiscard]] std::vector<uint> filterPrimitivesByData(const std::vector<uint> &UUIDs, const std::string &primitive_data_label, uint filter_value, const std::string &comparator) const;
7650
7652
7659 [[nodiscard]] std::vector<uint> filterPrimitivesByData(const std::vector<uint> &UUIDs, const std::string &primitive_data_label, const std::string &filter_value) const;
7660
7662
7670 [[nodiscard]] std::vector<uint> filterObjectsByData(const std::vector<uint> &objIDs, const std::string &object_data_label, float filter_value, const std::string &comparator) const;
7671
7673
7681 [[nodiscard]] std::vector<uint> filterObjectsByData(const std::vector<uint> &objIDs, const std::string &object_data_label, double filter_value, const std::string &comparator) const;
7682
7684
7692 [[nodiscard]] std::vector<uint> filterObjectsByData(const std::vector<uint> &objIDs, const std::string &object_data_label, int filter_value, const std::string &comparator) const;
7693
7695
7703 [[nodiscard]] std::vector<uint> filterObjectsByData(const std::vector<uint> &objIDs, const std::string &object_data_label, uint filter_value, const std::string &comparator) const;
7704
7706
7713 [[nodiscard]] std::vector<uint> filterObjectsByData(const std::vector<uint> &objIDs, const std::string &object_data_label, const std::string &filter_value) const;
7714
7716
7726 std::vector<std::string> generateTexturesFromColormap(const std::string &texturefile, const std::vector<RGBcolor> &colormap_data);
7727
7729
7734 std::vector<RGBcolor> generateColormap(const std::string &colormap, uint Ncolors);
7735
7737
7744 std::vector<RGBcolor> generateColormap(const std::vector<helios::RGBcolor> &ctable, const std::vector<float> &cfrac, uint Ncolors);
7745
7746 // ---------- Template method implementations for data type consistency ---------- //
7747
7748 template<typename T>
7749 void storeDataWithTypeCasting(uint UUID, const char *label, const T &data, HeliosDataType target_type) {
7750 // Only cast between numeric scalar types
7751 if constexpr (std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double>) {
7752 if (target_type == HELIOS_TYPE_INT) {
7753 primitives.at(UUID)->setPrimitiveData(label, static_cast<int>(data));
7754 } else if (target_type == HELIOS_TYPE_UINT) {
7755 primitives.at(UUID)->setPrimitiveData(label, static_cast<uint>(data));
7756 } else if (target_type == HELIOS_TYPE_FLOAT) {
7757 primitives.at(UUID)->setPrimitiveData(label, static_cast<float>(data));
7758 } else if (target_type == HELIOS_TYPE_DOUBLE) {
7759 primitives.at(UUID)->setPrimitiveData(label, static_cast<double>(data));
7760 } else {
7761 // Fallback: store as original type
7762 primitives.at(UUID)->setPrimitiveData(label, data);
7763 }
7764 } else {
7765 // For non-numeric types, store as-is
7766 primitives.at(UUID)->setPrimitiveData(label, data);
7767 }
7768 }
7769
7770 template<typename T>
7771 void storeObjectDataWithTypeCasting(uint objID, const char *label, const T &data, HeliosDataType target_type) {
7772 // Only cast between numeric scalar types
7773 if constexpr (std::is_same_v<T, int> || std::is_same_v<T, uint> || std::is_same_v<T, float> || std::is_same_v<T, double>) {
7774 if (target_type == HELIOS_TYPE_INT) {
7775 objects.at(objID)->setObjectData(label, static_cast<int>(data));
7776 } else if (target_type == HELIOS_TYPE_UINT) {
7777 objects.at(objID)->setObjectData(label, static_cast<uint>(data));
7778 } else if (target_type == HELIOS_TYPE_FLOAT) {
7779 objects.at(objID)->setObjectData(label, static_cast<float>(data));
7780 } else if (target_type == HELIOS_TYPE_DOUBLE) {
7781 objects.at(objID)->setObjectData(label, static_cast<double>(data));
7782 } else {
7783 // Fallback: store as original type
7784 objects.at(objID)->setObjectData(label, data);
7785 }
7786 } else {
7787 // For non-numeric types, store as-is
7788 objects.at(objID)->setObjectData(label, data);
7789 }
7790 }
7791
7792 template<typename T>
7793 HeliosDataType registerOrValidatePrimitiveDataType(const std::string &label, HeliosDataType data_type) {
7794 auto it = primitive_data_type_registry.find(label);
7795 if (it == primitive_data_type_registry.end()) {
7796 // First time this label is used - register the type
7797 primitive_data_type_registry[label] = data_type;
7798 return data_type;
7799 } else {
7800 // Label exists - check for type consistency
7801 HeliosDataType expected_type = it->second;
7802 if (expected_type != data_type) {
7803 // Types don't match - check if casting is possible
7804 if (!isTypeCastingSupported(data_type, expected_type)) {
7805 helios_runtime_error("ERROR (Context::setPrimitiveData): Primitive data '" + label + "' already exists with type '" + dataTypeToString(expected_type) + "', so it cannot be assigned a value with type '" +
7806 dataTypeToString(data_type) + "'. To assign a value of a different type, first remove the existing data from every primitive by calling Context::clearPrimitiveData(\"" + label + "\").");
7807 } else {
7808 std::cerr << "WARNING (Context::setPrimitiveData): Primitive data '" + label + "' already exists with type '" + dataTypeToString(expected_type) + "'; the assigned value of type '" + dataTypeToString(data_type) +
7809 "' will be cast to '" + dataTypeToString(expected_type) + "'. Consider using consistent types."
7810 << std::endl;
7811 }
7812 }
7813 return expected_type;
7814 }
7815 }
7816
7817 template<typename T>
7818 HeliosDataType registerOrValidateObjectDataType(const std::string &label, HeliosDataType data_type) {
7819 auto it = object_data_type_registry.find(label);
7820 if (it == object_data_type_registry.end()) {
7821 // First time this label is used - register the type
7822 object_data_type_registry[label] = data_type;
7823 return data_type;
7824 } else {
7825 // Label exists - check for type consistency
7826 HeliosDataType expected_type = it->second;
7827 if (expected_type != data_type) {
7828 // Types don't match - check if casting is possible
7829 if (!isTypeCastingSupported(data_type, expected_type)) {
7830 helios_runtime_error("ERROR (Context::setObjectData): Object data '" + label + "' already exists with type '" + dataTypeToString(expected_type) + "', so it cannot be assigned a value with type '" +
7831 dataTypeToString(data_type) + "'. To assign a value of a different type, first remove the existing data from every object by calling Context::clearObjectData(\"" + label + "\").");
7832 } else {
7833 std::cerr << "WARNING (Context::setObjectData): Object data '" + label + "' already exists with type '" + dataTypeToString(expected_type) + "'; the assigned value of type '" + dataTypeToString(data_type) +
7834 "' will be cast to '" + dataTypeToString(expected_type) + "'. Consider using consistent types."
7835 << std::endl;
7836 }
7837 }
7838 return expected_type;
7839 }
7840 }
7841
7842 //----------- TEMPLATE METHOD IMPLEMENTATIONS FOR VALUE REGISTRY ----------//
7843
7845 template<typename T>
7846 void incrementPrimitiveValueRegistry(const std::string &label, const T &value) {
7847 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
7848 std::string string_value = (std::is_same_v<T, std::string>) ? value : std::string(value);
7849 primitive_string_value_registry[label][string_value]++;
7850 } else if constexpr (std::is_same_v<T, int>) {
7851 primitive_int_value_registry[label][value]++;
7852 } else if constexpr (std::is_same_v<T, uint>) {
7853 primitive_uint_value_registry[label][value]++;
7854 }
7855 }
7856
7858 template<typename T>
7859 void decrementPrimitiveValueRegistry(const std::string &label, const T &value) {
7860 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
7861 std::string string_value = (std::is_same_v<T, std::string>) ? value : std::string(value);
7862 auto label_it = primitive_string_value_registry.find(label);
7863 if (label_it != primitive_string_value_registry.end()) {
7864 auto value_it = label_it->second.find(string_value);
7865 if (value_it != label_it->second.end() && value_it->second > 0) {
7866 value_it->second--;
7867 if (value_it->second == 0) {
7868 label_it->second.erase(value_it);
7869 if (label_it->second.empty()) {
7870 primitive_string_value_registry.erase(label_it);
7871 }
7872 }
7873 }
7874 }
7875 } else if constexpr (std::is_same_v<T, int>) {
7876 auto label_it = primitive_int_value_registry.find(label);
7877 if (label_it != primitive_int_value_registry.end()) {
7878 auto value_it = label_it->second.find(value);
7879 if (value_it != label_it->second.end() && value_it->second > 0) {
7880 value_it->second--;
7881 if (value_it->second == 0) {
7882 label_it->second.erase(value_it);
7883 if (label_it->second.empty()) {
7884 primitive_int_value_registry.erase(label_it);
7885 }
7886 }
7887 }
7888 }
7889 } else if constexpr (std::is_same_v<T, uint>) {
7890 auto label_it = primitive_uint_value_registry.find(label);
7891 if (label_it != primitive_uint_value_registry.end()) {
7892 auto value_it = label_it->second.find(value);
7893 if (value_it != label_it->second.end() && value_it->second > 0) {
7894 value_it->second--;
7895 if (value_it->second == 0) {
7896 label_it->second.erase(value_it);
7897 if (label_it->second.empty()) {
7898 primitive_uint_value_registry.erase(label_it);
7899 }
7900 }
7901 }
7902 }
7903 }
7904 }
7905
7907 template<typename T>
7908 void incrementObjectValueRegistry(const std::string &label, const T &value) {
7909 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
7910 std::string string_value = (std::is_same_v<T, std::string>) ? value : std::string(value);
7911 object_string_value_registry[label][string_value]++;
7912 } else if constexpr (std::is_same_v<T, int>) {
7913 object_int_value_registry[label][value]++;
7914 } else if constexpr (std::is_same_v<T, uint>) {
7915 object_uint_value_registry[label][value]++;
7916 }
7917 }
7918
7920 template<typename T>
7921 void decrementObjectValueRegistry(const std::string &label, const T &value) {
7922 if constexpr (std::is_same_v<T, std::string> || std::is_same_v<std::decay_t<T>, const char *> || std::is_same_v<std::decay_t<T>, char *>) {
7923 std::string string_value = (std::is_same_v<T, std::string>) ? value : std::string(value);
7924 auto label_it = object_string_value_registry.find(label);
7925 if (label_it != object_string_value_registry.end()) {
7926 auto value_it = label_it->second.find(string_value);
7927 if (value_it != label_it->second.end() && value_it->second > 0) {
7928 value_it->second--;
7929 if (value_it->second == 0) {
7930 label_it->second.erase(value_it);
7931 if (label_it->second.empty()) {
7932 object_string_value_registry.erase(label_it);
7933 }
7934 }
7935 }
7936 }
7937 } else if constexpr (std::is_same_v<T, int>) {
7938 auto label_it = object_int_value_registry.find(label);
7939 if (label_it != object_int_value_registry.end()) {
7940 auto value_it = label_it->second.find(value);
7941 if (value_it != label_it->second.end() && value_it->second > 0) {
7942 value_it->second--;
7943 if (value_it->second == 0) {
7944 label_it->second.erase(value_it);
7945 if (label_it->second.empty()) {
7946 object_int_value_registry.erase(label_it);
7947 }
7948 }
7949 }
7950 }
7951 } else if constexpr (std::is_same_v<T, uint>) {
7952 auto label_it = object_uint_value_registry.find(label);
7953 if (label_it != object_uint_value_registry.end()) {
7954 auto value_it = label_it->second.find(value);
7955 if (value_it != label_it->second.end() && value_it->second > 0) {
7956 value_it->second--;
7957 if (value_it->second == 0) {
7958 label_it->second.erase(value_it);
7959 if (label_it->second.empty()) {
7960 object_uint_value_registry.erase(label_it);
7961 }
7962 }
7963 }
7964 }
7965 }
7966 }
7967
7968 //----------- UNIQUE VALUE QUERY METHODS ----------//
7969
7971
7977 template<typename T>
7978 void getUniquePrimitiveDataValues(const std::string &label, std::vector<T> &values) const {
7979 static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>, "getUniquePrimitiveDataValues() only supports std::string, int, and uint types.");
7980
7982 helios_runtime_error("ERROR (Context::getUniquePrimitiveDataValues): Value-level caching is not enabled for primitive data label '" + label + "'. Use enablePrimitiveDataValueCaching() first.");
7983 }
7984
7985 values.clear();
7986
7987 if constexpr (std::is_same_v<T, std::string>) {
7988 auto it = primitive_string_value_registry.find(label);
7989 if (it != primitive_string_value_registry.end()) {
7990 values.reserve(it->second.size());
7991 for (const auto &[value, count]: it->second) {
7992 if (count > 0) {
7993 values.push_back(value);
7994 }
7995 }
7996 }
7997 } else if constexpr (std::is_same_v<T, int>) {
7998 auto it = primitive_int_value_registry.find(label);
7999 if (it != primitive_int_value_registry.end()) {
8000 values.reserve(it->second.size());
8001 for (const auto &[value, count]: it->second) {
8002 if (count > 0) {
8003 values.push_back(value);
8004 }
8005 }
8006 }
8007 } else if constexpr (std::is_same_v<T, uint>) {
8008 auto it = primitive_uint_value_registry.find(label);
8009 if (it != primitive_uint_value_registry.end()) {
8010 values.reserve(it->second.size());
8011 for (const auto &[value, count]: it->second) {
8012 if (count > 0) {
8013 values.push_back(value);
8014 }
8015 }
8016 }
8017 }
8018 }
8019
8021
8027 template<typename T>
8028 void getUniqueObjectDataValues(const std::string &label, std::vector<T> &values) const {
8029 static_assert(std::is_same_v<T, std::string> || std::is_same_v<T, int> || std::is_same_v<T, uint>, "getUniqueObjectDataValues() only supports std::string, int, and uint types.");
8030
8031 if (!isObjectDataValueCachingEnabled(label)) {
8032 helios_runtime_error("ERROR (Context::getUniqueObjectDataValues): Value-level caching is not enabled for object data label '" + label + "'. Use enableObjectDataValueCaching() first.");
8033 }
8034
8035 values.clear();
8036
8037 if constexpr (std::is_same_v<T, std::string>) {
8038 auto it = object_string_value_registry.find(label);
8039 if (it != object_string_value_registry.end()) {
8040 values.reserve(it->second.size());
8041 for (const auto &[value, count]: it->second) {
8042 if (count > 0) {
8043 values.push_back(value);
8044 }
8045 }
8046 }
8047 } else if constexpr (std::is_same_v<T, int>) {
8048 auto it = object_int_value_registry.find(label);
8049 if (it != object_int_value_registry.end()) {
8050 values.reserve(it->second.size());
8051 for (const auto &[value, count]: it->second) {
8052 if (count > 0) {
8053 values.push_back(value);
8054 }
8055 }
8056 }
8057 } else if constexpr (std::is_same_v<T, uint>) {
8058 auto it = object_uint_value_registry.find(label);
8059 if (it != object_uint_value_registry.end()) {
8060 values.reserve(it->second.size());
8061 for (const auto &[value, count]: it->second) {
8062 if (count > 0) {
8063 values.push_back(value);
8064 }
8065 }
8066 }
8067 }
8068 }
8069 };
8070
8071} // namespace helios
8072
8073
8074#endif