1.3.49
 
Loading...
Searching...
No Matches
Context.cpp
Go to the documentation of this file.
1
16#include "Context.h"
17
18using namespace helios;
19
21
22 install_out_of_memory_handler();
23
24 //---- ALL DEFAULT VALUES ARE SET HERE ----//
25
26 sim_date = make_Date(1, 6, 2000);
27
28 sim_time = make_Time(12, 0);
29
30 sim_location = make_Location(38.55, 121.76, 8);
31
32 // --- Initialize random number generator ---- //
33
34 unsigned seed = std::chrono::system_clock::now().time_since_epoch().count();
35 generator.seed(seed);
36
37 // --- Set Geometry as `Clean' --- //
38
39 currentUUID = 0;
40
41 currentObjectID = 1; // object ID of 0 is reserved for default object
42}
43
45 generator.seed(seed);
46}
47
48std::minstd_rand0 *Context::getRandomGenerator() {
49 return &generator;
50}
51
52// Asset directory registration system removed - now using HELIOS_BUILD resolution
53
54std::filesystem::path Context::resolveFilePath(const std::string &filename) const {
55 // Use the global helios::resolveFilePath function which implements HELIOS_BUILD resolution
56 return helios::resolveFilePath(filename);
57}
58
59void Context::addTexture(const char *texture_file) {
60 if (textures.find(texture_file) == textures.end()) { // texture has not already been added
61
62 // texture must have type PNG or JPEG
63 const std::string &fn = texture_file;
64 const std::string &ext = getFileExtension(fn);
65 if (ext != ".png" && ext != ".PNG" && ext != ".jpg" && ext != ".jpeg" && ext != ".JPG" && ext != ".JPEG") {
66 helios_runtime_error("ERROR (Context::addTexture): Texture file " + fn + " is not PNG or JPEG format.");
67 } else if (!doesTextureFileExist(texture_file)) {
68 helios_runtime_error("ERROR (Context::addTexture): Texture file " + std::string(texture_file) + " does not exist.");
69 }
70
71 // Use unified path resolution
72 auto resolved_path = resolveFilePath(texture_file);
73 textures.emplace(texture_file, Texture(resolved_path.string().c_str()));
74 }
75}
76
77bool Context::doesTextureFileExist(const char *texture_file) const {
78 try {
79 auto resolved_path = resolveFilePath(texture_file);
80 return std::filesystem::exists(resolved_path);
81 } catch (const std::runtime_error &) {
82 return false;
83 }
84}
85
86bool Context::validateTextureFileExtenstion(const char *texture_file) const {
87 const std::string &fn = texture_file;
88 const std::string &ext = getFileExtension(fn);
89 if (ext != ".png" && ext != ".PNG" && ext != ".jpg" && ext != ".jpeg" && ext != ".JPG" && ext != ".JPEG") {
90 return false;
91 } else {
92 return true;
93 }
94}
95
96Texture::Texture(const char *texture_file) {
97 filename = texture_file;
98
99 //------ determine if transparency channel exists ---------//
100
101 // check if texture file has extension ".png"
102 const std::string &ext = getFileExtension(filename);
103 if (ext != ".png") {
104 hastransparencychannel = false;
105 } else {
106 hastransparencychannel = PNGHasAlpha(filename.c_str());
107 }
108
109 //-------- load transparency channel (if exists) ------------//
110
111 if (ext == ".png") {
112 transparencydata = readPNGAlpha(filename);
113 image_resolution = make_int2(int(transparencydata.front().size()), int(transparencydata.size()));
114 } else {
115 image_resolution = getImageResolutionJPEG(texture_file);
116 }
117
118 //-------- determine solid fraction --------------//
119
120 if (hastransparencychannel) {
121 size_t p = 0.f;
122 for (auto &j: transparencydata) {
123 for (bool transparency: j) {
124 if (transparency) {
125 p += 1;
126 }
127 }
128 }
129 float sf = float(p) / float(transparencydata.size() * transparencydata.front().size());
130 if (std::isnan(sf)) {
131 sf = 0.f;
132 }
133 solidfraction = sf;
134 } else {
135 solidfraction = 1.f;
136 }
137}
138
139std::string Texture::getTextureFile() const {
140 return filename;
141}
142
144 return image_resolution;
145}
146
148 return hastransparencychannel;
149}
150
151const std::vector<std::vector<bool>> *Texture::getTransparencyData() const {
152 return &transparencydata;
153}
154
155float Texture::getSolidFraction(const std::vector<helios::vec2> &uvs) {
156 float solidfraction = 1;
157
158 PixelUVKey key;
159 key.coords.reserve(2 * uvs.size());
160 for (auto &uvc: uvs) {
161 key.coords.push_back(int(std::round(uvc.x * (image_resolution.x - 1))));
162 key.coords.push_back(int(std::round(uvc.y * (image_resolution.y - 1))));
163 }
164
165 if (solidFracCache.find(key) != solidFracCache.end()) {
166 return solidFracCache.at(key);
167 }
168
169 solidfraction = computeSolidFraction(uvs);
170 solidFracCache.emplace(std::move(key), solidfraction);
171
172 return solidfraction;
173}
174
175float Texture::computeSolidFraction(const std::vector<helios::vec2> &uvs) const {
176 // Early out for opaque textures or degenerate UVs
177 if (!hasTransparencyChannel() || uvs.size() < 3)
178 return 1.0f;
179
180 // Fetch alpha mask and dimensions
181 const auto *alpha2D = getTransparencyData(); // vector<vector<bool>>
182 int W = getImageResolution().x;
183 int H = getImageResolution().y;
184
185 // Flatten mask to contiguous array
186 std::vector<uint8_t> mask(W * H);
187 for (int y = 0; y < H; ++y)
188 for (int x = 0; x < W; ++x)
189 mask[y * W + x] = (*alpha2D)[H - 1 - y][x];
190
191 // Compute pixel‐space bounding box from UVs
192 float minU = uvs[0].x, maxU = uvs[0].x, minV = uvs[0].y, maxV = uvs[0].y;
193 for (auto &p: uvs) {
194 minU = std::min(minU, p.x);
195 maxU = std::max(maxU, p.x);
196 minV = std::min(minV, p.y);
197 maxV = std::max(maxV, p.y);
198 }
199 int xmin = std::clamp(int(std::floor(minU * (W - 1))), 0, W - 1);
200 int xmax = std::clamp(int(std::ceil(maxU * (W - 1))), 0, W - 1);
201 int ymin = std::clamp(int(std::floor(minV * (H - 1))), 0, H - 1);
202 int ymax = std::clamp(int(std::ceil(maxV * (H - 1))), 0, H - 1);
203
204 if (xmin > xmax || ymin > ymax)
205 return 0.0f;
206
207 // Precompute half‐space coefficients for each edge i→i+1
208 int N = int(uvs.size());
209 std::vector<float> A(N), B(N), C(N);
210 for (int i = 0; i < N; ++i) {
211 int j = (i + 1) % N;
212 const auto &a = uvs[i], &b = uvs[j];
213 // L(x,y) = (b.x - a.x)*y - (b.y - a.y)*x + (a.x*b.y - a.y*b.x)
214 A[i] = b.x - a.x;
215 B[i] = -(b.y - a.y);
216 C[i] = a.x * b.y - a.y * b.x;
217 }
218
219 // Raster‐scan, test each pixel center
220 int64_t countTotal = 0, countOpaque = 0;
221 float invWm1 = 1.0f / float(W - 1);
222 float invHm1 = 1.0f / float(H - 1);
223
224 for (int j = ymin; j <= ymax; ++j) {
225 float yuv = (j + 0.5f) * invHm1;
226 for (int i = xmin; i <= xmax; ++i) {
227 float xuv = (i + 0.5f) * invWm1;
228 bool inside = true;
229
230 // all edges must satisfy L(xuv,yuv) >= 0
231 for (int k = 0; k < N; ++k) {
232 float L = A[k] * yuv + B[k] * xuv + C[k];
233 if (L < 0.0f) {
234 inside = false;
235 break;
236 }
237 }
238
239 if (!inside)
240 continue;
241
242 ++countTotal;
243 countOpaque += mask[j * W + i];
244 }
245 }
246
247 return countTotal == 0 ? 0.0f : float(countOpaque) / float(countTotal);
248}
249
251 for (auto &[UUID, primitive]: primitives) {
252 primitive->dirty_flag = false;
253 }
254 dirty_deleted_primitives.clear();
255}
256
258 for (auto &[UUID, primitive]: primitives) {
259 primitive->dirty_flag = true;
260 }
261}
262
264 if (!dirty_deleted_primitives.empty()) {
265 return true;
266 }
267 for (auto &[UUID, primitive]: primitives) {
268 if (primitive->dirty_flag) {
269 return true;
270 }
271 }
272 return false;
273}
274
276#ifdef HELIOS_DEBUG
277 if (!doesPrimitiveExist(UUID)) {
278 helios_runtime_error("ERROR (Context::markPrimitiveDirty): Primitive with UUID " + std::to_string(UUID) + " does not exist.");
279 }
280#endif
281 primitives.at(UUID)->dirty_flag = true;
282}
283
284void Context::markPrimitiveDirty(const std::vector<uint> &UUIDs) const {
285 for (uint UUID: UUIDs) {
286 markPrimitiveDirty(UUID);
287 }
288}
289
291#ifdef HELIOS_DEBUG
292 if (!doesPrimitiveExist(UUID)) {
293 helios_runtime_error("ERROR (Context::markPrimitiveDirty): Primitive with UUID " + std::to_string(UUID) + " does not exist.");
294 }
295#endif
296 primitives.at(UUID)->dirty_flag = false;
297}
298
299void Context::markPrimitiveClean(const std::vector<uint> &UUIDs) const {
300 for (uint UUID: UUIDs) {
301 markPrimitiveClean(UUID);
302 }
303}
304
305[[nodiscard]] bool Context::isPrimitiveDirty(uint UUID) const {
306#ifdef HELIOS_DEBUG
307 if (!doesPrimitiveExist(UUID)) {
308 helios_runtime_error("ERROR (Context::markPrimitiveDirty): Primitive with UUID " + std::to_string(UUID) + " does not exist.");
309 }
310#endif
311 return primitives.at(UUID)->dirty_flag;
312}
313
314
315void Context::setDate(int day, int month, int year) {
316 if (day < 1 || day > 31) {
317 helios_runtime_error("ERROR (Context::setDate): Day of month is out of range (day of " + std::to_string(day) + " was given).");
318 } else if (month < 1 || month > 12) {
319 helios_runtime_error("ERROR (Context::setDate): Month of year is out of range (month of " + std::to_string(month) + " was given).");
320 } else if (year < 1000) {
321 helios_runtime_error("ERROR (Context::setDate): Year should be specified in YYYY format.");
322 }
323
324 sim_date = make_Date(day, month, year);
325}
326
327void Context::setDate(const Date &date) {
328 if (date.day < 1 || date.day > 31) {
329 helios_runtime_error("ERROR (Context::setDate): Day of month is out of range (day of " + std::to_string(date.day) + " was given).");
330 } else if (date.month < 1 || date.month > 12) {
331 helios_runtime_error("ERROR (Context::setDate): Month of year is out of range (month of " + std::to_string(date.month) + " was given).");
332 } else if (date.year < 1000) {
333 helios_runtime_error("ERROR (Context::setDate): Year should be specified in YYYY format.");
334 }
335
336 sim_date = date;
337}
338
339void Context::setDate(int Julian_day, int year) {
340 if (Julian_day < 1 || Julian_day > 366) {
341 helios_runtime_error("ERROR (Context::setDate): Julian day out of range.");
342 } else if (year < 1000) {
343 helios_runtime_error("ERROR (Context::setDate): Year should be specified in YYYY format.");
344 }
345
346 sim_date = CalendarDay(Julian_day, year);
347}
348
350 return sim_date;
351}
352
353const char *Context::getMonthString() const {
354 if (sim_date.month == 1) {
355 return "JAN";
356 } else if (sim_date.month == 2) {
357 return "FEB";
358 } else if (sim_date.month == 3) {
359 return "MAR";
360 } else if (sim_date.month == 4) {
361 return "APR";
362 } else if (sim_date.month == 5) {
363 return "MAY";
364 } else if (sim_date.month == 6) {
365 return "JUN";
366 } else if (sim_date.month == 7) {
367 return "JUL";
368 } else if (sim_date.month == 8) {
369 return "AUG";
370 } else if (sim_date.month == 9) {
371 return "SEP";
372 } else if (sim_date.month == 10) {
373 return "OCT";
374 } else if (sim_date.month == 11) {
375 return "NOV";
376 } else {
377 return "DEC";
378 }
379}
380
382 return JulianDay(sim_date.day, sim_date.month, sim_date.year);
383}
384
385void Context::setTime(int minute, int hour) {
386 setTime(0, minute, hour);
387}
388
389void Context::setTime(int second, int minute, int hour) {
390 if (second < 0 || second > 59) {
391 helios_runtime_error("ERROR (Context::setTime): Second out of range (0-59).");
392 } else if (minute < 0 || minute > 59) {
393 helios_runtime_error("ERROR (Context::setTime): Minute out of range (0-59).");
394 } else if (hour < 0 || hour > 23) {
395 helios_runtime_error("ERROR (Context::setTime): Hour out of range (0-23).");
396 }
397
398 sim_time = make_Time(hour, minute, second);
399}
400
401void Context::setTime(const Time &time) {
402 if (time.minute < 0 || time.minute > 59) {
403 helios_runtime_error("ERROR (Context::setTime): Minute out of range (0-59).");
404 } else if (time.hour < 0 || time.hour > 23) {
405 helios_runtime_error("ERROR (Context::setTime): Hour out of range (0-23).");
406 }
407
408 sim_time = time;
409}
410
412 return sim_time;
413}
414
416 sim_location = location;
417}
418
420 return sim_location;
421}
422
424 return unif_distribution(generator);
425}
426
427float Context::randu(float minrange, float maxrange) {
428 if (maxrange < minrange) {
429 helios_runtime_error("ERROR (Context::randu): Maximum value of range must be greater than minimum value of range.");
430 return 0;
431 } else if (maxrange == minrange) {
432 return minrange;
433 } else {
434 return minrange + unif_distribution(generator) * (maxrange - minrange);
435 }
436}
437
438int Context::randu(int minrange, int maxrange) {
439 if (maxrange < minrange) {
440 helios_runtime_error("ERROR (Context::randu): Maximum value of range must be greater than minimum value of range.");
441 return 0;
442 } else if (maxrange == minrange) {
443 return minrange;
444 } else {
445 return minrange + (int) lroundf(unif_distribution(generator) * float(maxrange - minrange));
446 }
447}
448
450 return norm_distribution(generator);
451}
452
453float Context::randn(float mean, float stddev) {
454 return mean + norm_distribution(generator) * fabs(stddev);
455}
456
457
458std::vector<uint> Context::getAllUUIDs() const {
459 // Use cached result if valid
460 if (all_uuids_cache_valid) {
461 return cached_all_uuids;
462 }
463
464 // Rebuild cache
465 cached_all_uuids.clear();
466 cached_all_uuids.reserve(primitives.size());
467 for (const auto &[UUID, primitive]: primitives) {
468 if (primitive->ishidden) {
469 continue;
470 }
471 cached_all_uuids.push_back(UUID);
472 }
473 all_uuids_cache_valid = true;
474 return cached_all_uuids;
475}
476
477std::vector<uint> Context::getDirtyUUIDs(bool include_deleted_UUIDs) const {
478
479 size_t dirty_count = std::count_if(primitives.begin(), primitives.end(), [&](auto const &kv) { return isPrimitiveDirty(kv.first); });
480
481 std::vector<uint> dirty_UUIDs;
482 dirty_UUIDs.reserve(dirty_count);
483 for (const auto &[UUID, primitive]: primitives) {
484 if (!primitive->dirty_flag || primitive->ishidden) {
485 continue;
486 }
487 dirty_UUIDs.push_back(UUID);
488 }
489
490 if (include_deleted_UUIDs) {
491 dirty_UUIDs.insert(dirty_UUIDs.end(), dirty_deleted_primitives.begin(), dirty_deleted_primitives.end());
492 }
493
494 return dirty_UUIDs;
495}
496
497std::vector<uint> Context::getDeletedUUIDs() const {
498 return dirty_deleted_primitives;
499}
500
501void Context::hidePrimitive(uint UUID) const {
502#ifdef HELIOS_DEBUG
503 if (!doesPrimitiveExist(UUID)) {
504 helios_runtime_error("ERROR (Context::hidePrimitive): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
505 }
506#endif
507 primitives.at(UUID)->ishidden = true;
508 invalidateAllUUIDsCache();
509}
510
511void Context::hidePrimitive(const std::vector<uint> &UUIDs) const {
512 for (uint UUID: UUIDs) {
513 hidePrimitive(UUID);
514 }
515}
516
517void Context::showPrimitive(uint UUID) const {
518#ifdef HELIOS_DEBUG
519 if (!doesPrimitiveExist(UUID)) {
520 helios_runtime_error("ERROR (Context::showPrimitive): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
521 }
522#endif
523 primitives.at(UUID)->ishidden = false;
524 invalidateAllUUIDsCache();
525}
526
527void Context::showPrimitive(const std::vector<uint> &UUIDs) const {
528 for (uint UUID: UUIDs) {
529 showPrimitive(UUID);
530 }
531}
532
534 if (!doesPrimitiveExist(UUID)) {
535 helios_runtime_error("ERROR (Context::isPrimitiveHidden): UUID of " + std::to_string(UUID) + " does not exist in the Context.");
536 }
537 return primitives.at(UUID)->ishidden;
538}
539
540void Context::cleanDeletedUUIDs(std::vector<uint> &UUIDs) const {
541 for (size_t i = UUIDs.size(); i-- > 0;) {
542 if (!doesPrimitiveExist(UUIDs.at(i))) {
543 UUIDs.erase(UUIDs.begin() + i);
544 }
545 }
546}
547
548void Context::cleanDeletedUUIDs(std::vector<std::vector<uint>> &UUIDs) const {
549 for (auto &vec: UUIDs) {
550 for (auto it = vec.begin(); it != vec.end();) {
551 if (!doesPrimitiveExist(*it)) {
552 it = vec.erase(it);
553 } else {
554 ++it;
555 }
556 }
557 }
558}
559
560void Context::cleanDeletedUUIDs(std::vector<std::vector<std::vector<uint>>> &UUIDs) const {
561 for (auto &vec2D: UUIDs) {
562 for (auto &vec: vec2D) {
563 for (auto it = vec.begin(); it != vec.end();) {
564 if (!doesPrimitiveExist(*it)) {
565 it = vec.erase(it);
566 } else {
567 ++it;
568 }
569 }
570 }
571 }
572}
573
574void Context::addTimeseriesData(const char *label, float value, const Date &date, const Time &time) {
575 // floating point value corresponding to date and time
576 double date_value = floor(date.year * 366.25) + date.JulianDay();
577 date_value += double(time.hour) / 24. + double(time.minute) / 1440. + double(time.second) / 86400.;
578
579 // Check if data label already exists
580 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
581 timeseries_data[label].push_back(value);
582 timeseries_datevalue[label].push_back(date_value);
583 return;
584 } else { // exists
585
586 uint N = getTimeseriesLength(label);
587
588 auto it_data = timeseries_data[label].begin();
589 auto it_datevalue = timeseries_datevalue[label].begin();
590
591 if (N == 1) {
592 if (date_value < timeseries_datevalue[label].front()) {
593 timeseries_data[label].insert(it_data, value);
594 timeseries_datevalue[label].insert(it_datevalue, date_value);
595 return;
596 } else {
597 timeseries_data[label].insert(it_data + 1, value);
598 timeseries_datevalue[label].insert(it_datevalue + 1, date_value);
599 return;
600 }
601 } else {
602 if (date_value < timeseries_datevalue[label].front()) { // check if data should be inserted at beginning of timeseries
603 timeseries_data[label].insert(it_data, value);
604 timeseries_datevalue[label].insert(it_datevalue, date_value);
605 return;
606 } else if (date_value > timeseries_datevalue[label].back()) { // check if data should be inserted at end of timeseries
607 timeseries_data[label].push_back(value);
608 timeseries_datevalue[label].push_back(date_value);
609 return;
610 }
611
612 // data should be inserted somewhere in the middle of timeseries
613 for (uint t = 0; t < N - 1; t++) {
614 if (date_value == timeseries_datevalue[label].at(t)) {
615 std::cerr << "WARNING (Context::addTimeseriesData): Skipping duplicate timeseries date/time." << std::endl;
616 continue;
617 }
618 if (date_value > timeseries_datevalue[label].at(t) && date_value < timeseries_datevalue[label].at(t + 1)) {
619 timeseries_data[label].insert(it_data + t + 1, value);
620 timeseries_datevalue[label].insert(it_datevalue + t + 1, date_value);
621 return;
622 }
623 }
624 }
625 }
626
627 helios_runtime_error("ERROR (Context::addTimeseriesData): Failed to insert timeseries data for unknown reason.");
628}
629
630void Context::setCurrentTimeseriesPoint(const char *label, uint index) {
631 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
632 helios_runtime_error("ERROR (setCurrentTimeseriesPoint): Timeseries variable `" + std::string(label) + "' does not exist.");
633 }
634 setDate(queryTimeseriesDate(label, index));
635 setTime(queryTimeseriesTime(label, index));
636}
637
638float Context::queryTimeseriesData(const char *label, const Date &date, const Time &time) const {
639 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
640 helios_runtime_error("ERROR (setCurrentTimeseriesData): Timeseries variable `" + std::string(label) + "' does not exist.");
641 }
642
643 double date_value = floor(date.year * 366.25) + date.JulianDay();
644 date_value += double(time.hour) / 24. + double(time.minute) / 1440. + double(time.second) / 86400.;
645
646 double tmin = timeseries_datevalue.at(label).front();
647 double tmax = timeseries_datevalue.at(label).back();
648
649 if (date_value < tmin) {
650 std::cerr << "WARNING (queryTimeseriesData): Timeseries date and time is outside of the range of the data. Using the earliest data point in the timeseries." << std::endl;
651 return timeseries_data.at(label).front();
652 } else if (date_value > tmax) {
653 std::cerr << "WARNING (queryTimeseriesData): Timeseries date and time is outside of the range of the data. Using the latest data point in the timeseries." << std::endl;
654 return timeseries_data.at(label).back();
655 }
656
657 if (timeseries_datevalue.at(label).empty()) {
658 std::cerr << "WARNING (queryTimeseriesData): timeseries " << label << " does not contain any data." << std::endl;
659 return 0;
660 } else if (timeseries_datevalue.at(label).size() == 1) {
661 return timeseries_data.at(label).front();
662 } else {
663 int i;
664 bool success = false;
665 for (i = 0; i < timeseries_data.at(label).size() - 1; i++) {
666 if (date_value >= timeseries_datevalue.at(label).at(i) && date_value <= timeseries_datevalue.at(label).at(i + 1)) {
667 success = true;
668 break;
669 }
670 }
671
672 if (!success) {
673 helios_runtime_error("ERROR (queryTimeseriesData): Failed to query timeseries data for unknown reason.");
674 }
675
676 double xminus = timeseries_data.at(label).at(i);
677 double xplus = timeseries_data.at(label).at(i + 1);
678
679 double tminus = timeseries_datevalue.at(label).at(i);
680 double tplus = timeseries_datevalue.at(label).at(i + 1);
681
682 return float(xminus + (xplus - xminus) * (date_value - tminus) / (tplus - tminus));
683 }
684}
685
686float Context::queryTimeseriesData(const char *label) const {
687 return queryTimeseriesData(label, sim_date, sim_time);
688}
689
690float Context::queryTimeseriesData(const char *label, const uint index) const {
691 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
692 helios_runtime_error("ERROR( Context::getTimeseriesData): Timeseries variable " + std::string(label) + " does not exist.");
693 }
694
695 return timeseries_data.at(label).at(index);
696}
697
698Time Context::queryTimeseriesTime(const char *label, const uint index) const {
699 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
700 helios_runtime_error("ERROR( Context::getTimeseriesTime): Timeseries variable " + std::string(label) + " does not exist.");
701 }
702
703 double dateval = timeseries_datevalue.at(label).at(index);
704
705 int year = floor(floor(dateval) / 366.25);
706 assert(year > 1000 && year < 10000);
707
708 int JD = floor(dateval - floor(double(year) * 366.25));
709 assert(JD > 0 && JD < 367);
710
711 int hour = floor((dateval - floor(dateval)) * 24.);
712 int minute = floor(((dateval - floor(dateval)) * 24. - double(hour)) * 60.);
713 int second = (int) lround((((dateval - floor(dateval)) * 24. - double(hour)) * 60. - double(minute)) * 60.);
714
715 if (second == 60) {
716 second = 0;
717 minute++;
718 }
719
720 if (minute == 60) {
721 minute = 0;
722 hour++;
723 }
724
725 assert(second >= 0 && second < 60);
726 assert(minute >= 0 && minute < 60);
727 assert(hour >= 0 && hour < 24);
728
729 return make_Time(hour, minute, second);
730}
731
732Date Context::queryTimeseriesDate(const char *label, const uint index) const {
733 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
734 helios_runtime_error("ERROR( Context::getTimeseriesDate): Timeseries variable " + std::string(label) + " does not exist.");
735 }
736
737 double dateval = timeseries_datevalue.at(label).at(index);
738
739 int year = floor(floor(dateval) / 366.25);
740 assert(year > 1000 && year < 10000);
741
742 int JD = floor(dateval - floor(double(year) * 366.25));
743 assert(JD > 0 && JD < 367);
744
745 return Julian2Calendar(JD, year);
746}
747
748uint Context::getTimeseriesLength(const char *label) const {
749 uint size = 0;
750 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
751 helios_runtime_error("ERROR (Context::getTimeseriesDate): Timeseries variable `" + std::string(label) + "' does not exist.");
752 } else {
753 size = timeseries_data.at(label).size();
754 }
755
756 return size;
757}
758
759bool Context::doesTimeseriesVariableExist(const char *label) const {
760 if (timeseries_data.find(label) == timeseries_data.end()) { // does not exist
761 return false;
762 } else {
763 return true;
764 }
765}
766
767std::vector<std::string> Context::listTimeseriesVariables() const {
768 std::vector<std::string> labels;
769 labels.reserve(timeseries_data.size());
770 for (const auto &[timeseries_label, timeseries_data]: timeseries_data) {
771 labels.push_back(timeseries_label);
772 }
773 return labels;
774}
775
776
777void Context::getDomainBoundingBox(vec2 &xbounds, vec2 &ybounds, vec2 &zbounds) const {
778 getDomainBoundingBox(getAllUUIDs(), xbounds, ybounds, zbounds);
779}
780
781void Context::getDomainBoundingBox(const std::vector<uint> &UUIDs, vec2 &xbounds, vec2 &ybounds, vec2 &zbounds) const {
782 // Global bounding box initialization
783 xbounds.x = 1e8; // global min x
784 xbounds.y = -1e8; // global max x
785 ybounds.x = 1e8; // global min y
786 ybounds.y = -1e8; // global max y
787 zbounds.x = 1e8; // global min z
788 zbounds.y = -1e8; // global max z
789
790 // Parallel region over the primitives (UUIDs)
791#ifdef USE_OPENMP
792#pragma omp parallel
793 {
794 // Each thread creates its own local bounding box.
795 float local_xmin = 1e8, local_xmax = -1e8;
796 float local_ymin = 1e8, local_ymax = -1e8;
797 float local_zmin = 1e8, local_zmax = -1e8;
798
799// Parallelize the outer loop over primitives. Use "for" inside the parallel region.
800#pragma omp for nowait
801 for (int i = 0; i < (int) UUIDs.size(); i++) {
802 // For each primitive:
803 const std::vector<vec3> &verts = getPrimitivePointer_private(UUIDs[i])->getVertices();
804 // Update local bounding box for each vertex in this primitive.
805 for (const auto &vert: verts) {
806 local_xmin = std::min(local_xmin, vert.x);
807 local_xmax = std::max(local_xmax, vert.x);
808 local_ymin = std::min(local_ymin, vert.y);
809 local_ymax = std::max(local_ymax, vert.y);
810 local_zmin = std::min(local_zmin, vert.z);
811 local_zmax = std::max(local_zmax, vert.z);
812 }
813 }
814
815// Merge the thread-local bounds into the global bounds.
816#pragma omp critical
817 {
818 xbounds.x = std::min(xbounds.x, local_xmin);
819 xbounds.y = std::max(xbounds.y, local_xmax);
820 ybounds.x = std::min(ybounds.x, local_ymin);
821 ybounds.y = std::max(ybounds.y, local_ymax);
822 zbounds.x = std::min(zbounds.x, local_zmin);
823 zbounds.y = std::max(zbounds.y, local_zmax);
824 }
825 } // end parallel region
826
827#else
828
829 for (uint UUID: UUIDs) {
830 const std::vector<vec3> &verts = getPrimitivePointer_private(UUID)->getVertices();
831
832 for (auto &vert: verts) {
833 if (vert.x < xbounds.x) {
834 xbounds.x = vert.x;
835 } else if (vert.x > xbounds.y) {
836 xbounds.y = vert.x;
837 }
838 if (vert.y < ybounds.x) {
839 ybounds.x = vert.y;
840 } else if (vert.y > ybounds.y) {
841 ybounds.y = vert.y;
842 }
843 if (vert.z < zbounds.x) {
844 zbounds.x = vert.z;
845 } else if (vert.z > zbounds.y) {
846 zbounds.y = vert.z;
847 }
848 }
849 }
850
851#endif
852}
853
854void Context::getDomainBoundingSphere(vec3 &center, float &radius) const {
855 vec2 xbounds, ybounds, zbounds;
856 getDomainBoundingBox(xbounds, ybounds, zbounds);
857
858 center.x = xbounds.x + 0.5f * (xbounds.y - xbounds.x);
859 center.y = ybounds.x + 0.5f * (ybounds.y - ybounds.x);
860 center.z = zbounds.x + 0.5f * (zbounds.y - zbounds.x);
861
862 radius = 0.5f * sqrtf(powf(xbounds.y - xbounds.x, 2) + powf(ybounds.y - ybounds.x, 2) + powf((zbounds.y - zbounds.x), 2));
863}
864
865void Context::getDomainBoundingSphere(const std::vector<uint> &UUIDs, vec3 &center, float &radius) const {
866 vec2 xbounds, ybounds, zbounds;
867 getDomainBoundingBox(UUIDs, xbounds, ybounds, zbounds);
868
869 center.x = xbounds.x + 0.5f * (xbounds.y - xbounds.x);
870 center.y = ybounds.x + 0.5f * (ybounds.y - ybounds.x);
871 center.z = zbounds.x + 0.5f * (zbounds.y - zbounds.x);
872
873 radius = 0.5f * sqrtf(powf(xbounds.y - xbounds.x, 2) + powf(ybounds.y - ybounds.x, 2) + powf((zbounds.y - zbounds.x), 2));
874}
875
876void Context::cropDomainX(const vec2 &xbounds) {
877 const std::vector<uint> &UUIDs_all = getAllUUIDs();
878
879 for (uint p: UUIDs_all) {
880 const std::vector<vec3> &vertices = getPrimitivePointer_private(p)->getVertices();
881
882 for (auto &vertex: vertices) {
883 if (vertex.x < xbounds.x || vertex.x > xbounds.y) {
885 break;
886 }
887 }
888 }
889
890 if (getPrimitiveCount() == 0) {
891 std::cerr << "WARNING (Context::cropDomainX): No primitives were inside cropped area, and thus all primitives were deleted." << std::endl;
892 }
893}
894
895void Context::cropDomainY(const vec2 &ybounds) {
896 const std::vector<uint> &UUIDs_all = getAllUUIDs();
897
898 for (uint p: UUIDs_all) {
899 const std::vector<vec3> &vertices = getPrimitivePointer_private(p)->getVertices();
900
901 for (auto &vertex: vertices) {
902 if (vertex.y < ybounds.x || vertex.y > ybounds.y) {
904 break;
905 }
906 }
907 }
908
909 if (getPrimitiveCount() == 0) {
910 std::cerr << "WARNING (Context::cropDomainY): No primitives were inside cropped area, and thus all primitives were deleted." << std::endl;
911 }
912}
913
914void Context::cropDomainZ(const vec2 &zbounds) {
915 const std::vector<uint> &UUIDs_all = getAllUUIDs();
916
917 for (uint p: UUIDs_all) {
918 const std::vector<vec3> &vertices = getPrimitivePointer_private(p)->getVertices();
919
920 for (auto &vertex: vertices) {
921 if (vertex.z < zbounds.x || vertex.z > zbounds.y) {
923 break;
924 }
925 }
926 }
927
928 if (getPrimitiveCount() == 0) {
929 std::cerr << "WARNING (Context::cropDomainZ): No primitives were inside cropped area, and thus all primitives were deleted." << std::endl;
930 }
931}
932
933void Context::cropDomain(std::vector<uint> &UUIDs, const vec2 &xbounds, const vec2 &ybounds, const vec2 &zbounds) {
934 size_t delete_count = 0;
935 for (uint UUID: UUIDs) {
936 const std::vector<vec3> &vertices = getPrimitivePointer_private(UUID)->getVertices();
937
938 for (auto &vertex: vertices) {
939 if (vertex.x < xbounds.x || vertex.x > xbounds.y || vertex.y < ybounds.x || vertex.y > ybounds.y || vertex.z < zbounds.x || vertex.z > zbounds.y) {
940 deletePrimitive(UUID);
941 delete_count++;
942 break;
943 }
944 }
945 }
946
947 if (delete_count == UUIDs.size()) {
948 std::cerr << "WARNING (Context::cropDomain): No specified primitives were entirely inside cropped area, and thus all specified primitives were deleted." << std::endl;
949 }
950
951 cleanDeletedUUIDs(UUIDs);
952}
953
954void Context::cropDomain(const vec2 &xbounds, const vec2 &ybounds, const vec2 &zbounds) {
955 std::vector<uint> UUIDs = getAllUUIDs();
956 cropDomain(UUIDs, xbounds, ybounds, zbounds);
957}
958
959
961#ifdef HELIOS_DEBUG
962 if (!doesObjectExist(objID)) {
963 helios_runtime_error("ERROR (Context::areObjectPrimitivesComplete): Object ID of " + std::to_string(objID) + " does not exist in the context.");
964 }
965#endif
967}
968
969void Context::cleanDeletedObjectIDs(std::vector<uint> &objIDs) const {
970 for (auto it = objIDs.begin(); it != objIDs.end();) {
971 if (!doesObjectExist(*it)) {
972 it = objIDs.erase(it);
973 } else {
974 ++it;
975 }
976 }
977}
978
979void Context::cleanDeletedObjectIDs(std::vector<std::vector<uint>> &objIDs) const {
980 for (auto &vec: objIDs) {
981 for (auto it = vec.begin(); it != vec.end();) {
982 if (!doesObjectExist(*it)) {
983 it = vec.erase(it);
984 } else {
985 ++it;
986 }
987 }
988 }
989}
990
991void Context::cleanDeletedObjectIDs(std::vector<std::vector<std::vector<uint>>> &objIDs) const {
992 for (auto &vec2D: objIDs) {
993 for (auto &vec: vec2D) {
994 for (auto it = vec.begin(); it != vec.end();) {
995 if (!doesObjectExist(*it)) {
996 it = vec.erase(it);
997 } else {
998 ++it;
999 }
1000 }
1001 }
1002 }
1003}
1004
1006#ifdef HELIOS_DEBUG
1007 if (objects.find(ObjID) == objects.end()) {
1008 helios_runtime_error("ERROR (Context::getObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
1009 }
1010#endif
1011 return objects.at(ObjID);
1012}
1013
1015 return objects.size();
1016}
1017
1018bool Context::doesObjectExist(const uint ObjID) const {
1019 return objects.find(ObjID) != objects.end();
1020}
1021
1022std::vector<uint> Context::getAllObjectIDs() const {
1023 std::vector<uint> objIDs;
1024 objIDs.reserve(objects.size());
1025 size_t i = 0;
1026 for (auto [objID, object]: objects) {
1027 if (object->ishidden) {
1028 continue;
1029 }
1030 objIDs.push_back(objID);
1031 i++;
1032 }
1033 return objIDs;
1034}
1035
1036void Context::deleteObject(const std::vector<uint> &ObjIDs) {
1037 for (const uint ObjID: ObjIDs) {
1038 deleteObject(ObjID);
1039 }
1040}
1041
1043 if (objects.find(ObjID) == objects.end()) {
1044 helios_runtime_error("ERROR (Context::deleteObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1045 }
1046
1047 CompoundObject *obj = objects.at(ObjID);
1048
1049 for (const auto &[label, type]: obj->object_data_types) {
1050 decrementObjectDataLabelCounter(label);
1051 }
1052
1053 const std::vector<uint> &UUIDs = obj->getPrimitiveUUIDs();
1054
1055
1056 delete obj;
1057 objects.erase(ObjID);
1058
1059 deletePrimitive(UUIDs);
1060}
1061
1062std::vector<uint> Context::copyObject(const std::vector<uint> &ObjIDs) {
1063 std::vector<uint> ObjIDs_copy(ObjIDs.size());
1064 size_t i = 0;
1065 for (uint ObjID: ObjIDs) {
1066 ObjIDs_copy.at(i) = copyObject(ObjID);
1067 i++;
1068 }
1069
1070 return ObjIDs_copy;
1071}
1072
1074 if (objects.find(ObjID) == objects.end()) {
1075 helios_runtime_error("ERROR (Context::copyObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1076 }
1077
1078 ObjectType type = objects.at(ObjID)->getObjectType();
1079
1080 const std::vector<uint> &UUIDs = getObjectPointer(ObjID)->getPrimitiveUUIDs();
1081
1082 const std::vector<uint> &UUIDs_copy = copyPrimitive(UUIDs);
1083 for (uint p: UUIDs_copy) {
1084 getPrimitivePointer_private(p)->setParentObjectID(currentObjectID);
1085 }
1086
1087 const std::string &texturefile = objects.at(ObjID)->getTextureFile();
1088
1089 if (type == OBJECT_TYPE_TILE) {
1090 Tile *o = getTileObjectPointer(ObjID);
1091
1092 const int2 &subdiv = o->getSubdivisionCount();
1093
1094 auto *tile_new = (new Tile(currentObjectID, UUIDs_copy, subdiv, texturefile.c_str(), this));
1095
1096 objects[currentObjectID] = tile_new;
1097 } else if (type == OBJECT_TYPE_SPHERE) {
1098 Sphere *o = getSphereObjectPointer(ObjID);
1099
1100 uint subdiv = o->getSubdivisionCount();
1101
1102 auto *sphere_new = (new Sphere(currentObjectID, UUIDs_copy, subdiv, texturefile.c_str(), this));
1103
1104 objects[currentObjectID] = sphere_new;
1105 } else if (type == OBJECT_TYPE_TUBE) {
1106 Tube *o = getTubeObjectPointer(ObjID);
1107
1108 const std::vector<vec3> &nodes = o->getNodes();
1109 const std::vector<float> &radius = o->getNodeRadii();
1110 const std::vector<RGBcolor> &colors = o->getNodeColors();
1111 const std::vector<std::vector<vec3>> &triangle_vertices = o->getTriangleVertices();
1112 uint subdiv = o->getSubdivisionCount();
1113
1114 auto *tube_new = (new Tube(currentObjectID, UUIDs_copy, nodes, radius, colors, triangle_vertices, subdiv, texturefile.c_str(), this));
1115
1116 objects[currentObjectID] = tube_new;
1117 } else if (type == OBJECT_TYPE_BOX) {
1118 Box *o = getBoxObjectPointer(ObjID);
1119
1120 const int3 &subdiv = o->getSubdivisionCount();
1121
1122 auto *box_new = (new Box(currentObjectID, UUIDs_copy, subdiv, texturefile.c_str(), this));
1123
1124 objects[currentObjectID] = box_new;
1125 } else if (type == OBJECT_TYPE_DISK) {
1126 Disk *o = getDiskObjectPointer(ObjID);
1127
1128 const int2 &subdiv = o->getSubdivisionCount();
1129
1130 auto *disk_new = (new Disk(currentObjectID, UUIDs_copy, subdiv, texturefile.c_str(), this));
1131
1132 objects[currentObjectID] = disk_new;
1133 } else if (type == OBJECT_TYPE_POLYMESH) {
1134 auto *polymesh_new = (new Polymesh(currentObjectID, UUIDs_copy, texturefile.c_str(), this));
1135
1136 objects[currentObjectID] = polymesh_new;
1137 } else if (type == OBJECT_TYPE_CONE) {
1138 Cone *o = getConeObjectPointer(ObjID);
1139
1140 const std::vector<vec3> &nodes = o->getNodeCoordinates();
1141 const std::vector<float> &radius = o->getNodeRadii();
1142 uint subdiv = o->getSubdivisionCount();
1143
1144 auto *cone_new = (new Cone(currentObjectID, UUIDs_copy, nodes.at(0), nodes.at(1), radius.at(0), radius.at(1), subdiv, texturefile.c_str(), this));
1145
1146 objects[currentObjectID] = cone_new;
1147 }
1148
1149 copyObjectData(ObjID, currentObjectID);
1150
1151 float T[16];
1153
1154 getObjectPointer(currentObjectID)->setTransformationMatrix(T);
1155
1156 currentObjectID++;
1157 return currentObjectID - 1;
1158}
1159
1160std::vector<uint> Context::filterObjectsByData(const std::vector<uint> &IDs, const char *object_data, float threshold, const char *comparator) const {
1161 std::vector<uint> output_object_IDs;
1162 output_object_IDs.resize(IDs.size());
1163 uint passed_count = 0;
1164
1165 for (uint i = 0; i < IDs.size(); i++) {
1166 if (doesObjectDataExist(IDs.at(i), object_data)) {
1167 HeliosDataType type = getObjectDataType(object_data);
1168 if (type == HELIOS_TYPE_UINT) {
1169 uint R;
1170 getObjectData(IDs.at(i), object_data, R);
1171 if (strcmp(comparator, "<") == 0) {
1172 if (float(R) < threshold) {
1173 output_object_IDs.at(passed_count) = IDs.at(i);
1174 passed_count++;
1175 }
1176 } else if (strcmp(comparator, ">") == 0) {
1177 if (float(R) > threshold) {
1178 output_object_IDs.at(passed_count) = IDs.at(i);
1179 passed_count++;
1180 }
1181 } else if (strcmp(comparator, "=") == 0) {
1182 if (float(R) == threshold) {
1183 output_object_IDs.at(passed_count) = IDs.at(i);
1184 passed_count++;
1185 }
1186 }
1187 } else if (type == HELIOS_TYPE_FLOAT) {
1188 float R;
1189 getObjectData(IDs.at(i), object_data, R);
1190
1191 if (strcmp(comparator, "<") == 0) {
1192 if (R < threshold) {
1193 output_object_IDs.at(passed_count) = IDs.at(i);
1194 passed_count++;
1195 }
1196 } else if (strcmp(comparator, ">") == 0) {
1197 if (R > threshold) {
1198 output_object_IDs.at(passed_count) = IDs.at(i);
1199 passed_count++;
1200 }
1201 } else if (strcmp(comparator, "=") == 0) {
1202 if (R == threshold) {
1203 output_object_IDs.at(passed_count) = IDs.at(i);
1204 passed_count++;
1205 }
1206 }
1207 } else if (type == HELIOS_TYPE_INT) {
1208 int R;
1209 getObjectData(IDs.at(i), object_data, R);
1210
1211 if (strcmp(comparator, "<") == 0) {
1212 if (float(R) < threshold) {
1213 output_object_IDs.at(passed_count) = IDs.at(i);
1214 passed_count++;
1215 }
1216 } else if (strcmp(comparator, ">") == 0) {
1217 if (float(R) > threshold) {
1218 output_object_IDs.at(passed_count) = IDs.at(i);
1219 passed_count++;
1220 }
1221 } else if (strcmp(comparator, "=") == 0) {
1222 if (float(R) == threshold) {
1223 output_object_IDs.at(passed_count) = IDs.at(i);
1224 passed_count++;
1225 }
1226 }
1227 } else {
1228 std::cerr << "WARNING: Object data not of type UINT, INT, or FLOAT. Filtering for other types not yet supported." << std::endl;
1229 }
1230 }
1231 }
1232
1233 output_object_IDs.resize(passed_count);
1234
1235 return output_object_IDs;
1236}
1237
1238void Context::translateObject(uint ObjID, const vec3 &shift) const {
1239#ifdef HELIOS_DEBUG
1240 if (!doesObjectExist(ObjID)) {
1241 helios_runtime_error("ERROR (Context::translateObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1242 }
1243#endif
1244 getObjectPointer(ObjID)->translate(shift);
1245}
1246
1247void Context::translateObject(const std::vector<uint> &ObjIDs, const vec3 &shift) const {
1248 for (uint ID: ObjIDs) {
1249 translateObject(ID, shift);
1250 }
1251}
1252
1253void Context::rotateObject(uint ObjID, float rotation_radians, const char *rotation_axis_xyz) const {
1254#ifdef HELIOS_DEBUG
1255 if (!doesObjectExist(ObjID)) {
1256 helios_runtime_error("ERROR (Context::rotateObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1257 }
1258#endif
1259 getObjectPointer(ObjID)->rotate(rotation_radians, rotation_axis_xyz);
1260}
1261
1262void Context::rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const char *rotation_axis_xyz) const {
1263 for (uint ID: ObjIDs) {
1264 rotateObject(ID, rotation_radians, rotation_axis_xyz);
1265 }
1266}
1267
1268void Context::rotateObject(uint ObjID, float rotation_radians, const vec3 &rotation_axis_vector) const {
1269#ifdef HELIOS_DEBUG
1270 if (!doesObjectExist(ObjID)) {
1271 helios_runtime_error("ERROR (Context::rotateObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1272 }
1273#endif
1274 getObjectPointer(ObjID)->rotate(rotation_radians, rotation_axis_vector);
1275}
1276
1277void Context::rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_axis_vector) const {
1278 for (uint ID: ObjIDs) {
1279 rotateObject(ID, rotation_radians, rotation_axis_vector);
1280 }
1281}
1282
1283void Context::rotateObject(uint ObjID, float rotation_radians, const vec3 &rotation_origin, const vec3 &rotation_axis_vector) const {
1284#ifdef HELIOS_DEBUG
1285 if (!doesObjectExist(ObjID)) {
1286 helios_runtime_error("ERROR (Context::rotateObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1287 }
1288#endif
1289 getObjectPointer(ObjID)->rotate(rotation_radians, rotation_origin, rotation_axis_vector);
1290}
1291
1292void Context::rotateObject(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_origin, const vec3 &rotation_axis_vector) const {
1293 for (uint ID: ObjIDs) {
1294 rotateObject(ID, rotation_radians, rotation_origin, rotation_axis_vector);
1295 }
1296}
1297
1298void Context::rotateObjectAboutOrigin(uint ObjID, float rotation_radians, const vec3 &rotation_axis_vector) const {
1299#ifdef HELIOS_DEBUG
1300 if (!doesObjectExist(ObjID)) {
1301 helios_runtime_error("ERROR (Context::rotateObjectAboutOrigin): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1302 }
1303#endif
1304 getObjectPointer(ObjID)->rotate(rotation_radians, objects.at(ObjID)->object_origin, rotation_axis_vector);
1305}
1306
1307void Context::rotateObjectAboutOrigin(const std::vector<uint> &ObjIDs, float rotation_radians, const vec3 &rotation_axis_vector) const {
1308 for (uint ID: ObjIDs) {
1309 rotateObject(ID, rotation_radians, objects.at(ID)->object_origin, rotation_axis_vector);
1310 }
1311}
1312
1313void Context::scaleObject(uint ObjID, const helios::vec3 &scalefact) const {
1314#ifdef HELIOS_DEBUG
1315 if (!doesObjectExist(ObjID)) {
1316 helios_runtime_error("ERROR (Context::scaleObject): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1317 }
1318#endif
1319 getObjectPointer(ObjID)->scale(scalefact);
1320}
1321
1322void Context::scaleObject(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const {
1323 for (uint ID: ObjIDs) {
1324 scaleObject(ID, scalefact);
1325 }
1326}
1327
1328void Context::scaleObjectAboutCenter(uint ObjID, const helios::vec3 &scalefact) const {
1329#ifdef HELIOS_DEBUG
1330 if (!doesObjectExist(ObjID)) {
1331 helios_runtime_error("ERROR (Context::scaleObjectAboutCenter): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1332 }
1333#endif
1334 getObjectPointer(ObjID)->scaleAboutCenter(scalefact);
1335}
1336
1337void Context::scaleObjectAboutCenter(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const {
1338 for (uint ID: ObjIDs) {
1339 scaleObjectAboutCenter(ID, scalefact);
1340 }
1341}
1342
1343void Context::scaleObjectAboutPoint(uint ObjID, const helios::vec3 &scalefact, const helios::vec3 &point) const {
1344#ifdef HELIOS_DEBUG
1345 if (!doesObjectExist(ObjID)) {
1346 helios_runtime_error("ERROR (Context::scaleObjectAboutPoint): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1347 }
1348#endif
1349 getObjectPointer(ObjID)->scaleAboutPoint(scalefact, point);
1350}
1351
1352void Context::scaleObjectAboutPoint(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact, const helios::vec3 &point) const {
1353 for (uint ID: ObjIDs) {
1354 scaleObjectAboutPoint(ID, scalefact, point);
1355 }
1356}
1357
1358void Context::scaleObjectAboutOrigin(uint ObjID, const helios::vec3 &scalefact) const {
1359#ifdef HELIOS_DEBUG
1360 if (!doesObjectExist(ObjID)) {
1361 helios_runtime_error("ERROR (Context::scaleObjectAboutOrigin): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1362 }
1363#endif
1364 getObjectPointer(ObjID)->scaleAboutPoint(scalefact, objects.at(ObjID)->object_origin);
1365}
1366
1367void Context::scaleObjectAboutOrigin(const std::vector<uint> &ObjIDs, const helios::vec3 &scalefact) const {
1368 for (uint ID: ObjIDs) {
1369 scaleObjectAboutPoint(ID, scalefact, objects.at(ID)->object_origin);
1370 }
1371}
1372
1373std::vector<uint> Context::getObjectPrimitiveUUIDs(uint ObjID) const {
1374#ifdef HELIOS_DEBUG
1375 if (!doesObjectExist(ObjID) && ObjID != 0) {
1376 helios_runtime_error("ERROR (Context::getObjectPrimitiveUUIDs): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1377 }
1378#endif
1379
1380 if (ObjID == 0) {
1381 // \todo This is inefficient and should be improved by storing the UUIDs for all objID = 0 primitives in the Context.
1382 std::vector<uint> UUIDs;
1383 UUIDs.reserve(getPrimitiveCount());
1384 for (uint UUID: getAllUUIDs()) {
1385 if (getPrimitiveParentObjectID(UUID) == 0) {
1386 UUIDs.push_back(UUID);
1387 }
1388 }
1389 return UUIDs;
1390 }
1391
1392 return getObjectPointer(ObjID)->getPrimitiveUUIDs();
1393}
1394
1395std::vector<uint> Context::getObjectPrimitiveUUIDs(const std::vector<uint> &ObjIDs) const {
1396 std::vector<uint> output_UUIDs;
1397
1398 for (uint ObjID: ObjIDs) {
1399#ifdef HELIOS_DEBUG
1400 if (!doesObjectExist(ObjID)) {
1401 helios_runtime_error("ERROR (Context::getObjectPrimitiveUUIDs): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1402 }
1403#endif
1404 const std::vector<uint> &current_UUIDs = getObjectPrimitiveUUIDs(ObjID);
1405 output_UUIDs.insert(output_UUIDs.end(), current_UUIDs.begin(), current_UUIDs.end());
1406 }
1407 return output_UUIDs;
1408}
1409
1410std::vector<uint> Context::getObjectPrimitiveUUIDs(const std::vector<std::vector<uint>> &ObjIDs) const {
1411 std::vector<uint> output_UUIDs;
1412
1413 for (uint j = 0; j < ObjIDs.size(); j++) {
1414 for (uint i = 0; i < ObjIDs.at(j).size(); i++) {
1415#ifdef HELIOS_DEBUG
1416 if (!doesObjectExist(ObjIDs.at(j).at(i))) {
1417 helios_runtime_error("ERROR (Context::getObjectPrimitiveUUIDs): Object ID of " + std::to_string(ObjIDs.at(j).at(i)) + " not found in the context.");
1418 }
1419#endif
1420
1421 const std::vector<uint> &current_UUIDs = getObjectPointer(ObjIDs.at(j).at(i))->getPrimitiveUUIDs();
1422 output_UUIDs.insert(output_UUIDs.end(), current_UUIDs.begin(), current_UUIDs.end());
1423 }
1424 }
1425 return output_UUIDs;
1426}
1427
1429 if (ObjID == 0) {
1430 return OBJECT_TYPE_NONE;
1431 }
1432#ifdef HELIOS_DEBUG
1433 if (!doesObjectExist(ObjID)) {
1434 helios_runtime_error("ERROR (Context::getObjectType): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1435 }
1436#endif
1437 return getObjectPointer(ObjID)->getObjectType();
1438}
1439
1441#ifdef HELIOS_DEBUG
1442 if (!doesObjectExist(ObjID)) {
1443 helios_runtime_error("ERROR (Context::getTileObjectAreaRatio): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1444 }
1445#endif
1447 std::cerr << "WARNING (Context::getTileObjectAreaRatio): ObjectID " << ObjID << " is not a tile object. Skipping..." << std::endl;
1448 return 0.0;
1449 }
1450
1451 if (!(getObjectPointer(ObjID)->arePrimitivesComplete())) {
1452 std::cerr << "WARNING (Context::getTileObjectAreaRatio): ObjectID " << ObjID << " is missing primitives. Area ratio calculated is area of non-missing subpatches divided by the area of an individual subpatch." << std::endl;
1453 }
1454
1455 const int2 &subdiv = getTileObjectPointer(ObjID)->getSubdivisionCount();
1456 if (subdiv.x == 1 && subdiv.y == 1) {
1457 return 1.0;
1458 }
1459
1460 float area = getTileObjectPointer(ObjID)->getArea();
1461 const vec2 size = getTileObjectPointer(ObjID)->getSize();
1462
1463 float subpatch_area = size.x * size.y / scast<float>(subdiv.x * subdiv.y);
1464 return area / subpatch_area;
1465}
1466
1467std::vector<float> Context::getTileObjectAreaRatio(const std::vector<uint> &ObjIDs) const {
1468 std::vector<float> AreaRatios(ObjIDs.size());
1469 for (uint i = 0; i < ObjIDs.size(); i++) {
1470 AreaRatios.at(i) = getTileObjectAreaRatio(ObjIDs.at(i));
1471 }
1472
1473 return AreaRatios;
1474}
1475
1476void Context::setTileObjectSubdivisionCount(const std::vector<uint> &ObjIDs, const int2 &new_subdiv) {
1477 // check that all objects are Tile Objects, and get vector of texture files
1478 std::vector<uint> tile_ObjectIDs;
1479 std::vector<uint> textured_tile_ObjectIDs;
1480
1481
1482 std::vector<std::string> tex;
1483
1484 for (uint ObjID: ObjIDs) {
1485#ifdef HELIOS_DEBUG
1486 if (!doesObjectExist(ObjID)) {
1487 helios_runtime_error("ERROR (Context::setTileObjectSubdivisionCount): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1488 }
1489#endif
1490
1491 // check if the object ID is a tile object and if it is add it the tile_ObjectIDs vector
1493 std::cerr << "WARNING (Context::setTileObjectSubdivisionCount): ObjectID " << ObjID << " is not a tile object. Skipping..." << std::endl;
1494 } else if (!(getObjectPointer(ObjID)->arePrimitivesComplete())) {
1495 std::cerr << "WARNING (Context::setTileObjectSubdivisionCount): ObjectID " << ObjID << " is missing primitives. Skipping..." << std::endl;
1496 } else {
1497 // test if the tile is textured and push into two different vectors
1498 Patch *p = getPatchPointer_private(getObjectPointer(ObjID)->getPrimitiveUUIDs().at(0));
1499 if (!p->hasTexture()) { // no texture
1500 tile_ObjectIDs.push_back(ObjID);
1501 } else { // texture
1502 textured_tile_ObjectIDs.push_back(ObjID);
1503 tex.push_back(p->getTextureFile());
1504 }
1505 }
1506 }
1507
1508 // Here just call setSubdivisionCount directly for the non-textured tile objects
1509 for (unsigned int tile_ObjectID: tile_ObjectIDs) {
1510 Tile *current_object_pointer = getTileObjectPointer(tile_ObjectID);
1511 const std::vector<uint> &UUIDs_old = current_object_pointer->getPrimitiveUUIDs();
1512
1513 vec2 size = current_object_pointer->getSize();
1514 vec3 center = current_object_pointer->getCenter();
1515 vec3 normal = current_object_pointer->getNormal();
1516 SphericalCoord rotation = cart2sphere(normal);
1517 RGBcolor color = getPrimitiveColor(UUIDs_old.front());
1518
1519 std::vector<uint> UUIDs_new = addTile(center, size, rotation, new_subdiv, color);
1520
1521 for (uint UUID: UUIDs_new) {
1522 getPrimitivePointer_private(UUID)->setParentObjectID(tile_ObjectID);
1523 }
1524
1525 current_object_pointer->setPrimitiveUUIDs(UUIDs_new);
1526 current_object_pointer->setSubdivisionCount(new_subdiv);
1527 deletePrimitive(UUIDs_old);
1528 }
1529
1530 // get a vector of unique texture files that are represented in the input tile objects
1531 sort(tex.begin(), tex.end());
1532 std::vector<std::string>::iterator it;
1533 it = std::unique(tex.begin(), tex.end());
1534 tex.resize(std::distance(tex.begin(), it));
1535
1536 // create object templates for all the unique texture files
1537 std::vector<uint> object_templates;
1538 std::vector<std::vector<uint>> template_primitives;
1539 for (uint j = 0; j < tex.size(); j++) {
1540 // create a template object for the current texture
1541 uint object_template = addTileObject(make_vec3(0, 0, 0), make_vec2(1, 1), nullrotation, new_subdiv, tex.at(j).c_str());
1542 object_templates.emplace_back(object_template);
1543 std::vector<uint> object_primitives = getTileObjectPointer(object_template)->getPrimitiveUUIDs();
1544 template_primitives.emplace_back(object_primitives);
1545 }
1546
1547 // keep loop over objects on the outside, otherwise need to update textured_tile_ObjectIDs vector all the time
1548 // for each textured tile object
1549 for (uint i = 0; i < textured_tile_ObjectIDs.size(); i++) {
1550 // get info from current object
1551 Tile *current_object_pointer = getTileObjectPointer(textured_tile_ObjectIDs.at(i));
1552 std::string current_texture_file = current_object_pointer->getTextureFile();
1553
1554 std::vector<uint> UUIDs_old = current_object_pointer->getPrimitiveUUIDs();
1555
1556 vec2 size = current_object_pointer->getSize();
1557 vec3 center = current_object_pointer->getCenter();
1558 vec3 normal = current_object_pointer->getNormal();
1559 SphericalCoord rotation = cart2sphere(normal);
1560
1561 // for unique textures
1562 for (uint j = 0; j < tex.size(); j++) {
1563 // if the current tile object has the same texture file as the current unique texture file
1564 if (current_texture_file == tex.at(j)) {
1565 // copy the template primitives and create a new tile with them
1566 std::vector<uint> new_primitives = copyPrimitive(template_primitives.at(j));
1567
1568 // change the objectID for the new primitives
1569 setPrimitiveParentObjectID(new_primitives, textured_tile_ObjectIDs.at(i));
1570 current_object_pointer->setPrimitiveUUIDs(new_primitives);
1571 current_object_pointer->setSubdivisionCount(new_subdiv);
1572
1573 // delete the original object primitives
1574 deletePrimitive(UUIDs_old);
1575
1576 float IM[16];
1578 current_object_pointer->setTransformationMatrix(IM);
1579
1580 current_object_pointer->scale(make_vec3(size.x, size.y, 1));
1581
1582 // transform based on original object data
1583 if (rotation.elevation != 0) {
1584 current_object_pointer->rotate(-rotation.elevation, "x");
1585 }
1586 if (rotation.azimuth != 0) {
1587 current_object_pointer->rotate(rotation.azimuth, "z");
1588 }
1589 current_object_pointer->translate(center);
1590 }
1591 }
1592 }
1593
1594
1595 // delete the template (objects and primitives)
1596 deleteObject(object_templates);
1597}
1598
1599void Context::setTileObjectSubdivisionCount(const std::vector<uint> &ObjIDs, float area_ratio) {
1600 // check that all objects are Tile Objects, and get vector of texture files
1601 std::vector<uint> tile_ObjectIDs;
1602 std::vector<uint> textured_tile_ObjectIDs;
1603
1604 std::vector<std::string> tex;
1605 // for(uint i=1;i<ObjectIDs.size();i++)
1606 for (uint ObjID: ObjIDs) {
1607#ifdef HELIOS_DEBUG
1608 if (!doesObjectExist(ObjID)) {
1609 helios_runtime_error("ERROR (Context::setTileObjectSubdivisionCount): Object ID of " + std::to_string(ObjID) + " not found in the context.");
1610 }
1611#endif
1612
1613 // check if the object ID is a tile object and if it is add it the tile_ObjectIDs vector
1615 std::cerr << "WARNING (Context::setTileObjectSubdivisionCount): ObjectID " << ObjID << " is not a tile object. Skipping..." << std::endl;
1616 } else if (!(getObjectPointer(ObjID)->arePrimitivesComplete())) {
1617 std::cerr << "WARNING (Context::setTileObjectSubdivisionCount): ObjectID " << ObjID << " is missing primitives. Skipping..." << std::endl;
1618 } else {
1619 // test if the tile is textured and push into two different vectors
1620 Patch *p = getPatchPointer_private(getObjectPointer(ObjID)->getPrimitiveUUIDs().at(0));
1621 if (!p->hasTexture()) { // no texture
1622 tile_ObjectIDs.push_back(ObjID);
1623 } else { // texture
1624 textured_tile_ObjectIDs.push_back(ObjID);
1625 tex.push_back(p->getTextureFile());
1626 }
1627 }
1628 }
1629
1630 // Here just call setSubdivisionCount directly for the non-textured tile objects
1631 for (uint i = 0; i < tile_ObjectIDs.size(); i++) {
1632 Tile *current_object_pointer = getTileObjectPointer(tile_ObjectIDs.at(i));
1633 std::vector<uint> UUIDs_old = current_object_pointer->getPrimitiveUUIDs();
1634
1635 vec2 size = current_object_pointer->getSize();
1636 vec3 center = current_object_pointer->getCenter();
1637 vec3 normal = current_object_pointer->getNormal();
1638 SphericalCoord rotation = cart2sphere(normal);
1639 RGBcolor color = getPrimitiveColor(UUIDs_old.front());
1640
1641 float tile_area = current_object_pointer->getArea();
1642
1643 // subpatch dimensions needed to keep the correct ratio and have the solid fraction area = the input area
1644 float subpatch_dimension = sqrtf(tile_area / area_ratio);
1645 float subpatch_per_x = size.x / subpatch_dimension;
1646 float subpatch_per_y = size.y / subpatch_dimension;
1647
1648 float option_1_AR = (tile_area / (size.x / ceil(subpatch_per_x) * size.y / floor(subpatch_per_y))) - area_ratio;
1649 float option_2_AR = (tile_area / (size.x / floor(subpatch_per_x) * size.y / ceil(subpatch_per_y))) - area_ratio;
1650
1651 int2 new_subdiv;
1652 if ((int) area_ratio == 1) {
1653 new_subdiv = make_int2(1, 1);
1654 } else if (option_1_AR >= option_2_AR) {
1655 new_subdiv = make_int2(ceil(subpatch_per_x), floor(subpatch_per_y));
1656 } else {
1657 new_subdiv = make_int2(floor(subpatch_per_x), ceil(subpatch_per_y));
1658 }
1659
1660
1661 std::vector<uint> UUIDs_new = addTile(center, size, rotation, new_subdiv, color);
1662
1663 for (uint UUID: UUIDs_new) {
1664 getPrimitivePointer_private(UUID)->setParentObjectID(tile_ObjectIDs.at(i));
1665 }
1666
1667 current_object_pointer->setPrimitiveUUIDs(UUIDs_new);
1668 current_object_pointer->setSubdivisionCount(new_subdiv);
1669 deletePrimitive(UUIDs_old);
1670 }
1671
1672 // get a vector of unique texture files that are represented in the input tile objects
1673 sort(tex.begin(), tex.end());
1674 std::vector<std::string>::iterator it;
1675 it = std::unique(tex.begin(), tex.end());
1676 tex.resize(std::distance(tex.begin(), it));
1677
1678 // create object templates for all the unique texture files
1679 // the assumption here is that all tile objects with the same texture have the same aspect ratio
1680 // if this is not true then the copying method won't work well because a new template will need to be created for each texture/aspect ratio combination
1681
1682 std::vector<uint> object_templates;
1683 std::vector<std::vector<uint>> template_primitives;
1684 for (uint j = 0; j < tex.size(); j++) {
1685 // here we just want to get one tile object with the matching texture
1686 uint ii;
1687 for (uint i = 0; i < textured_tile_ObjectIDs.size(); i++) {
1688 // get info from current object
1689 Tile *current_object_pointer_b = getTileObjectPointer(textured_tile_ObjectIDs.at(i));
1690 std::string current_texture_file_b = current_object_pointer_b->getTextureFile();
1691 // if the current tile object has the same texture file as the current unique texture file
1692 if (current_texture_file_b == tex.at(j)) {
1693 ii = i;
1694 break;
1695 }
1696 }
1697
1698 // get info from current object
1699 Tile *current_object_pointer = getTileObjectPointer(textured_tile_ObjectIDs.at(ii));
1700 vec2 tile_size = current_object_pointer->getSize();
1701 float tile_area = current_object_pointer->getArea();
1702
1703 // subpatch dimensions needed to keep the correct ratio and have the solid fraction area = the input area
1704 float subpatch_dimension = sqrtf(tile_area / area_ratio);
1705 float subpatch_per_x = tile_size.x / subpatch_dimension;
1706 float subpatch_per_y = tile_size.y / subpatch_dimension;
1707
1708 float option_1_AR = (tile_area / (tile_size.x / ceil(subpatch_per_x) * tile_size.y / floor(subpatch_per_y))) - area_ratio;
1709 float option_2_AR = (tile_area / (tile_size.x / floor(subpatch_per_x) * tile_size.y / ceil(subpatch_per_y))) - area_ratio;
1710
1711 int2 new_subdiv;
1712 if ((int) area_ratio == 1) {
1713 new_subdiv = make_int2(1, 1);
1714 } else if (option_1_AR >= option_2_AR) {
1715 new_subdiv = make_int2(ceil(subpatch_per_x), floor(subpatch_per_y));
1716 } else {
1717 new_subdiv = make_int2(floor(subpatch_per_x), ceil(subpatch_per_y));
1718 }
1719
1720 // create a template object for the current texture
1721 uint object_template = addTileObject(make_vec3(0, 0, 0), make_vec2(1, 1), nullrotation, new_subdiv, tex.at(j).c_str());
1722 object_templates.emplace_back(object_template);
1723 std::vector<uint> object_primitives = getTileObjectPointer(object_template)->getPrimitiveUUIDs();
1724 template_primitives.emplace_back(object_primitives);
1725 }
1726
1727 // keep loop over objects on the outside, otherwise need to update textured_tile_ObjectIDs vector all the time
1728 // for each textured tile object
1729 for (uint i = 0; i < textured_tile_ObjectIDs.size(); i++) {
1730 // get info from current object
1731 Tile *current_object_pointer = getTileObjectPointer(textured_tile_ObjectIDs.at(i));
1732 // std::string current_texture_file = getPrimitivePointer_private(current_object_pointer->getPrimitiveUUIDs().at(0))->getTextureFile();
1733 std::string current_texture_file = current_object_pointer->getTextureFile();
1734 // std::cout << "current_texture_file for ObjID " << textured_tile_ObjectIDs.at(i) << " = " << current_texture_file << std::endl;
1735 std::vector<uint> UUIDs_old = current_object_pointer->getPrimitiveUUIDs();
1736
1737 vec2 size = current_object_pointer->getSize();
1738 vec3 center = current_object_pointer->getCenter();
1739 vec3 normal = current_object_pointer->getNormal();
1740 SphericalCoord rotation = cart2sphere(normal);
1741
1742 // for unique textures
1743 for (uint j = 0; j < tex.size(); j++) {
1744 // if the current tile object has the same texture file as the current unique texture file
1745 if (current_texture_file == tex.at(j)) {
1746 // copy the template primitives and create a new tile with them
1747 std::vector<uint> new_primitives = copyPrimitive(template_primitives.at(j));
1748
1749 // change the objectID for the new primitives
1750 setPrimitiveParentObjectID(new_primitives, textured_tile_ObjectIDs.at(i));
1751
1752 int2 new_subdiv = getTileObjectPointer(object_templates.at(j))->getSubdivisionCount();
1753 current_object_pointer->setPrimitiveUUIDs(new_primitives);
1754 current_object_pointer->setSubdivisionCount(new_subdiv);
1755
1756 // delete the original object primitives
1757 deletePrimitive(UUIDs_old);
1758
1759 float IM[16];
1761 current_object_pointer->setTransformationMatrix(IM);
1762
1763 current_object_pointer->scale(make_vec3(size.x, size.y, 1));
1764
1765 if (rotation.elevation != 0) {
1766 current_object_pointer->rotate(-rotation.elevation, "x");
1767 }
1768 if (rotation.azimuth != 0) {
1769 current_object_pointer->rotate(rotation.azimuth, "z");
1770 }
1771 current_object_pointer->translate(center);
1772 }
1773 }
1774 }
1775
1776 // delete the template (objects and primitives)
1777 deleteObject(object_templates);
1778}
1779
1780
1781std::vector<uint> Context::addSphere(uint Ndivs, const vec3 &center, float radius) {
1782 RGBcolor color = make_RGBcolor(0.f, 0.75f, 0.f); // Default color is green
1783
1784 return addSphere(Ndivs, center, radius, color);
1785}
1786
1787std::vector<uint> Context::addSphere(uint Ndivs, const vec3 &center, float radius, const RGBcolor &color) {
1788 std::vector<uint> UUID;
1789
1790 float dtheta = PI_F / float(Ndivs);
1791 float dphi = 2.0f * PI_F / float(Ndivs);
1792
1793 // bottom cap
1794 for (int j = 0; j < Ndivs; j++) {
1795 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F, 0));
1796 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, float(j) * dphi));
1797 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, float(j + 1) * dphi));
1798
1799 UUID.push_back(addTriangle(v0, v1, v2, color));
1800 }
1801
1802 // top cap
1803 for (int j = 0; j < Ndivs; j++) {
1804 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F, 0));
1805 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, float(j) * dphi));
1806 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, float(j + 1) * dphi));
1807
1808 UUID.push_back(addTriangle(v2, v1, v0, color));
1809 }
1810
1811 // middle
1812 for (int j = 0; j < Ndivs; j++) {
1813 for (int i = 1; i < Ndivs - 1; i++) {
1814 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i) * dtheta, float(j) * dphi));
1815 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i + 1) * dtheta, float(j) * dphi));
1816 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i + 1) * dtheta, float(j + 1) * dphi));
1817 vec3 v3 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i) * dtheta, float(j + 1) * dphi));
1818
1819 UUID.push_back(addTriangle(v0, v1, v2, color));
1820 UUID.push_back(addTriangle(v0, v2, v3, color));
1821 }
1822 }
1823
1824 return UUID;
1825}
1826
1827std::vector<uint> Context::addSphere(uint Ndivs, const vec3 &center, float radius, const char *texturefile) {
1828 if (!validateTextureFileExtenstion(texturefile)) {
1829 helios_runtime_error("ERROR (Context::addSphere): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
1830 } else if (!doesTextureFileExist(texturefile)) {
1831 helios_runtime_error("ERROR (Context::addSphere): Texture file " + std::string(texturefile) + " does not exist.");
1832 }
1833
1834 std::vector<uint> UUID;
1835
1836 float dtheta = PI_F / float(Ndivs);
1837 float dphi = 2.0f * PI_F / float(Ndivs);
1838
1839 // bottom cap
1840 for (int j = 0; j < Ndivs; j++) {
1841 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F, 0));
1842 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, float(j) * dphi));
1843 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + dtheta, float(j + 1) * dphi));
1844
1845 vec3 n0 = v0 - center;
1846 n0.normalize();
1847 vec3 n1 = v1 - center;
1848 n1.normalize();
1849 vec3 n2 = v2 - center;
1850 n2.normalize();
1851
1852 vec2 uv0 = make_vec2(1.f - atan2f(sin((float(j) + 0.5f) * dphi), -cos((float(j) + 0.5f) * dphi)) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
1853 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
1854 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
1855
1856 if (j == Ndivs - 1) {
1857 uv2.x = 1;
1858 }
1859
1860 uint triangle_uuid = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
1861 if (getPrimitiveArea(triangle_uuid) > 0) {
1862 UUID.push_back(triangle_uuid);
1863 } else {
1864 deletePrimitive(triangle_uuid);
1865 }
1866 }
1867
1868 // top cap
1869 for (int j = 0; j < Ndivs; j++) {
1870 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F, 0));
1871 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, float(j + 1) * dphi));
1872 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, 0.5f * PI_F - dtheta, float(j) * dphi));
1873
1874 vec3 n0 = v0 - center;
1875 n0.normalize();
1876 vec3 n1 = v1 - center;
1877 n1.normalize();
1878 vec3 n2 = v2 - center;
1879 n2.normalize();
1880
1881 vec2 uv0 = make_vec2(1.f - atan2f(sinf((float(j) + 0.5f) * dphi), -cosf((float(j) + 0.5f) * dphi)) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
1882 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
1883 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
1884
1885 if (j == Ndivs - 1) {
1886 uv2.x = 1;
1887 }
1888
1889 uint triangle_uuid = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
1890 if (getPrimitiveArea(triangle_uuid) > 0) {
1891 UUID.push_back(triangle_uuid);
1892 } else {
1893 deletePrimitive(triangle_uuid);
1894 }
1895 }
1896
1897 // middle
1898 for (int j = 0; j < Ndivs; j++) {
1899 for (int i = 1; i < Ndivs - 1; i++) {
1900 vec3 v0 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i) * dtheta, float(j) * dphi));
1901 vec3 v1 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i + 1) * dtheta, float(j) * dphi));
1902 vec3 v2 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i + 1) * dtheta, float(j + 1) * dphi));
1903 vec3 v3 = center + sphere2cart(make_SphericalCoord(radius, -0.5f * PI_F + float(i) * dtheta, float(j + 1) * dphi));
1904
1905 vec3 n0 = v0 - center;
1906 n0.normalize();
1907 vec3 n1 = v1 - center;
1908 n1.normalize();
1909 vec3 n2 = v2 - center;
1910 n2.normalize();
1911 vec3 n3 = v3 - center;
1912 n3.normalize();
1913
1914 vec2 uv0 = make_vec2(1.f - atan2f(n0.x, -n0.y) / (2.f * PI_F) - 0.5f, 1.f - n0.z * 0.5f - 0.5f);
1915 vec2 uv1 = make_vec2(1.f - atan2f(n1.x, -n1.y) / (2.f * PI_F) - 0.5f, 1.f - n1.z * 0.5f - 0.5f);
1916 vec2 uv2 = make_vec2(1.f - atan2f(n2.x, -n2.y) / (2.f * PI_F) - 0.5f, 1.f - n2.z * 0.5f - 0.5f);
1917 vec2 uv3 = make_vec2(1.f - atan2f(n3.x, -n3.y) / (2.f * PI_F) - 0.5f, 1.f - n3.z * 0.5f - 0.5f);
1918
1919 if (j == Ndivs - 1) {
1920 uv2.x = 1;
1921 uv3.x = 1;
1922 }
1923
1924 uint triangle_uuid1 = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
1925 if (getPrimitiveArea(triangle_uuid1) > 0) {
1926 UUID.push_back(triangle_uuid1);
1927 } else {
1928 deletePrimitive(triangle_uuid1);
1929 }
1930 uint triangle_uuid2 = addTriangle(v0, v2, v3, texturefile, uv0, uv2, uv3);
1931 if (getPrimitiveArea(triangle_uuid2) > 0) {
1932 UUID.push_back(triangle_uuid2);
1933 } else {
1934 deletePrimitive(triangle_uuid2);
1935 }
1936 }
1937 }
1938
1939 return UUID;
1940}
1941
1942std::vector<uint> Context::addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv) {
1943 RGBcolor color(0.f, 0.75f, 0.f); // Default color is green
1944
1945 return addTile(center, size, rotation, subdiv, color);
1946}
1947
1948std::vector<uint> Context::addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const RGBcolor &color) {
1949 vec2 subsize;
1950 subsize.x = size.x / float(subdiv.x);
1951 subsize.y = size.y / float(subdiv.y);
1952
1953 std::vector<uint> UUID(subdiv.x * subdiv.y);
1954
1955 size_t t = 0;
1956 for (uint j = 0; j < subdiv.y; j++) {
1957 for (uint i = 0; i < subdiv.x; i++) {
1958 vec3 subcenter = make_vec3(-0.5f * size.x + (float(i) + 0.5f) * subsize.x, -0.5f * size.y + (float(j) + 0.5f) * subsize.y, 0);
1959
1960 UUID[t] = addPatch(subcenter, subsize, make_SphericalCoord(0, 0), color);
1961
1962 if (rotation.elevation != 0.f) {
1963 getPrimitivePointer_private(UUID[t])->rotate(-rotation.elevation, "x");
1964 }
1965 if (rotation.azimuth != 0.f) {
1966 getPrimitivePointer_private(UUID[t])->rotate(-rotation.azimuth, "z");
1967 }
1968 getPrimitivePointer_private(UUID[t])->translate(center);
1969
1970 t++;
1971 }
1972 }
1973
1974 return UUID;
1975}
1976
1977std::vector<uint> Context::addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile) {
1978 return addTile(center, size, rotation, subdiv, texturefile, make_int2(1, 1));
1979}
1980
1981std::vector<uint> Context::addTile(const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const int2 &subdiv, const char *texturefile, const int2 &texture_repeat) {
1982 if (!validateTextureFileExtenstion(texturefile)) {
1983 helios_runtime_error("ERROR (Context::addTile): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
1984 } else if (!doesTextureFileExist(texturefile)) {
1985 helios_runtime_error("ERROR (Context::addTile): Texture file " + std::string(texturefile) + " does not exist.");
1986 } else if (texture_repeat.x < 1 || texture_repeat.y < 1) {
1987 helios_runtime_error("ERROR (Context::addTile): Number of texture repeats must be greater than 0.");
1988 }
1989
1990 // Automatically resize the repeat count so that it evenly divides the subdivisions.
1991 int2 repeat = texture_repeat;
1992 repeat.x = std::min(subdiv.x, repeat.x);
1993 repeat.y = std::min(subdiv.y, repeat.y);
1994 while (subdiv.x % repeat.x != 0) {
1995 repeat.x--;
1996 }
1997 while (subdiv.y % repeat.y != 0) {
1998 repeat.y--;
1999 }
2000
2001 std::vector<uint> UUID;
2002
2003 vec2 subsize;
2004 subsize.x = size.x / float(subdiv.x);
2005 subsize.y = size.y / float(subdiv.y);
2006
2007 std::vector<helios::vec2> uv(4);
2008 int2 sub_per_repeat;
2009 sub_per_repeat.x = subdiv.x / repeat.x;
2010 sub_per_repeat.y = subdiv.y / repeat.y;
2011 vec2 uv_sub;
2012 uv_sub.x = 1.f / float(sub_per_repeat.x);
2013 uv_sub.y = 1.f / float(sub_per_repeat.y);
2014
2015 addTexture(texturefile);
2016
2017 const int2 &sz = textures.at(texturefile).getImageResolution();
2018 if (subdiv.x >= repeat.x * sz.x || subdiv.y >= repeat.y * sz.y) {
2019 helios_runtime_error("ERROR (Context::addTile): The resolution of the texture image '" + std::string(texturefile) + "' is lower than the number of tile subdivisions. Increase resolution of the texture image.");
2020 }
2021
2022 for (uint j = 0; j < subdiv.y; j++) {
2023 for (uint i = 0; i < subdiv.x; i++) {
2024 vec3 subcenter = make_vec3(-0.5f * size.x + (float(i) + 0.5f) * subsize.x, -0.5f * size.y + (float(j) + 0.5f) * subsize.y, 0.f);
2025
2026 uint i_local = i % sub_per_repeat.x;
2027 uint j_local = j % sub_per_repeat.y;
2028 uv.at(0) = make_vec2(float(i_local) * uv_sub.x, float(j_local) * uv_sub.y);
2029 uv.at(1) = make_vec2(float(i_local + 1) * uv_sub.x, float(j_local) * uv_sub.y);
2030 uv.at(2) = make_vec2(float(i_local + 1) * uv_sub.x, float(j_local + 1) * uv_sub.y);
2031 uv.at(3) = make_vec2(float(i_local) * uv_sub.x, float(j_local + 1) * uv_sub.y);
2032
2033 auto *patch_new = (new Patch(texturefile, uv, textures, 0, currentUUID));
2034
2035 if (patch_new->getSolidFraction() == 0) {
2036 delete patch_new;
2037 continue;
2038 }
2039
2040 assert(size.x > 0.f && size.y > 0.f);
2041 patch_new->scale(make_vec3(subsize.x, subsize.y, 1));
2042
2043 patch_new->translate(subcenter);
2044
2045 if (rotation.elevation != 0) {
2046 patch_new->rotate(-rotation.elevation, "x");
2047 }
2048 if (rotation.azimuth != 0) {
2049 patch_new->rotate(-rotation.azimuth, "z");
2050 }
2051
2052 patch_new->translate(center);
2053
2054 primitives[currentUUID] = patch_new;
2055 currentUUID++;
2056 UUID.push_back(currentUUID - 1);
2057 }
2058 }
2059
2060 return UUID;
2061}
2062
2063std::vector<uint> Context::addTube(uint Ndivs, const std::vector<vec3> &nodes, const std::vector<float> &radius) {
2064 std::vector<RGBcolor> color(nodes.size(), make_RGBcolor(0.f, 0.75f, 0.f));
2065
2066 return addTube(Ndivs, nodes, radius, color);
2067}
2068
2069std::vector<uint> Context::addTube(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const std::vector<RGBcolor> &color) {
2070 const uint node_count = nodes.size();
2071
2072 if (node_count == 0) {
2073 helios_runtime_error("ERROR (Context::addTube): Node and radius arrays are empty.");
2074 } else if (node_count != radius.size()) {
2075 helios_runtime_error("ERROR (Context::addTube): Size of `nodes' and `radius' arguments must agree.");
2076 } else if (node_count != color.size()) {
2077 helios_runtime_error("ERROR (Context::addTube): Size of `nodes' and `color' arguments must agree.");
2078 }
2079
2080 vec3 vec, convec;
2081 std::vector<float> cfact(radial_subdivisions + 1);
2082 std::vector<float> sfact(radial_subdivisions + 1);
2083 std::vector<std::vector<vec3>> xyz;
2084 resize_vector(xyz, node_count, radial_subdivisions + 1);
2085
2086 vec3 nvec(0.1817f, 0.6198f, 0.7634f); // random vector to get things going
2087
2088 for (int j = 0; j < radial_subdivisions + 1; j++) {
2089 cfact[j] = cosf(2.f * PI_F * float(j) / float(radial_subdivisions));
2090 sfact[j] = sinf(2.f * PI_F * float(j) / float(radial_subdivisions));
2091 }
2092
2093 for (int i = 0; i < node_count; i++) { // looping over tube segments
2094
2095 if (radius.at(i) < 0) {
2096 helios_runtime_error("ERROR (Context::addTube): Radius of tube must be positive.");
2097 }
2098
2099 if (i == 0) {
2100 vec.x = nodes[i + 1].x - nodes[i].x;
2101 vec.y = nodes[i + 1].y - nodes[i].y;
2102 vec.z = nodes[i + 1].z - nodes[i].z;
2103 } else if (i == node_count - 1) {
2104 vec.x = nodes[i].x - nodes[i - 1].x;
2105 vec.y = nodes[i].y - nodes[i - 1].y;
2106 vec.z = nodes[i].z - nodes[i - 1].z;
2107 } else {
2108 vec.x = 0.5f * ((nodes[i].x - nodes[i - 1].x) + (nodes[i + 1].x - nodes[i].x));
2109 vec.y = 0.5f * ((nodes[i].y - nodes[i - 1].y) + (nodes[i + 1].y - nodes[i].y));
2110 vec.z = 0.5f * ((nodes[i].z - nodes[i - 1].z) + (nodes[i + 1].z - nodes[i].z));
2111 }
2112
2113 // Ensure nvec is not parallel to vec to avoid degenerate cross products
2114 vec.normalize();
2115 if (fabs(nvec * vec) > 0.95f) {
2116 nvec = vec3(0.1817f, 0.6198f, 0.7634f); // Reset to original random vector
2117 if (fabs(nvec * vec) > 0.95f) {
2118 nvec = vec3(1.0f, 0.0f, 0.0f); // Use x-axis if still parallel
2119 }
2120 }
2121 // Also handle nearly vertical axes
2122 if (fabs(vec.z) > 0.95f) {
2123 nvec = vec3(1.0f, 0.0f, 0.0f); // Use horizontal direction for vertical axes
2124 }
2125
2126 convec = cross(nvec, vec);
2127 convec.normalize();
2128 nvec = cross(vec, convec);
2129 nvec.normalize();
2130
2131 for (int j = 0; j < radial_subdivisions + 1; j++) {
2132 vec3 normal;
2133 normal.x = cfact[j] * radius[i] * nvec.x + sfact[j] * radius[i] * convec.x;
2134 normal.y = cfact[j] * radius[i] * nvec.y + sfact[j] * radius[i] * convec.y;
2135 normal.z = cfact[j] * radius[i] * nvec.z + sfact[j] * radius[i] * convec.z;
2136
2137 xyz[j][i].x = nodes[i].x + normal.x;
2138 xyz[j][i].y = nodes[i].y + normal.y;
2139 xyz[j][i].z = nodes[i].z + normal.z;
2140 }
2141 }
2142
2143 vec3 v0, v1, v2;
2144 std::vector<uint> UUIDs(2 * (node_count - 1) * radial_subdivisions);
2145
2146 int ii = 0;
2147 for (int i = 0; i < node_count - 1; i++) {
2148 for (int j = 0; j < radial_subdivisions; j++) {
2149 v0 = xyz[j][i];
2150 v1 = xyz[j + 1][i + 1];
2151 v2 = xyz[j + 1][i];
2152
2153 UUIDs.at(ii) = addTriangle(v0, v1, v2, color.at(i));
2154
2155 v0 = xyz[j][i];
2156 v1 = xyz[j][i + 1];
2157 v2 = xyz[j + 1][i + 1];
2158
2159 UUIDs.at(ii + 1) = addTriangle(v0, v1, v2, color.at(i));
2160
2161 ii += 2;
2162 }
2163 }
2164
2165 return UUIDs;
2166}
2167
2168std::vector<uint> Context::addTube(uint radial_subdivisions, const std::vector<vec3> &nodes, const std::vector<float> &radius, const char *texturefile) {
2169 if (!validateTextureFileExtenstion(texturefile)) {
2170 helios_runtime_error("ERROR (Context::addTube): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
2171 } else if (!doesTextureFileExist(texturefile)) {
2172 helios_runtime_error("ERROR (Context::addTube): Texture file " + std::string(texturefile) + " does not exist.");
2173 }
2174
2175 const uint node_count = nodes.size();
2176
2177 if (node_count == 0) {
2178 helios_runtime_error("ERROR (Context::addTube): Node and radius arrays are empty.");
2179 } else if (node_count != radius.size()) {
2180 helios_runtime_error("ERROR (Context::addTube): Size of `nodes' and `radius' arguments must agree.");
2181 }
2182
2183 vec3 vec, convec;
2184 std::vector<float> cfact(radial_subdivisions + 1);
2185 std::vector<float> sfact(radial_subdivisions + 1);
2186 std::vector<std::vector<vec3>> xyz, normal;
2187 std::vector<std::vector<vec2>> uv;
2188 resize_vector(xyz, node_count, radial_subdivisions + 1);
2189 resize_vector(normal, node_count, radial_subdivisions + 1);
2190 resize_vector(uv, node_count, radial_subdivisions + 1);
2191
2192 vec3 nvec(0.1817f, 0.6198f, 0.7634f); // random vector to get things going
2193
2194 for (int j = 0; j < radial_subdivisions + 1; j++) {
2195 cfact[j] = cosf(2.f * PI_F * float(j) / float(radial_subdivisions));
2196 sfact[j] = sinf(2.f * PI_F * float(j) / float(radial_subdivisions));
2197 }
2198
2199 for (int i = 0; i < node_count; i++) { // looping over tube segments
2200
2201 if (radius.at(i) < 0) {
2202 helios_runtime_error("ERROR (Context::addTube): Radius of tube must be positive.");
2203 }
2204
2205 if (i == 0) {
2206 vec.x = nodes[i + 1].x - nodes[i].x;
2207 vec.y = nodes[i + 1].y - nodes[i].y;
2208 vec.z = nodes[i + 1].z - nodes[i].z;
2209 } else if (i == node_count - 1) {
2210 vec.x = nodes[i].x - nodes[i - 1].x;
2211 vec.y = nodes[i].y - nodes[i - 1].y;
2212 vec.z = nodes[i].z - nodes[i - 1].z;
2213 } else {
2214 vec.x = 0.5f * ((nodes[i].x - nodes[i - 1].x) + (nodes[i + 1].x - nodes[i].x));
2215 vec.y = 0.5f * ((nodes[i].y - nodes[i - 1].y) + (nodes[i + 1].y - nodes[i].y));
2216 vec.z = 0.5f * ((nodes[i].z - nodes[i - 1].z) + (nodes[i + 1].z - nodes[i].z));
2217 }
2218
2219 // Ensure nvec is not parallel to vec to avoid degenerate cross products
2220 vec.normalize();
2221 if (fabs(nvec * vec) > 0.95f) {
2222 nvec = vec3(0.1817f, 0.6198f, 0.7634f); // Reset to original random vector
2223 if (fabs(nvec * vec) > 0.95f) {
2224 nvec = vec3(1.0f, 0.0f, 0.0f); // Use x-axis if still parallel
2225 }
2226 }
2227 // Also handle nearly vertical axes
2228 if (fabs(vec.z) > 0.95f) {
2229 nvec = vec3(1.0f, 0.0f, 0.0f); // Use horizontal direction for vertical axes
2230 }
2231
2232 convec = cross(nvec, vec);
2233 convec.normalize();
2234 nvec = cross(vec, convec);
2235 nvec.normalize();
2236
2237 for (int j = 0; j < radial_subdivisions + 1; j++) {
2238 normal[j][i].x = cfact[j] * radius[i] * nvec.x + sfact[j] * radius[i] * convec.x;
2239 normal[j][i].y = cfact[j] * radius[i] * nvec.y + sfact[j] * radius[i] * convec.y;
2240 normal[j][i].z = cfact[j] * radius[i] * nvec.z + sfact[j] * radius[i] * convec.z;
2241
2242 xyz[j][i].x = nodes[i].x + normal[j][i].x;
2243 xyz[j][i].y = nodes[i].y + normal[j][i].y;
2244 xyz[j][i].z = nodes[i].z + normal[j][i].z;
2245
2246 uv[j][i].x = float(i) / float(node_count - 1);
2247 uv[j][i].y = float(j) / float(radial_subdivisions);
2248
2249 normal[j][i] = normal[j][i] / radius[i];
2250 }
2251 }
2252
2253 vec3 v0, v1, v2;
2254 vec2 uv0, uv1, uv2;
2255 std::vector<uint> UUIDs(2 * (node_count - 1) * radial_subdivisions);
2256
2257 int ii = 0;
2258 for (int i = 0; i < node_count - 1; i++) {
2259 for (int j = 0; j < radial_subdivisions; j++) {
2260 v0 = xyz[j][i];
2261 v1 = xyz[j + 1][i + 1];
2262 v2 = xyz[j + 1][i];
2263
2264 uv0 = uv[j][i];
2265 uv1 = uv[j + 1][i + 1];
2266 uv2 = uv[j + 1][i];
2267
2268 uint triangle_uuid = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
2269 if (getPrimitiveArea(triangle_uuid) > 0) {
2270 UUIDs.at(ii) = triangle_uuid;
2271 } else {
2272 deletePrimitive(triangle_uuid);
2273 UUIDs.at(ii) = 0; // Mark as invalid
2274 }
2275
2276 v0 = xyz[j][i];
2277 v1 = xyz[j][i + 1];
2278 v2 = xyz[j + 1][i + 1];
2279
2280 uv0 = uv[j][i];
2281 uv1 = uv[j][i + 1];
2282 uv2 = uv[j + 1][i + 1];
2283
2284 uint triangle_uuid2 = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
2285 if (getPrimitiveArea(triangle_uuid2) > 0) {
2286 UUIDs.at(ii + 1) = triangle_uuid2;
2287 } else {
2288 deletePrimitive(triangle_uuid2);
2289 UUIDs.at(ii + 1) = 0; // Mark as invalid
2290 }
2291
2292 ii += 2;
2293 }
2294 }
2295
2296 // Remove invalid UUIDs (zeros) from the vector
2297 UUIDs.erase(std::remove(UUIDs.begin(), UUIDs.end(), 0), UUIDs.end());
2298
2299 return UUIDs;
2300}
2301
2302std::vector<uint> Context::addBox(const vec3 &center, const vec3 &size, const int3 &subdiv) {
2303 RGBcolor color = make_RGBcolor(0.f, 0.75f, 0.f); // Default color is green
2304
2305 return addBox(center, size, subdiv, color, false);
2306}
2307
2308std::vector<uint> Context::addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color) {
2309 return addBox(center, size, subdiv, color, false);
2310}
2311
2312std::vector<uint> Context::addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const char *texturefile) {
2313 return addBox(center, size, subdiv, texturefile, false);
2314}
2315
2316std::vector<uint> Context::addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const RGBcolor &color, bool reverse_normals) {
2317 std::vector<uint> UUID;
2318
2319 vec3 subsize;
2320 subsize.x = size.x / float(subdiv.x);
2321 subsize.y = size.y / float(subdiv.y);
2322 subsize.z = size.z / float(subdiv.z);
2323
2324 vec3 subcenter;
2325 std::vector<uint> U;
2326
2327 if (reverse_normals) { // normals point inward
2328
2329 // x-z faces (vertical)
2330
2331 // right
2332 subcenter = center + make_vec3(0, 0.5f * size.y, 0);
2333 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, PI_F), make_int2(subdiv.x, subdiv.z), color);
2334 UUID.insert(UUID.end(), U.begin(), U.end());
2335
2336 // left
2337 subcenter = center - make_vec3(0, 0.5f * size.y, 0);
2338 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, 0), make_int2(subdiv.x, subdiv.z), color);
2339 UUID.insert(UUID.end(), U.begin(), U.end());
2340
2341 // y-z faces (vertical)
2342
2343 // front
2344 subcenter = center + make_vec3(0.5f * size.x, 0, 0);
2345 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 1.5 * PI_F), make_int2(subdiv.y, subdiv.z), color);
2346 UUID.insert(UUID.end(), U.begin(), U.end());
2347
2348 // back
2349 subcenter = center - make_vec3(0.5f * size.x, 0, 0);
2350 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F), make_int2(subdiv.y, subdiv.z), color);
2351 UUID.insert(UUID.end(), U.begin(), U.end());
2352
2353 // x-y faces (horizontal)
2354
2355 // top
2356 subcenter = center + make_vec3(0, 0, 0.5f * size.z);
2357 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(PI_F, 0), make_int2(subdiv.x, subdiv.y), color);
2358 UUID.insert(UUID.end(), U.begin(), U.end());
2359
2360 // bottom
2361 subcenter = center - make_vec3(0, 0, 0.5f * size.z);
2362 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(0, 0), make_int2(subdiv.x, subdiv.y), color);
2363 UUID.insert(UUID.end(), U.begin(), U.end());
2364 } else { // normals point outward
2365
2366 // x-z faces (vertical)
2367
2368 // right
2369 subcenter = center + make_vec3(0, 0.5f * size.y, 0);
2370 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, 0), make_int2(subdiv.x, subdiv.z), color);
2371 UUID.insert(UUID.end(), U.begin(), U.end());
2372
2373 // left
2374 subcenter = center - make_vec3(0, 0.5f * size.y, 0);
2375 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, PI_F), make_int2(subdiv.x, subdiv.z), color);
2376 UUID.insert(UUID.end(), U.begin(), U.end());
2377
2378 // y-z faces (vertical)
2379
2380 // front
2381 subcenter = center + make_vec3(0.5f * size.x, 0, 0);
2382 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F), make_int2(subdiv.y, subdiv.z), color);
2383 UUID.insert(UUID.end(), U.begin(), U.end());
2384
2385 // back
2386 subcenter = center - make_vec3(0.5f * size.x, 0, 0);
2387 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 1.5 * PI_F), make_int2(subdiv.y, subdiv.z), color);
2388 UUID.insert(UUID.end(), U.begin(), U.end());
2389
2390 // x-y faces (horizontal)
2391
2392 // top
2393 subcenter = center + make_vec3(0, 0, 0.5f * size.z);
2394 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(0, 0), make_int2(subdiv.x, subdiv.y), color);
2395 UUID.insert(UUID.end(), U.begin(), U.end());
2396
2397 // bottom
2398 subcenter = center - make_vec3(0, 0, 0.5f * size.z);
2399 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(PI_F, 0), make_int2(subdiv.x, subdiv.y), color);
2400 UUID.insert(UUID.end(), U.begin(), U.end());
2401 }
2402
2403 return UUID;
2404}
2405
2406std::vector<uint> Context::addBox(const vec3 &center, const vec3 &size, const int3 &subdiv, const char *texturefile, bool reverse_normals) {
2407 if (!validateTextureFileExtenstion(texturefile)) {
2408 helios_runtime_error("ERROR (Context::addBox): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
2409 } else if (!doesTextureFileExist(texturefile)) {
2410 helios_runtime_error("ERROR (Context::addBox): Texture file " + std::string(texturefile) + " does not exist.");
2411 }
2412
2413 std::vector<uint> UUID;
2414
2415 vec3 subsize;
2416 subsize.x = size.x / float(subdiv.x);
2417 subsize.y = size.y / float(subdiv.y);
2418 subsize.z = size.z / float(subdiv.z);
2419
2420 vec3 subcenter;
2421 std::vector<uint> U;
2422
2423 if (reverse_normals) { // normals point inward
2424
2425 // x-z faces (vertical)
2426
2427 // right
2428 subcenter = center + make_vec3(0, 0.5f * size.y, 0);
2429 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, PI_F), make_int2(subdiv.x, subdiv.z), texturefile);
2430 UUID.insert(UUID.end(), U.begin(), U.end());
2431
2432 // left
2433 subcenter = center - make_vec3(0, 0.5f * size.y, 0);
2434 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, 0), make_int2(subdiv.x, subdiv.z), texturefile);
2435 UUID.insert(UUID.end(), U.begin(), U.end());
2436
2437 // y-z faces (vertical)
2438
2439 // front
2440 subcenter = center + make_vec3(0.5f * size.x, 0, 0);
2441 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 1.5 * PI_F), make_int2(subdiv.y, subdiv.z), texturefile);
2442 UUID.insert(UUID.end(), U.begin(), U.end());
2443
2444 // back
2445 subcenter = center - make_vec3(0.5f * size.x, 0, 0);
2446 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F), make_int2(subdiv.y, subdiv.z), texturefile);
2447 UUID.insert(UUID.end(), U.begin(), U.end());
2448
2449 // x-y faces (horizontal)
2450
2451 // top
2452 subcenter = center + make_vec3(0, 0, 0.5f * size.z);
2453 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(PI_F, 0), make_int2(subdiv.x, subdiv.y), texturefile);
2454 UUID.insert(UUID.end(), U.begin(), U.end());
2455
2456 // bottom
2457 subcenter = center - make_vec3(0, 0, 0.5f * size.z);
2458 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(0, 0), make_int2(subdiv.x, subdiv.y), texturefile);
2459 UUID.insert(UUID.end(), U.begin(), U.end());
2460 } else { // normals point outward
2461
2462 // x-z faces (vertical)
2463
2464 // right
2465 subcenter = center + make_vec3(0, 0.5f * size.y, 0);
2466 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, 0), make_int2(subdiv.x, subdiv.z), texturefile);
2467 UUID.insert(UUID.end(), U.begin(), U.end());
2468
2469 // left
2470 subcenter = center - make_vec3(0, 0.5f * size.y, 0);
2471 U = addTile(subcenter, make_vec2(size.x, size.z), make_SphericalCoord(0.5 * PI_F, PI_F), make_int2(subdiv.x, subdiv.z), texturefile);
2472 UUID.insert(UUID.end(), U.begin(), U.end());
2473
2474 // y-z faces (vertical)
2475
2476 // front
2477 subcenter = center + make_vec3(0.5f * size.x, 0, 0);
2478 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 0.5 * PI_F), make_int2(subdiv.y, subdiv.z), texturefile);
2479 UUID.insert(UUID.end(), U.begin(), U.end());
2480
2481 // back
2482 subcenter = center - make_vec3(0.5f * size.x, 0, 0);
2483 U = addTile(subcenter, make_vec2(size.y, size.z), make_SphericalCoord(0.5 * PI_F, 1.5 * PI_F), make_int2(subdiv.y, subdiv.z), texturefile);
2484 UUID.insert(UUID.end(), U.begin(), U.end());
2485
2486 // x-y faces (horizontal)
2487
2488 // top
2489 subcenter = center + make_vec3(0, 0, 0.5f * size.z);
2490 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(0, 0), make_int2(subdiv.x, subdiv.y), texturefile);
2491 UUID.insert(UUID.end(), U.begin(), U.end());
2492
2493 // bottom
2494 subcenter = center - make_vec3(0, 0, 0.5f * size.z);
2495 U = addTile(subcenter, make_vec2(size.x, size.y), make_SphericalCoord(PI_F, 0), make_int2(subdiv.x, subdiv.y), texturefile);
2496 UUID.insert(UUID.end(), U.begin(), U.end());
2497 }
2498
2499 return UUID;
2500}
2501
2502std::vector<uint> Context::addDisk(uint Ndivs, const vec3 &center, const vec2 &size) {
2503 return addDisk(make_int2(Ndivs, 1), center, size, make_SphericalCoord(0, 0), make_RGBAcolor(1, 0, 0, 1));
2504}
2505
2506std::vector<uint> Context::addDisk(uint Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation) {
2507 return addDisk(make_int2(Ndivs, 1), center, size, rotation, make_RGBAcolor(1, 0, 0, 1));
2508}
2509
2510std::vector<uint> Context::addDisk(uint Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color) {
2511 return addDisk(make_int2(Ndivs, 1), center, size, rotation, make_RGBAcolor(color, 1));
2512}
2513
2514std::vector<uint> Context::addDisk(uint Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBAcolor &color) {
2515 return addDisk(make_int2(Ndivs, 1), center, size, rotation, color);
2516}
2517
2518std::vector<uint> Context::addDisk(uint Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const char *texture_file) {
2519 return addDisk(make_int2(Ndivs, 1), center, size, rotation, texture_file);
2520}
2521
2522std::vector<uint> Context::addDisk(const int2 &Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBcolor &color) {
2523 return addDisk(Ndivs, center, size, rotation, make_RGBAcolor(color, 1));
2524}
2525
2526std::vector<uint> Context::addDisk(const int2 &Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const RGBAcolor &color) {
2527 std::vector<uint> UUID(Ndivs.x + Ndivs.x * (Ndivs.y - 1) * 2);
2528 int i = 0;
2529 for (int r = 0; r < Ndivs.y; r++) {
2530 for (int t = 0; t < Ndivs.x; t++) {
2531 float dtheta = 2.f * PI_F / float(Ndivs.x);
2532 float theta = dtheta * float(t);
2533 float theta_plus = dtheta * float(t + 1);
2534
2535 float rx = size.x / float(Ndivs.y) * float(r);
2536 float ry = size.y / float(Ndivs.y) * float(r);
2537
2538 float rx_plus = size.x / float(Ndivs.y) * float(r + 1);
2539 float ry_plus = size.y / float(Ndivs.y) * float(r + 1);
2540
2541 if (r == 0) {
2542 UUID.at(i) = addTriangle(make_vec3(0, 0, 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), make_vec3(rx_plus * cosf(theta_plus), ry_plus * sinf(theta_plus), 0), color);
2543 } else {
2544 UUID.at(i) = addTriangle(make_vec3(rx * cosf(theta_plus), ry * sinf(theta_plus), 0), make_vec3(rx * cosf(theta), ry * sinf(theta), 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), color);
2545 i++;
2546 UUID.at(i) = addTriangle(make_vec3(rx * cosf(theta_plus), ry * sinf(theta_plus), 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), make_vec3(rx_plus * cosf(theta_plus), ry_plus * sinf(theta_plus), 0), color);
2547 }
2548 getPrimitivePointer_private(UUID.at(i))->rotate(rotation.elevation, "y");
2549 getPrimitivePointer_private(UUID.at(i))->rotate(rotation.azimuth, "z");
2550 getPrimitivePointer_private(UUID.at(i))->translate(center);
2551
2552 i++;
2553 }
2554 }
2555
2556 return UUID;
2557}
2558
2559std::vector<uint> Context::addDisk(const int2 &Ndivs, const vec3 &center, const vec2 &size, const SphericalCoord &rotation, const char *texturefile) {
2560 if (!validateTextureFileExtenstion(texturefile)) {
2561 helios_runtime_error("ERROR (Context::addDisk): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
2562 } else if (!doesTextureFileExist(texturefile)) {
2563 helios_runtime_error("ERROR (Context::addDisk): Texture file " + std::string(texturefile) + " does not exist.");
2564 }
2565
2566 std::vector<uint> UUID;
2567 UUID.reserve(Ndivs.x + Ndivs.x * (Ndivs.y - 1) * 2); // Reserve expected capacity
2568 for (int r = 0; r < Ndivs.y; r++) {
2569 for (int t = 0; t < Ndivs.x; t++) {
2570 float dtheta = 2.f * PI_F / float(Ndivs.x);
2571 float theta = dtheta * float(t);
2572 float theta_plus = dtheta * float(t + 1);
2573
2574 float rx = size.x / float(Ndivs.y) * float(r);
2575 float ry = size.y / float(Ndivs.y) * float(r);
2576 float rx_plus = size.x / float(Ndivs.y) * float(r + 1);
2577 float ry_plus = size.y / float(Ndivs.y) * float(r + 1);
2578
2579 if (r == 0) {
2580 uint triangle_uuid = addTriangle(make_vec3(0, 0, 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), make_vec3(rx_plus * cosf(theta_plus), ry_plus * sinf(theta_plus), 0), texturefile, make_vec2(0.5, 0.5),
2581 make_vec2(0.5f * (1.f + cosf(theta) * rx_plus / size.x), 0.5f * (1.f + sinf(theta) * ry_plus / size.y)),
2582 make_vec2(0.5f * (1.f + cosf(theta_plus) * rx_plus / size.x), 0.5f * (1.f + sinf(theta_plus) * ry_plus / size.y)));
2583 if (getPrimitiveArea(triangle_uuid) > 0) {
2584 UUID.push_back(triangle_uuid);
2585 } else {
2586 deletePrimitive(triangle_uuid);
2587 continue;
2588 }
2589 } else {
2590 uint triangle_uuid1 = addTriangle(make_vec3(rx * cosf(theta_plus), ry * sinf(theta_plus), 0), make_vec3(rx * cosf(theta), ry * sinf(theta), 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), texturefile,
2591 make_vec2(0.5f * (1.f + cosf(theta_plus) * rx / size.x), 0.5f * (1.f + sinf(theta_plus) * ry / size.y)), make_vec2(0.5f * (1.f + cosf(theta) * rx / size.x), 0.5f * (1.f + sinf(theta) * ry / size.y)),
2592 make_vec2(0.5f * (1.f + cosf(theta) * rx_plus / size.x), 0.5f * (1.f + sinf(theta) * ry_plus / size.y)));
2593 if (getPrimitiveArea(triangle_uuid1) > 0) {
2594 UUID.push_back(triangle_uuid1);
2595 } else {
2596 deletePrimitive(triangle_uuid1);
2597 }
2598
2599 uint triangle_uuid2 =
2600 addTriangle(make_vec3(rx * cosf(theta_plus), ry * sinf(theta_plus), 0), make_vec3(rx_plus * cosf(theta), ry_plus * sinf(theta), 0), make_vec3(rx_plus * cosf(theta_plus), ry_plus * sinf(theta_plus), 0), texturefile,
2601 make_vec2(0.5f * (1.f + cosf(theta_plus) * rx / size.x), 0.5f * (1.f + sinf(theta_plus) * ry / size.y)), make_vec2(0.5f * (1.f + cosf(theta) * rx_plus / size.x), 0.5f * (1.f + sinf(theta) * ry_plus / size.y)),
2602 make_vec2(0.5f * (1.f + cosf(theta_plus) * rx_plus / size.x), 0.5f * (1.f + sinf(theta_plus) * ry_plus / size.y)));
2603 if (getPrimitiveArea(triangle_uuid2) > 0) {
2604 UUID.push_back(triangle_uuid2);
2605 } else {
2606 deletePrimitive(triangle_uuid2);
2607 continue;
2608 }
2609 }
2610 // Apply transformations to all valid triangles added in this iteration
2611 size_t start_idx = UUID.size() - (r == 0 ? 1 : 2);
2612 for (size_t uuid_idx = start_idx; uuid_idx < UUID.size(); uuid_idx++) {
2613 getPrimitivePointer_private(UUID.at(uuid_idx))->rotate(rotation.elevation, "y");
2614 getPrimitivePointer_private(UUID.at(uuid_idx))->rotate(rotation.azimuth, "z");
2615 getPrimitivePointer_private(UUID.at(uuid_idx))->translate(center);
2616 }
2617 }
2618 }
2619
2620 return UUID;
2621}
2622
2623std::vector<uint> Context::addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1) {
2624 RGBcolor color;
2625 color = make_RGBcolor(0.f, 0.75f, 0.f); // Default color is green
2626
2627 return addCone(Ndivs, node0, node1, radius0, radius1, color);
2628}
2629
2630std::vector<uint> Context::addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, RGBcolor &color) {
2631 std::vector<helios::vec3> nodes{node0, node1};
2632 std::vector<float> radii{radius0, radius1};
2633
2634 vec3 vec, convec;
2635 std::vector<float> cfact(Ndivs + 1);
2636 std::vector<float> sfact(Ndivs + 1);
2637 std::vector<std::vector<vec3>> xyz, normal;
2638 xyz.resize(Ndivs + 1);
2639 normal.resize(Ndivs + 1);
2640 for (uint j = 0; j < Ndivs + 1; j++) {
2641 xyz.at(j).resize(2);
2642 normal.at(j).resize(2);
2643 }
2644 vec3 nvec(0.1817f, 0.6198f, 0.7634f); // random vector to get things going
2645
2646 for (int j = 0; j < Ndivs + 1; j++) {
2647 cfact[j] = cosf(2.f * PI_F * float(j) / float(Ndivs));
2648 sfact[j] = sinf(2.f * PI_F * float(j) / float(Ndivs));
2649 }
2650
2651 for (int i = 0; i < 2; i++) { // looping over cone segments
2652
2653 if (i == 0) {
2654 vec.x = nodes[i + 1].x - nodes[i].x;
2655 vec.y = nodes[i + 1].y - nodes[i].y;
2656 vec.z = nodes[i + 1].z - nodes[i].z;
2657 } else if (i == 1) {
2658 vec.x = nodes[i].x - nodes[i - 1].x;
2659 vec.y = nodes[i].y - nodes[i - 1].y;
2660 vec.z = nodes[i].z - nodes[i - 1].z;
2661 }
2662
2663 float norm;
2664 convec = cross(nvec, vec);
2665 norm = convec.magnitude();
2666 convec.x = convec.x / norm;
2667 convec.y = convec.y / norm;
2668 convec.z = convec.z / norm;
2669 nvec = cross(vec, convec);
2670 norm = nvec.magnitude();
2671 nvec.x = nvec.x / norm;
2672 nvec.y = nvec.y / norm;
2673 nvec.z = nvec.z / norm;
2674
2675
2676 for (int j = 0; j < Ndivs + 1; j++) {
2677 normal[j][i].x = cfact[j] * radii[i] * nvec.x + sfact[j] * radii[i] * convec.x;
2678 normal[j][i].y = cfact[j] * radii[i] * nvec.y + sfact[j] * radii[i] * convec.y;
2679 normal[j][i].z = cfact[j] * radii[i] * nvec.z + sfact[j] * radii[i] * convec.z;
2680
2681 xyz[j][i].x = nodes[i].x + normal[j][i].x;
2682 xyz[j][i].y = nodes[i].y + normal[j][i].y;
2683 xyz[j][i].z = nodes[i].z + normal[j][i].z;
2684
2685 normal[j][i] = normal[j][i] / radii[i];
2686 }
2687 }
2688
2689 vec3 v0, v1, v2;
2690 std::vector<uint> UUID;
2691
2692 for (int i = 0; i < 2 - 1; i++) {
2693 for (int j = 0; j < Ndivs; j++) {
2694 v0 = xyz[j][i];
2695 v1 = xyz[j + 1][i + 1];
2696 v2 = xyz[j + 1][i];
2697
2698 UUID.push_back(addTriangle(v0, v1, v2, color));
2699
2700 v0 = xyz[j][i];
2701 v1 = xyz[j][i + 1];
2702 v2 = xyz[j + 1][i + 1];
2703
2704 UUID.push_back(addTriangle(v0, v1, v2, color));
2705 }
2706 }
2707
2708 return UUID;
2709}
2710
2711std::vector<uint> Context::addCone(uint Ndivs, const vec3 &node0, const vec3 &node1, float radius0, float radius1, const char *texturefile) {
2712 if (!validateTextureFileExtenstion(texturefile)) {
2713 helios_runtime_error("ERROR (Context::addCone): Texture file " + std::string(texturefile) + " is not PNG or JPEG format.");
2714 } else if (!doesTextureFileExist(texturefile)) {
2715 helios_runtime_error("ERROR (Context::addCone): Texture file " + std::string(texturefile) + " does not exist.");
2716 }
2717
2718 std::vector<helios::vec3> nodes{node0, node1};
2719 std::vector<float> radii{radius0, radius1};
2720
2721 vec3 vec, convec;
2722 std::vector<float> cfact(Ndivs + 1);
2723 std::vector<float> sfact(Ndivs + 1);
2724 std::vector<std::vector<vec3>> xyz, normal;
2725 std::vector<std::vector<vec2>> uv;
2726 xyz.resize(Ndivs + 1);
2727 normal.resize(Ndivs + 1);
2728 uv.resize(Ndivs + 1);
2729 for (uint j = 0; j < Ndivs + 1; j++) {
2730 xyz.at(j).resize(2);
2731 normal.at(j).resize(2);
2732 uv.at(j).resize(2);
2733 }
2734 vec3 nvec(0.f, 1.f, 0.f);
2735
2736 for (int j = 0; j < Ndivs + 1; j++) {
2737 cfact[j] = cosf(2.f * PI_F * float(j) / float(Ndivs));
2738 sfact[j] = sinf(2.f * PI_F * float(j) / float(Ndivs));
2739 }
2740
2741 for (int i = 0; i < 2; i++) { // looping over cone segments
2742
2743 if (i == 0) {
2744 vec.x = nodes[i + 1].x - nodes[i].x;
2745 vec.y = nodes[i + 1].y - nodes[i].y;
2746 vec.z = nodes[i + 1].z - nodes[i].z;
2747 } else if (i == 1) {
2748 vec.x = nodes[i].x - nodes[i - 1].x;
2749 vec.y = nodes[i].y - nodes[i - 1].y;
2750 vec.z = nodes[i].z - nodes[i - 1].z;
2751 }
2752
2753 float norm;
2754 convec = cross(nvec, vec);
2755 norm = convec.magnitude();
2756 convec.x = convec.x / norm;
2757 convec.y = convec.y / norm;
2758 convec.z = convec.z / norm;
2759 nvec = cross(vec, convec);
2760 norm = nvec.magnitude();
2761 nvec.x = nvec.x / norm;
2762 nvec.y = nvec.y / norm;
2763 nvec.z = nvec.z / norm;
2764
2765 for (int j = 0; j < Ndivs + 1; j++) {
2766 normal[j][i].x = cfact[j] * radii[i] * nvec.x + sfact[j] * radii[i] * convec.x;
2767 normal[j][i].y = cfact[j] * radii[i] * nvec.y + sfact[j] * radii[i] * convec.y;
2768 normal[j][i].z = cfact[j] * radii[i] * nvec.z + sfact[j] * radii[i] * convec.z;
2769
2770 xyz[j][i].x = nodes[i].x + normal[j][i].x;
2771 xyz[j][i].y = nodes[i].y + normal[j][i].y;
2772 xyz[j][i].z = nodes[i].z + normal[j][i].z;
2773
2774 uv[j][i].x = float(i) / float(2 - 1);
2775 uv[j][i].y = float(j) / float(Ndivs);
2776
2777 normal[j][i] = normal[j][i] / radii[i];
2778 }
2779 }
2780
2781 vec3 v0, v1, v2;
2782 vec2 uv0, uv1, uv2;
2783 std::vector<uint> UUID;
2784
2785 for (int i = 0; i < 2 - 1; i++) {
2786 for (int j = 0; j < Ndivs; j++) {
2787 v0 = xyz[j][i];
2788 v1 = xyz[j + 1][i + 1];
2789 v2 = xyz[j + 1][i];
2790
2791 uv0 = uv[j][i];
2792 uv1 = uv[j + 1][i + 1];
2793 uv2 = uv[j + 1][i];
2794
2795 if ((v1 - v0).magnitude() > 1e-6 && (v2 - v0).magnitude() > 1e-6 && (v2 - v1).magnitude() > 1e-6) {
2796 uint triangle_uuid = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
2797 if (getPrimitiveArea(triangle_uuid) > 0) {
2798 UUID.push_back(triangle_uuid);
2799 } else {
2800 deletePrimitive(triangle_uuid);
2801 }
2802 }
2803
2804 v0 = xyz[j][i];
2805 v1 = xyz[j][i + 1];
2806 v2 = xyz[j + 1][i + 1];
2807
2808 uv0 = uv[j][i];
2809 uv1 = uv[j][i + 1];
2810 uv2 = uv[j + 1][i + 1];
2811
2812 if ((v1 - v0).magnitude() > 1e-6 && (v2 - v0).magnitude() > 1e-6 && (v2 - v1).magnitude() > 1e-6) {
2813 uint triangle_uuid = addTriangle(v0, v1, v2, texturefile, uv0, uv1, uv2);
2814 if (getPrimitiveArea(triangle_uuid) > 0) {
2815 UUID.push_back(triangle_uuid);
2816 } else {
2817 deletePrimitive(triangle_uuid);
2818 }
2819 }
2820 }
2821 }
2822
2823 return UUID;
2824}
2825
2826void Context::colorPrimitiveByDataPseudocolor(const std::vector<uint> &UUIDs, const std::string &primitive_data, const std::string &colormap, uint Ncolors) {
2827 colorPrimitiveByDataPseudocolor(UUIDs, primitive_data, colormap, Ncolors, 9999999, -9999999);
2828}
2829
2830void Context::colorPrimitiveByDataPseudocolor(const std::vector<uint> &UUIDs, const std::string &primitive_data, const std::string &colormap, uint Ncolors, float data_min, float data_max) {
2831 std::map<uint, float> pcolor_data;
2832
2833 float data_min_new = 9999999;
2834 float data_max_new = -9999999;
2835 for (uint UUID: UUIDs) {
2836 if (!doesPrimitiveExist(UUID)) {
2837 std::cerr << "WARNING (Context::colorPrimitiveDataPseudocolor): primitive for UUID " << std::to_string(UUID) << " does not exist. Skipping this primitive." << std::endl;
2838 continue;
2839 }
2840
2841 float dataf = 0;
2842 if (doesPrimitiveDataExist(UUID, primitive_data.c_str())) {
2843 if (getPrimitiveDataType(primitive_data.c_str()) != HELIOS_TYPE_FLOAT && getPrimitiveDataType(primitive_data.c_str()) != HELIOS_TYPE_INT && getPrimitiveDataType(primitive_data.c_str()) != HELIOS_TYPE_UINT &&
2844 getPrimitiveDataType(primitive_data.c_str()) != HELIOS_TYPE_DOUBLE) {
2845 std::cerr << "WARNING (Context::colorPrimitiveDataPseudocolor): Only primitive data types of int, uint, float, and double are supported for this function. Skipping this primitive." << std::endl;
2846 continue;
2847 }
2848
2849 if (getPrimitiveDataType(primitive_data.c_str()) == HELIOS_TYPE_FLOAT) {
2850 float data;
2851 getPrimitiveData(UUID, primitive_data.c_str(), data);
2852 dataf = data;
2853 } else if (getPrimitiveDataType(primitive_data.c_str()) == HELIOS_TYPE_DOUBLE) {
2854 double data;
2855 getPrimitiveData(UUID, primitive_data.c_str(), data);
2856 dataf = float(data);
2857 } else if (getPrimitiveDataType(primitive_data.c_str()) == HELIOS_TYPE_INT) {
2858 int data;
2859 getPrimitiveData(UUID, primitive_data.c_str(), data);
2860 dataf = float(data);
2861 } else if (getPrimitiveDataType(primitive_data.c_str()) == HELIOS_TYPE_UINT) {
2862 uint data;
2863 getPrimitiveData(UUID, primitive_data.c_str(), data);
2864 dataf = float(data);
2865 }
2866 }
2867
2868 if (data_min == 9999999 && data_max == -9999999) {
2869 if (dataf < data_min_new) {
2870 data_min_new = dataf;
2871 }
2872 if (dataf > data_max_new) {
2873 data_max_new = dataf;
2874 }
2875 }
2876
2877 pcolor_data[UUID] = dataf;
2878 }
2879
2880 if (data_min == 9999999 && data_max == -9999999) {
2881 data_min = data_min_new;
2882 data_max = data_max_new;
2883 }
2884
2885 std::vector<RGBcolor> colormap_data = generateColormap(colormap, Ncolors);
2886
2887 std::map<std::string, std::vector<std::string>> cmap_texture_filenames;
2888
2889 for (auto &[UUID, pdata]: pcolor_data) {
2890 std::string texturefile = getPrimitiveTextureFile(UUID);
2891
2892 int cmap_ind = std::round((pdata - data_min) / (data_max - data_min) * float(Ncolors - 1));
2893
2894 if (cmap_ind < 0) {
2895 cmap_ind = 0;
2896 } else if (cmap_ind >= Ncolors) {
2897 cmap_ind = Ncolors - 1;
2898 }
2899
2900 if (!texturefile.empty() && primitiveTextureHasTransparencyChannel(UUID)) { // primitive has texture with transparency channel
2901
2903 setPrimitiveColor(UUID, colormap_data.at(cmap_ind));
2904 } else { // primitive does not have texture with transparency channel - assign constant color
2905
2906 if (!getPrimitiveTextureFile(UUID).empty()) {
2908 }
2909
2910 setPrimitiveColor(UUID, colormap_data.at(cmap_ind));
2911 }
2912 }
2913}
2914
2915std::vector<RGBcolor> Context::generateColormap(const std::vector<helios::RGBcolor> &ctable, const std::vector<float> &cfrac, uint Ncolors) {
2916 if (Ncolors > 9999) {
2917 std::cerr << "WARNING (Context::generateColormap): Truncating number of color map textures to maximum value of 9999." << std::endl;
2918 }
2919
2920 if (ctable.size() != cfrac.size()) {
2921 helios_runtime_error("ERROR (Context::generateColormap): The length of arguments 'ctable' and 'cfrac' must match.");
2922 }
2923 if (ctable.empty()) {
2924 helios_runtime_error("ERROR (Context::generateColormap): 'ctable' and 'cfrac' arguments contain empty vectors.");
2925 }
2926
2927 std::vector<RGBcolor> color_table(Ncolors);
2928
2929 for (int i = 0; i < Ncolors; i++) {
2930 float frac = float(i) / float(Ncolors - 1) * cfrac.back();
2931
2932 int j;
2933 for (j = 0; j < cfrac.size() - 1; j++) {
2934 if (frac >= cfrac.at(j) && frac <= cfrac.at(j + 1)) {
2935 break;
2936 }
2937 }
2938
2939 float cminus = std::fmaxf(0.f, cfrac.at(j));
2940 float cplus = std::fminf(1.f, cfrac.at(j + 1));
2941
2942 float jfrac = (frac - cminus) / (cplus - cminus);
2943
2944 RGBcolor color;
2945 color.r = ctable.at(j).r + jfrac * (ctable.at(j + 1).r - ctable.at(j).r);
2946 color.g = ctable.at(j).g + jfrac * (ctable.at(j + 1).g - ctable.at(j).g);
2947 color.b = ctable.at(j).b + jfrac * (ctable.at(j + 1).b - ctable.at(j).b);
2948
2949 color_table.at(i) = color;
2950 }
2951
2952 return color_table;
2953}
2954
2955std::vector<RGBcolor> Context::generateColormap(const std::string &colormap, uint Ncolors) {
2956 std::vector<RGBcolor> ctable_c;
2957 std::vector<float> clocs_c;
2958
2959 if (colormap == "hot") {
2960 ctable_c.resize(5);
2961 ctable_c.at(0) = make_RGBcolor(0.f, 0.f, 0.f);
2962 ctable_c.at(1) = make_RGBcolor(0.5f, 0.f, 0.5f);
2963 ctable_c.at(2) = make_RGBcolor(1.f, 0.f, 0.f);
2964 ctable_c.at(3) = make_RGBcolor(1.f, 0.5f, 0.f);
2965 ctable_c.at(4) = make_RGBcolor(1.f, 1.f, 0.f);
2966
2967 clocs_c.resize(5);
2968 clocs_c.at(0) = 0.f;
2969 clocs_c.at(1) = 0.25f;
2970 clocs_c.at(2) = 0.5f;
2971 clocs_c.at(3) = 0.75f;
2972 clocs_c.at(4) = 1.f;
2973 } else if (colormap == "cool") {
2974 ctable_c.resize(2);
2975 ctable_c.at(0) = RGB::cyan;
2976 ctable_c.at(1) = RGB::magenta;
2977
2978 clocs_c.resize(2);
2979 clocs_c.at(0) = 0.f;
2980 clocs_c.at(1) = 1.f;
2981 } else if (colormap == "lava") {
2982 ctable_c.resize(5);
2983 ctable_c.at(0) = make_RGBcolor(0.f, 0.05f, 0.05f);
2984 ctable_c.at(1) = make_RGBcolor(0.f, 0.6f, 0.6f);
2985 ctable_c.at(2) = make_RGBcolor(1.f, 1.f, 1.f);
2986 ctable_c.at(3) = make_RGBcolor(1.f, 0.f, 0.f);
2987 ctable_c.at(4) = make_RGBcolor(0.5f, 0.f, 0.f);
2988
2989 clocs_c.resize(5);
2990 clocs_c.at(0) = 0.f;
2991 clocs_c.at(1) = 0.4f;
2992 clocs_c.at(2) = 0.5f;
2993 clocs_c.at(3) = 0.6f;
2994 clocs_c.at(4) = 1.f;
2995 } else if (colormap == "rainbow") {
2996 ctable_c.resize(4);
2997 ctable_c.at(0) = RGB::navy;
2998 ctable_c.at(1) = RGB::cyan;
2999 ctable_c.at(2) = RGB::yellow;
3000 ctable_c.at(3) = make_RGBcolor(0.75f, 0.f, 0.f);
3001
3002 clocs_c.resize(4);
3003 clocs_c.at(0) = 0.f;
3004 clocs_c.at(1) = 0.3f;
3005 clocs_c.at(2) = 0.7f;
3006 clocs_c.at(3) = 1.f;
3007 } else if (colormap == "parula") {
3008 ctable_c.resize(4);
3009 ctable_c.at(0) = RGB::navy;
3010 ctable_c.at(1) = make_RGBcolor(0, 0.6, 0.6);
3011 ctable_c.at(2) = RGB::goldenrod;
3012 ctable_c.at(3) = RGB::yellow;
3013
3014 clocs_c.resize(4);
3015 clocs_c.at(0) = 0.f;
3016 clocs_c.at(1) = 0.4f;
3017 clocs_c.at(2) = 0.7f;
3018 clocs_c.at(3) = 1.f;
3019 } else if (colormap == "gray") {
3020 ctable_c.resize(2);
3021 ctable_c.at(0) = RGB::black;
3022 ctable_c.at(1) = RGB::white;
3023
3024 clocs_c.resize(2);
3025 clocs_c.at(0) = 0.f;
3026 clocs_c.at(1) = 1.f;
3027 } else if (colormap == "green") {
3028 ctable_c.resize(2);
3029 ctable_c.at(0) = RGB::black;
3030 ctable_c.at(1) = RGB::green;
3031
3032 clocs_c.resize(2);
3033 clocs_c.at(0) = 0.f;
3034 clocs_c.at(1) = 1.f;
3035 } else {
3036 helios_runtime_error("ERROR (Context::generateColormapTextures): Unknown colormap " + colormap + ".");
3037 }
3038
3039 return generateColormap(ctable_c, clocs_c, Ncolors);
3040}
3041
3042std::vector<std::string> Context::generateTexturesFromColormap(const std::string &texturefile, const std::vector<RGBcolor> &colormap_data) {
3043 uint Ncolors = colormap_data.size();
3044
3045 // check that texture file exists
3046 std::ifstream tfile(texturefile);
3047 if (!tfile) {
3048 helios_runtime_error("ERROR (Context::generateTexturesFromColormap): Texture file " + texturefile + " does not exist, or you do not have permission to read it.");
3049 }
3050 tfile.close();
3051
3052 // get file extension
3053 std::string file_ext = getFileExtension(texturefile);
3054
3055 // get file base/stem
3056 std::string file_base = getFileStem(texturefile);
3057
3058 std::vector<RGBcolor> color_table(Ncolors);
3059
3060 std::vector<std::string> texture_filenames(Ncolors);
3061
3062 if (file_ext == "png" || file_ext == "PNG") {
3063 std::vector<RGBAcolor> pixel_data;
3064 uint width, height;
3065 readPNG(texturefile, width, height, pixel_data);
3066
3067 for (int i = 0; i < Ncolors; i++) {
3068 std::ostringstream filename;
3069 filename << "lib/images/colormap_" << file_base << "_" << std::setw(4) << std::setfill('0') << std::to_string(i) << ".png";
3070
3071 texture_filenames.at(i) = filename.str();
3072
3073 RGBcolor color = colormap_data.at(i);
3074
3075 for (int row = 0; row < height; row++) {
3076 for (int col = 0; col < width; col++) {
3077 pixel_data.at(row * width + col) = make_RGBAcolor(color, pixel_data.at(row * width + col).a);
3078 }
3079 }
3080
3081 writePNG(filename.str(), width, height, pixel_data);
3082 }
3083 }
3084
3085 return texture_filenames;
3086}
3087
3088void Context::out_of_memory_handler() {
3089 helios_runtime_error("ERROR: Out of host memory. The program has run out of memory and cannot continue.");
3090}
3091
3092void Context::install_out_of_memory_handler() {
3093 std::set_new_handler(out_of_memory_handler);
3094}
3095
3097 for (auto &[UUID, primitive]: primitives) {
3098 delete getPrimitivePointer_private(UUID);
3099 }
3100
3101 for (auto &[UUID, object]: objects) {
3102 delete getObjectPointer(UUID);
3103 }
3104}
3105
3107#ifdef HELIOS_DEBUG
3108 if (!doesPrimitiveExist(UUID)) {
3109 helios_runtime_error("ERROR (Context::getPrimitiveType): Primitive with UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3110 }
3111#endif
3112 return getPrimitivePointer_private(UUID)->getType();
3113}
3114
3116#ifdef HELIOS_DEBUG
3117 if (!doesPrimitiveExist(UUID)) {
3118 helios_runtime_error("ERROR (Context::setPrimitiveParentObjectID): Primitive with UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3119 }
3120#endif
3121
3122 uint current_objID = getPrimitivePointer_private(UUID)->getParentObjectID();
3123 getPrimitivePointer_private(UUID)->setParentObjectID(objID);
3124
3125 if (current_objID != 0u && current_objID != objID) {
3126 if (doesObjectExist(current_objID)) {
3127 objects.at(current_objID)->deleteChildPrimitive(UUID);
3128
3129 if (getObjectPointer_private(current_objID)->getPrimitiveUUIDs().empty()) {
3130 CompoundObject *obj = objects.at(current_objID);
3131 delete obj;
3132 objects.erase(current_objID);
3133 }
3134 }
3135 }
3136}
3137
3138void Context::setPrimitiveParentObjectID(const std::vector<uint> &UUIDs, uint objID) {
3139 for (uint UUID: UUIDs) {
3140 setPrimitiveParentObjectID(UUID, objID);
3141 }
3142}
3143
3145#ifdef HELIOS_DEBUG
3146 if (!doesPrimitiveExist(UUID)) {
3147 helios_runtime_error("ERROR (Context::getPrimitiveParentObjectID): Primitive with UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3148 }
3149#endif
3150 return getPrimitivePointer_private(UUID)->getParentObjectID();
3151}
3152
3153std::vector<uint> Context::getPrimitiveParentObjectID(const std::vector<uint> &UUIDs) const {
3154 std::vector<uint> objIDs(UUIDs.size());
3155 for (uint i = 0; i < UUIDs.size(); i++) {
3156#ifdef HELIOS_DEBUG
3157 if (!doesPrimitiveExist(UUIDs[i])) {
3158 helios_runtime_error("ERROR (Context::getPrimitiveParentObjectID): Primitive with UUID of " + std::to_string(UUIDs[i]) + " does not exist in the Context.");
3159 }
3160#endif
3161 objIDs[i] = getPrimitivePointer_private(UUIDs[i])->getParentObjectID();
3162 }
3163 return objIDs;
3164}
3165
3166
3167std::vector<uint> Context::getUniquePrimitiveParentObjectIDs(const std::vector<uint> &UUIDs) const {
3168 return getUniquePrimitiveParentObjectIDs(UUIDs, false);
3169}
3170
3171
3172std::vector<uint> Context::getUniquePrimitiveParentObjectIDs(const std::vector<uint> &UUIDs, bool include_ObjID_zero) const {
3173 std::vector<uint> primitiveObjIDs;
3174 if (UUIDs.empty()) {
3175 return primitiveObjIDs;
3176 }
3177
3178 // vector of parent object ID for each primitive
3179 primitiveObjIDs.resize(UUIDs.size());
3180 for (uint i = 0; i < UUIDs.size(); i++) {
3181#ifdef HELIOS_DEBUG
3182 if (!doesPrimitiveExist(UUIDs.at(i))) {
3183 helios_runtime_error("ERROR (Context::getUniquePrimitiveParentObjectIDs): Primitive with UUID of " + std::to_string(UUIDs.at(i)) + " does not exist in the Context.");
3184 }
3185#endif
3186 primitiveObjIDs.at(i) = getPrimitivePointer_private(UUIDs.at(i))->getParentObjectID();
3187 }
3188
3189 // sort
3190 std::sort(primitiveObjIDs.begin(), primitiveObjIDs.end());
3191
3192 // unique
3193 auto it = unique(primitiveObjIDs.begin(), primitiveObjIDs.end());
3194 primitiveObjIDs.resize(distance(primitiveObjIDs.begin(), it));
3195
3196 // remove object ID = 0 from the output if desired and it exists
3197 if (include_ObjID_zero == false & primitiveObjIDs.front() == uint(0)) {
3198 primitiveObjIDs.erase(primitiveObjIDs.begin());
3199 }
3200
3201 return primitiveObjIDs;
3202}
3203
3205#ifdef HELIOS_DEBUG
3206 if (!doesPrimitiveExist(UUID)) {
3207 helios_runtime_error("ERROR (Context::getPrimitiveArea): Primitive with UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3208 }
3209#endif
3210 return getPrimitivePointer_private(UUID)->getArea();
3211}
3212
3213void Context::getPrimitiveBoundingBox(uint UUID, vec3 &min_corner, vec3 &max_corner) const {
3214 const std::vector UUIDs = {UUID};
3215 getPrimitiveBoundingBox(UUIDs, min_corner, max_corner);
3216}
3217
3218void Context::getPrimitiveBoundingBox(const std::vector<uint> &UUIDs, vec3 &min_corner, vec3 &max_corner) const {
3219 uint p = 0;
3220 for (uint UUID: UUIDs) {
3221 if (!doesPrimitiveExist(UUID)) {
3222 helios_runtime_error("ERROR (Context::getPrimitiveBoundingBox): Primitive with UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3223 }
3224
3225 const std::vector<vec3> &vertices = getPrimitiveVertices(UUID);
3226
3227 if (p == 0) {
3228 min_corner = vertices.front();
3229 max_corner = min_corner;
3230 }
3231
3232 for (const vec3 &vert: vertices) {
3233 if (vert.x < min_corner.x) {
3234 min_corner.x = vert.x;
3235 }
3236 if (vert.y < min_corner.y) {
3237 min_corner.y = vert.y;
3238 }
3239 if (vert.z < min_corner.z) {
3240 min_corner.z = vert.z;
3241 }
3242 if (vert.x > max_corner.x) {
3243 max_corner.x = vert.x;
3244 }
3245 if (vert.y > max_corner.y) {
3246 max_corner.y = vert.y;
3247 }
3248 if (vert.z > max_corner.z) {
3249 max_corner.z = vert.z;
3250 }
3251 }
3252
3253 p++;
3254 }
3255}
3256
3258 return getPrimitivePointer_private(UUID)->getNormal();
3259}
3260
3261void Context::getPrimitiveTransformationMatrix(uint UUID, float (&T)[16]) const {
3262 getPrimitivePointer_private(UUID)->getTransformationMatrix(T);
3263}
3264
3266 getPrimitivePointer_private(UUID)->setTransformationMatrix(T);
3267}
3268
3269void Context::setPrimitiveTransformationMatrix(const std::vector<uint> &UUIDs, float (&T)[16]) {
3270 for (uint UUID: UUIDs) {
3271 getPrimitivePointer_private(UUID)->setTransformationMatrix(T);
3272 }
3273}
3274
3275std::vector<helios::vec3> Context::getPrimitiveVertices(uint UUID) const {
3276 return getPrimitivePointer_private(UUID)->getVertices();
3277}
3278
3279
3281 return getPrimitivePointer_private(UUID)->getColor();
3282}
3283
3285 return getPrimitivePointer_private(UUID)->getColorRGB();
3286}
3287
3289 return getPrimitivePointer_private(UUID)->getColorRGBA();
3290}
3291
3292void Context::setPrimitiveColor(uint UUID, const RGBcolor &color) const {
3293 getPrimitivePointer_private(UUID)->setColor(color);
3294}
3295
3296void Context::setPrimitiveColor(const std::vector<uint> &UUIDs, const RGBcolor &color) const {
3297 for (uint UUID: UUIDs) {
3298 getPrimitivePointer_private(UUID)->setColor(color);
3299 }
3300}
3301
3302void Context::setPrimitiveColor(uint UUID, const RGBAcolor &color) const {
3303 getPrimitivePointer_private(UUID)->setColor(color);
3304}
3305
3306void Context::setPrimitiveColor(const std::vector<uint> &UUIDs, const RGBAcolor &color) const {
3307 for (uint UUID: UUIDs) {
3308 getPrimitivePointer_private(UUID)->setColor(color);
3309 }
3310}
3311
3313 return getPrimitivePointer_private(UUID)->getTextureFile();
3314}
3315
3316void Context::setPrimitiveTextureFile(uint UUID, const std::string &texturefile) const {
3317 getPrimitivePointer_private(UUID)->setTextureFile(texturefile.c_str());
3318}
3319
3321 std::string texturefile = getPrimitivePointer_private(UUID)->getTextureFile();
3322 if (!texturefile.empty() && textures.find(texturefile) != textures.end()) {
3323 return textures.at(texturefile).getImageResolution();
3324 }
3325 return {0, 0};
3326}
3327
3328std::vector<helios::vec2> Context::getPrimitiveTextureUV(uint UUID) const {
3329 return getPrimitivePointer_private(UUID)->getTextureUV();
3330}
3331
3333 std::string texturefile = getPrimitivePointer_private(UUID)->getTextureFile();
3334 if (!texturefile.empty() && textures.find(texturefile) != textures.end()) {
3335 return textures.at(texturefile).hasTransparencyChannel();
3336 }
3337 return false;
3338}
3339
3340const std::vector<std::vector<bool>> *Context::getPrimitiveTextureTransparencyData(uint UUID) const {
3342 const std::vector<std::vector<bool>> *data = textures.at(getPrimitivePointer_private(UUID)->getTextureFile()).getTransparencyData();
3343 return data;
3344 }
3345
3346 helios_runtime_error("ERROR (Context::getPrimitiveTransparencyData): Texture transparency data does not exist for primitive " + std::to_string(UUID) + ".");
3347 return nullptr;
3348}
3349
3351 getPrimitivePointer_private(UUID)->overrideTextureColor();
3352}
3353
3354void Context::overridePrimitiveTextureColor(const std::vector<uint> &UUIDs) const {
3355 for (uint UUID: UUIDs) {
3356 getPrimitivePointer_private(UUID)->overrideTextureColor();
3357 }
3358}
3359
3361 getPrimitivePointer_private(UUID)->useTextureColor();
3362}
3363
3364void Context::usePrimitiveTextureColor(const std::vector<uint> &UUIDs) const {
3365 for (uint UUID: UUIDs) {
3366 getPrimitivePointer_private(UUID)->useTextureColor();
3367 }
3368}
3369
3371 return getPrimitivePointer_private(UUID)->isTextureColorOverridden();
3372}
3373
3375 return getPrimitivePointer_private(UUID)->getSolidFraction();
3376}
3377
3379 std::cout << "-------------------------------------------" << std::endl;
3380 std::cout << "Info for UUID " << UUID << std::endl;
3381 std::cout << "-------------------------------------------" << std::endl;
3382
3383 PrimitiveType type = getPrimitiveType(UUID);
3384 std::string stype;
3385 if (type == 0) {
3386 stype = "PRIMITIVE_TYPE_PATCH";
3387 } else if (type == 1) {
3388 stype = "PRIMITIVE_TYPE_TRIANGLE";
3389 } else if (type == 2) {
3390 stype = "PRIMITIVE_TYPE_VOXEL";
3391 }
3392
3393 std::cout << "Type: " << stype << std::endl;
3394 std::cout << "Parent ObjID: " << getPrimitiveParentObjectID(UUID) << std::endl;
3395 std::cout << "Surface Area: " << getPrimitiveArea(UUID) << std::endl;
3396 std::cout << "Normal Vector: " << getPrimitiveNormal(UUID) << std::endl;
3397
3398 if (type == PRIMITIVE_TYPE_PATCH) {
3399 std::cout << "Patch Center: " << getPatchCenter(UUID) << std::endl;
3400 std::cout << "Patch Size: " << getPatchSize(UUID) << std::endl;
3401 } else if (type == PRIMITIVE_TYPE_VOXEL) {
3402 std::cout << "Voxel Center: " << getVoxelCenter(UUID) << std::endl;
3403 std::cout << "Voxel Size: " << getVoxelSize(UUID) << std::endl;
3404 }
3405
3406 std::vector<vec3> primitive_vertices = getPrimitiveVertices(UUID);
3407 std::cout << "Vertices: " << std::endl;
3408 for (uint i = 0; i < primitive_vertices.size(); i++) {
3409 std::cout << " " << primitive_vertices.at(i) << std::endl;
3410 }
3411
3412 float T[16];
3414 std::cout << "Transform: " << std::endl;
3415 std::cout << " " << T[0] << " " << T[1] << " " << T[2] << " " << T[3] << std::endl;
3416 std::cout << " " << T[4] << " " << T[5] << " " << T[6] << " " << T[7] << std::endl;
3417 std::cout << " " << T[8] << " " << T[9] << " " << T[10] << " " << T[11] << std::endl;
3418 std::cout << " " << T[12] << " " << T[13] << " " << T[14] << " " << T[15] << std::endl;
3419
3420 std::cout << "Color: " << getPrimitiveColor(UUID) << std::endl;
3421 std::cout << "Texture File: " << getPrimitiveTextureFile(UUID) << std::endl;
3422 std::cout << "Texture Size: " << getPrimitiveTextureSize(UUID) << std::endl;
3423 std::cout << "Texture UV: " << std::endl;
3424 std::vector<vec2> uv = getPrimitiveTextureUV(UUID);
3425 for (uint i = 0; i < uv.size(); i++) {
3426 std::cout << " " << uv.at(i) << std::endl;
3427 }
3428
3429 std::cout << "Texture Transparency: " << primitiveTextureHasTransparencyChannel(UUID) << std::endl;
3430 std::cout << "Color Overridden: " << isPrimitiveTextureColorOverridden(UUID) << std::endl;
3431 std::cout << "Solid Fraction: " << getPrimitiveSolidFraction(UUID) << std::endl;
3432
3433
3434 std::cout << "Primitive Data: " << std::endl;
3435 // Primitive* pointer = getPrimitivePointer_private(UUID);
3436 std::vector<std::string> pd = listPrimitiveData(UUID);
3437 for (uint i = 0; i < pd.size(); i++) {
3438 uint dsize = getPrimitiveDataSize(UUID, pd.at(i).c_str());
3439 HeliosDataType dtype = getPrimitiveDataType(pd.at(i).c_str());
3440 std::string dstype;
3441
3442 if (dtype == HELIOS_TYPE_INT) {
3443 dstype = "HELIOS_TYPE_INT";
3444 } else if (dtype == HELIOS_TYPE_UINT) {
3445 dstype = "HELIOS_TYPE_UINT";
3446 } else if (dtype == HELIOS_TYPE_FLOAT) {
3447 dstype = "HELIOS_TYPE_FLOAT";
3448 } else if (dtype == HELIOS_TYPE_DOUBLE) {
3449 dstype = "HELIOS_TYPE_DOUBLE";
3450 } else if (dtype == HELIOS_TYPE_VEC2) {
3451 dstype = "HELIOS_TYPE_VEC2";
3452 } else if (dtype == HELIOS_TYPE_VEC3) {
3453 dstype = "HELIOS_TYPE_VEC3";
3454 } else if (dtype == HELIOS_TYPE_VEC4) {
3455 dstype = "HELIOS_TYPE_VEC4";
3456 } else if (dtype == HELIOS_TYPE_INT2) {
3457 dstype = "HELIOS_TYPE_INT2";
3458 } else if (dtype == HELIOS_TYPE_INT3) {
3459 dstype = "HELIOS_TYPE_INT3";
3460 } else if (dtype == HELIOS_TYPE_INT4) {
3461 dstype = "HELIOS_TYPE_INT4";
3462 } else if (dtype == HELIOS_TYPE_STRING) {
3463 dstype = "HELIOS_TYPE_STRING";
3464 } else {
3465 assert(false);
3466 }
3467
3468
3469 std::cout << " " << "[name: " << pd.at(i) << ", type: " << dstype << ", size: " << dsize << "]:" << std::endl;
3470
3471
3472 if (dtype == HELIOS_TYPE_INT) {
3473 std::vector<int> pdata;
3474 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3475 for (uint j = 0; j < dsize; j++) {
3476 if (j < 10) {
3477 std::cout << " " << pdata.at(j) << std::endl;
3478 } else {
3479 std::cout << " ..." << std::endl;
3480 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3481 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3482 break;
3483 }
3484 }
3485 } else if (dtype == HELIOS_TYPE_UINT) {
3486 std::vector<uint> pdata;
3487 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3488 for (uint j = 0; j < dsize; j++) {
3489 if (j < 10) {
3490 std::cout << " " << pdata.at(j) << std::endl;
3491 } else {
3492 std::cout << " ..." << std::endl;
3493 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3494 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3495 break;
3496 }
3497 }
3498 } else if (dtype == HELIOS_TYPE_FLOAT) {
3499 std::vector<float> pdata;
3500 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3501 for (uint j = 0; j < dsize; j++) {
3502 if (j < 10) {
3503 std::cout << " " << pdata.at(j) << std::endl;
3504 } else {
3505 std::cout << " ..." << std::endl;
3506 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3507 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3508 break;
3509 }
3510 }
3511 } else if (dtype == HELIOS_TYPE_DOUBLE) {
3512 std::vector<double> pdata;
3513 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3514 for (uint j = 0; j < dsize; j++) {
3515 if (j < 10) {
3516 std::cout << " " << pdata.at(j) << std::endl;
3517 } else {
3518 std::cout << " ..." << std::endl;
3519 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3520 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3521 break;
3522 }
3523 }
3524 } else if (dtype == HELIOS_TYPE_VEC2) {
3525 std::vector<vec2> pdata;
3526 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3527 for (uint j = 0; j < dsize; j++) {
3528 if (j < 10) {
3529 std::cout << " " << pdata.at(j) << std::endl;
3530 } else {
3531 std::cout << " ..." << std::endl;
3532 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3533 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3534 break;
3535 }
3536 }
3537 } else if (dtype == HELIOS_TYPE_VEC3) {
3538 std::vector<vec3> pdata;
3539 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3540 for (uint j = 0; j < dsize; j++) {
3541 if (j < 10) {
3542 std::cout << " " << pdata.at(j) << std::endl;
3543 } else {
3544 std::cout << " ..." << std::endl;
3545 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3546 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3547 break;
3548 }
3549 }
3550 } else if (dtype == HELIOS_TYPE_VEC4) {
3551 std::vector<vec4> pdata;
3552 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3553 for (uint j = 0; j < dsize; j++) {
3554 if (j < 10) {
3555 std::cout << " " << pdata.at(j) << std::endl;
3556 } else {
3557 std::cout << " ..." << std::endl;
3558 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3559 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3560 break;
3561 }
3562 }
3563 } else if (dtype == HELIOS_TYPE_INT2) {
3564 std::vector<int2> pdata;
3565 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3566 for (uint j = 0; j < dsize; j++) {
3567 if (j < 10) {
3568 std::cout << " " << pdata.at(j) << std::endl;
3569 } else {
3570 std::cout << " ..." << std::endl;
3571 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3572 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3573 break;
3574 }
3575 }
3576 } else if (dtype == HELIOS_TYPE_INT3) {
3577 std::vector<int3> pdata;
3578 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3579 for (uint j = 0; j < dsize; j++) {
3580 if (j < 10) {
3581 std::cout << " " << pdata.at(j) << std::endl;
3582 } else {
3583 std::cout << " ..." << std::endl;
3584 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3585 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3586 break;
3587 }
3588 }
3589 } else if (dtype == HELIOS_TYPE_INT4) {
3590 std::vector<int4> pdata;
3591 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3592 for (uint j = 0; j < dsize; j++) {
3593 if (j < 10) {
3594 std::cout << " " << pdata.at(j) << std::endl;
3595 } else {
3596 std::cout << " ..." << std::endl;
3597 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3598 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3599 break;
3600 }
3601 }
3602 } else if (dtype == HELIOS_TYPE_STRING) {
3603 std::vector<std::string> pdata;
3604 getPrimitiveData(UUID, pd.at(i).c_str(), pdata);
3605 for (uint j = 0; j < dsize; j++) {
3606 if (j < 10) {
3607 std::cout << " " << pdata.at(j) << std::endl;
3608 } else {
3609 std::cout << " ..." << std::endl;
3610 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3611 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3612 break;
3613 }
3614 }
3615 } else {
3616 assert(false);
3617 }
3618 }
3619 std::cout << "-------------------------------------------" << std::endl;
3620}
3621
3623 std::cout << "-------------------------------------------" << std::endl;
3624 std::cout << "Info for ObjID " << ObjID << std::endl;
3625 std::cout << "-------------------------------------------" << std::endl;
3626
3627 ObjectType otype = getObjectType(ObjID);
3628 std::string ostype;
3629 if (otype == 0) {
3630 ostype = "OBJECT_TYPE_TILE";
3631 } else if (otype == 1) {
3632 ostype = "OBJECT_TYPE_SPHERE";
3633 } else if (otype == 2) {
3634 ostype = "OBJECT_TYPE_TUBE";
3635 } else if (otype == 3) {
3636 ostype = "OBJECT_TYPE_BOX";
3637 } else if (otype == 4) {
3638 ostype = "OBJECT_TYPE_DISK";
3639 } else if (otype == 5) {
3640 ostype = "OBJECT_TYPE_POLYMESH";
3641 } else if (otype == 6) {
3642 ostype = "OBJECT_TYPE_CONE";
3643 }
3644
3645 std::cout << "Type: " << ostype << std::endl;
3646 std::cout << "Object Bounding Box Center: " << getObjectCenter(ObjID) << std::endl;
3647 std::cout << "One-sided Surface Area: " << getObjectArea(ObjID) << std::endl;
3648
3649 std::cout << "Primitive Count: " << getObjectPrimitiveCount(ObjID) << std::endl;
3650
3651 if (areObjectPrimitivesComplete(ObjID)) {
3652 std::cout << "Object Primitives Complete" << std::endl;
3653 } else {
3654 std::cout << "Object Primitives Incomplete" << std::endl;
3655 }
3656
3657 std::cout << "Primitive UUIDs: " << std::endl;
3658 std::vector<uint> primitive_UUIDs = getObjectPrimitiveUUIDs(ObjID);
3659 for (uint i = 0; i < primitive_UUIDs.size(); i++) {
3660 if (i < 5) {
3661 PrimitiveType ptype = getPrimitiveType(primitive_UUIDs.at(i));
3662 std::string pstype;
3663 if (ptype == 0) {
3664 pstype = "PRIMITIVE_TYPE_PATCH";
3665 } else if (ptype == 1) {
3666 pstype = "PRIMITIVE_TYPE_TRIANGLE";
3667 }
3668 std::cout << " " << primitive_UUIDs.at(i) << " (" << pstype << ")" << std::endl;
3669 } else {
3670 std::cout << " ..." << std::endl;
3671 PrimitiveType ptype = getPrimitiveType(primitive_UUIDs.at(primitive_UUIDs.size() - 2));
3672 std::string pstype;
3673 if (ptype == 0) {
3674 pstype = "PRIMITIVE_TYPE_PATCH";
3675 } else if (ptype == 1) {
3676 pstype = "PRIMITIVE_TYPE_TRIANGLE";
3677 }
3678 std::cout << " " << primitive_UUIDs.at(primitive_UUIDs.size() - 2) << " (" << pstype << ")" << std::endl;
3679 ptype = getPrimitiveType(primitive_UUIDs.at(primitive_UUIDs.size() - 1));
3680 if (ptype == 0) {
3681 pstype = "PRIMITIVE_TYPE_PATCH";
3682 } else if (ptype == 1) {
3683 pstype = "PRIMITIVE_TYPE_TRIANGLE";
3684 }
3685 std::cout << " " << primitive_UUIDs.at(primitive_UUIDs.size() - 1) << " (" << pstype << ")" << std::endl;
3686 break;
3687 }
3688 }
3689
3690 if (otype == OBJECT_TYPE_TILE) {
3691 std::cout << "Tile Center: " << getTileObjectCenter(ObjID) << std::endl;
3692 std::cout << "Tile Size: " << getTileObjectSize(ObjID) << std::endl;
3693 std::cout << "Tile Subdivision Count: " << getTileObjectSubdivisionCount(ObjID) << std::endl;
3694 std::cout << "Tile Normal: " << getTileObjectNormal(ObjID) << std::endl;
3695
3696 std::cout << "Tile Texture UV: " << std::endl;
3697 std::vector<vec2> uv = getTileObjectTextureUV(ObjID);
3698 for (uint i = 0; i < uv.size(); i++) {
3699 std::cout << " " << uv.at(i) << std::endl;
3700 }
3701
3702 std::cout << "Tile Vertices: " << std::endl;
3703 std::vector<vec3> primitive_vertices = getTileObjectVertices(ObjID);
3704 for (uint i = 0; i < primitive_vertices.size(); i++) {
3705 std::cout << " " << primitive_vertices.at(i) << std::endl;
3706 }
3707 } else if (otype == OBJECT_TYPE_SPHERE) {
3708 std::cout << "Sphere Center: " << getSphereObjectCenter(ObjID) << std::endl;
3709 std::cout << "Sphere Radius: " << getSphereObjectRadius(ObjID) << std::endl;
3710 std::cout << "Sphere Subdivision Count: " << getSphereObjectSubdivisionCount(ObjID) << std::endl;
3711 } else if (otype == OBJECT_TYPE_TUBE) {
3712 std::cout << "Tube Subdivision Count: " << getTubeObjectSubdivisionCount(ObjID) << std::endl;
3713 std::cout << "Tube Nodes: " << std::endl;
3714 std::vector<vec3> nodes = getTubeObjectNodes(ObjID);
3715 for (uint i = 0; i < nodes.size(); i++) {
3716 if (i < 10) {
3717 std::cout << " " << nodes.at(i) << std::endl;
3718 } else {
3719 std::cout << " ..." << std::endl;
3720 std::cout << " " << nodes.at(nodes.size() - 2) << std::endl;
3721 std::cout << " " << nodes.at(nodes.size() - 1) << std::endl;
3722 break;
3723 }
3724 }
3725 std::cout << "Tube Node Radii: " << std::endl;
3726 std::vector<float> noderadii = getTubeObjectNodeRadii(ObjID);
3727 for (uint i = 0; i < noderadii.size(); i++) {
3728 if (i < 10) {
3729 std::cout << " " << noderadii.at(i) << std::endl;
3730 } else {
3731 std::cout << " ..." << std::endl;
3732 std::cout << " " << noderadii.at(noderadii.size() - 2) << std::endl;
3733 std::cout << " " << noderadii.at(noderadii.size() - 1) << std::endl;
3734 break;
3735 }
3736 }
3737 std::cout << "Tube Node Colors: " << std::endl;
3738 std::vector<helios::RGBcolor> nodecolors = getTubeObjectNodeColors(ObjID);
3739 for (uint i = 0; i < nodecolors.size(); i++) {
3740 if (i < 10) {
3741 std::cout << " " << nodecolors.at(i) << std::endl;
3742 } else {
3743 std::cout << " ..." << std::endl;
3744 std::cout << " " << nodecolors.at(nodecolors.size() - 2) << std::endl;
3745 std::cout << " " << nodecolors.at(nodecolors.size() - 1) << std::endl;
3746 break;
3747 }
3748 }
3749 } else if (otype == OBJECT_TYPE_BOX) {
3750 std::cout << "Box Center: " << getBoxObjectCenter(ObjID) << std::endl;
3751 std::cout << "Box Size: " << getBoxObjectSize(ObjID) << std::endl;
3752 std::cout << "Box Subdivision Count: " << getBoxObjectSubdivisionCount(ObjID) << std::endl;
3753 } else if (otype == OBJECT_TYPE_DISK) {
3754 std::cout << "Disk Center: " << getDiskObjectCenter(ObjID) << std::endl;
3755 std::cout << "Disk Size: " << getDiskObjectSize(ObjID) << std::endl;
3756 std::cout << "Disk Subdivision Count: " << getDiskObjectSubdivisionCount(ObjID) << std::endl;
3757
3758 // }else if(type == OBJECT_TYPE_POLYMESH){
3759 // nothing for now
3760 } else if (otype == OBJECT_TYPE_CONE) {
3761 std::cout << "Cone Length: " << getConeObjectLength(ObjID) << std::endl;
3762 std::cout << "Cone Axis Unit Vector: " << getConeObjectAxisUnitVector(ObjID) << std::endl;
3763 std::cout << "Cone Subdivision Count: " << getConeObjectSubdivisionCount(ObjID) << std::endl;
3764 std::cout << "Cone Nodes: " << std::endl;
3765 std::vector<vec3> nodes = getConeObjectNodes(ObjID);
3766 for (uint i = 0; i < nodes.size(); i++) {
3767 std::cout << " " << nodes.at(i) << std::endl;
3768 }
3769 std::cout << "Cone Node Radii: " << std::endl;
3770 std::vector<float> noderadii = getConeObjectNodeRadii(ObjID);
3771 for (uint i = 0; i < noderadii.size(); i++) {
3772 std::cout << " " << noderadii.at(i) << std::endl;
3773 }
3774 }
3775
3776
3777 float T[16];
3779 std::cout << "Transform: " << std::endl;
3780 std::cout << " " << T[0] << " " << T[1] << " " << T[2] << " " << T[3] << std::endl;
3781 std::cout << " " << T[4] << " " << T[5] << " " << T[6] << " " << T[7] << std::endl;
3782 std::cout << " " << T[8] << " " << T[9] << " " << T[10] << " " << T[11] << std::endl;
3783 std::cout << " " << T[12] << " " << T[13] << " " << T[14] << " " << T[15] << std::endl;
3784
3785 std::cout << "Texture File: " << getObjectTextureFile(ObjID) << std::endl;
3786
3787 std::cout << "Object Data: " << std::endl;
3788 // Primitive* pointer = getPrimitivePointer_private(ObjID);
3789 std::vector<std::string> pd = listObjectData(ObjID);
3790 for (uint i = 0; i < pd.size(); i++) {
3791 uint dsize = getObjectDataSize(ObjID, pd.at(i).c_str());
3792 HeliosDataType dtype = getObjectDataType(pd.at(i).c_str());
3793 std::string dstype;
3794
3795 if (dtype == HELIOS_TYPE_INT) {
3796 dstype = "HELIOS_TYPE_INT";
3797 } else if (dtype == HELIOS_TYPE_UINT) {
3798 dstype = "HELIOS_TYPE_UINT";
3799 } else if (dtype == HELIOS_TYPE_FLOAT) {
3800 dstype = "HELIOS_TYPE_FLOAT";
3801 } else if (dtype == HELIOS_TYPE_DOUBLE) {
3802 dstype = "HELIOS_TYPE_DOUBLE";
3803 } else if (dtype == HELIOS_TYPE_VEC2) {
3804 dstype = "HELIOS_TYPE_VEC2";
3805 } else if (dtype == HELIOS_TYPE_VEC3) {
3806 dstype = "HELIOS_TYPE_VEC3";
3807 } else if (dtype == HELIOS_TYPE_VEC4) {
3808 dstype = "HELIOS_TYPE_VEC4";
3809 } else if (dtype == HELIOS_TYPE_INT2) {
3810 dstype = "HELIOS_TYPE_INT2";
3811 } else if (dtype == HELIOS_TYPE_INT3) {
3812 dstype = "HELIOS_TYPE_INT3";
3813 } else if (dtype == HELIOS_TYPE_INT4) {
3814 dstype = "HELIOS_TYPE_INT4";
3815 } else if (dtype == HELIOS_TYPE_STRING) {
3816 dstype = "HELIOS_TYPE_STRING";
3817 } else {
3818 assert(false);
3819 }
3820
3821
3822 std::cout << " " << "[name: " << pd.at(i) << ", type: " << dstype << ", size: " << dsize << "]:" << std::endl;
3823
3824
3825 if (dtype == HELIOS_TYPE_INT) {
3826 std::vector<int> pdata;
3827 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3828 for (uint j = 0; j < dsize; j++) {
3829 if (j < 10) {
3830 std::cout << " " << pdata.at(j) << std::endl;
3831 } else {
3832 std::cout << " ..." << std::endl;
3833 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3834 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3835 break;
3836 }
3837 }
3838 } else if (dtype == HELIOS_TYPE_UINT) {
3839 std::vector<uint> pdata;
3840 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3841 for (uint j = 0; j < dsize; j++) {
3842 if (j < 10) {
3843 std::cout << " " << pdata.at(j) << std::endl;
3844 } else {
3845 std::cout << " ..." << std::endl;
3846 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3847 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3848 break;
3849 }
3850 }
3851 } else if (dtype == HELIOS_TYPE_FLOAT) {
3852 std::vector<float> pdata;
3853 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3854 for (uint j = 0; j < dsize; j++) {
3855 if (j < 10) {
3856 std::cout << " " << pdata.at(j) << std::endl;
3857 } else {
3858 std::cout << " ..." << std::endl;
3859 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3860 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3861 break;
3862 }
3863 }
3864 } else if (dtype == HELIOS_TYPE_DOUBLE) {
3865 std::vector<double> pdata;
3866 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3867 for (uint j = 0; j < dsize; j++) {
3868 if (j < 10) {
3869 std::cout << " " << pdata.at(j) << std::endl;
3870 } else {
3871 std::cout << " ..." << std::endl;
3872 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3873 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3874 break;
3875 }
3876 }
3877 } else if (dtype == HELIOS_TYPE_VEC2) {
3878 std::vector<vec2> pdata;
3879 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3880 for (uint j = 0; j < dsize; j++) {
3881 if (j < 10) {
3882 std::cout << " " << pdata.at(j) << std::endl;
3883 } else {
3884 std::cout << " ..." << std::endl;
3885 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3886 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3887 break;
3888 }
3889 }
3890 } else if (dtype == HELIOS_TYPE_VEC3) {
3891 std::vector<vec3> pdata;
3892 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3893 for (uint j = 0; j < dsize; j++) {
3894 if (j < 10) {
3895 std::cout << " " << pdata.at(j) << std::endl;
3896 } else {
3897 std::cout << " ..." << std::endl;
3898 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3899 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3900 break;
3901 }
3902 }
3903 } else if (dtype == HELIOS_TYPE_VEC4) {
3904 std::vector<vec4> pdata;
3905 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3906 for (uint j = 0; j < dsize; j++) {
3907 if (j < 10) {
3908 std::cout << " " << pdata.at(j) << std::endl;
3909 } else {
3910 std::cout << " ..." << std::endl;
3911 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3912 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3913 break;
3914 }
3915 }
3916 } else if (dtype == HELIOS_TYPE_INT2) {
3917 std::vector<int2> pdata;
3918 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3919 for (uint j = 0; j < dsize; j++) {
3920 if (j < 10) {
3921 std::cout << " " << pdata.at(j) << std::endl;
3922 } else {
3923 std::cout << " ..." << std::endl;
3924 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3925 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3926 break;
3927 }
3928 }
3929 } else if (dtype == HELIOS_TYPE_INT3) {
3930 std::vector<int3> pdata;
3931 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3932 for (uint j = 0; j < dsize; j++) {
3933 if (j < 10) {
3934 std::cout << " " << pdata.at(j) << std::endl;
3935 } else {
3936 std::cout << " ..." << std::endl;
3937 std::cout << " " << pdata.at(dsize - 2) << std::endl;
3938 std::cout << " " << pdata.at(dsize - 1) << std::endl;
3939 break;
3940 }
3941 }
3942 } else if (dtype == HELIOS_TYPE_INT4) {
3943 std::vector<int4> pdata;
3944 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3945 for (uint j = 0; j < dsize; j++) {
3946 if (j < 10) {
3947 std::cout << " " << pdata.at(j) << std::endl;
3948 } else {
3949 std::cout << " ..." << std::endl;
3950 break;
3951 }
3952 }
3953 } else if (dtype == HELIOS_TYPE_STRING) {
3954 std::vector<std::string> pdata;
3955 getObjectData(ObjID, pd.at(i).c_str(), pdata);
3956 for (uint j = 0; j < dsize; j++) {
3957 if (j < 10) {
3958 std::cout << " " << pdata.at(j) << std::endl;
3959 } else {
3960 std::cout << " ..." << std::endl;
3961 break;
3962 }
3963 }
3964 } else {
3965 assert(false);
3966 }
3967 }
3968 std::cout << "-------------------------------------------" << std::endl;
3969}
3970
3971CompoundObject *Context::getObjectPointer_private(uint ObjID) const {
3972#ifdef HELIOS_DEBUG
3973 if (objects.find(ObjID) == objects.end()) {
3974 helios_runtime_error("ERROR (Context::getObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
3975 }
3976#endif
3977 return objects.at(ObjID);
3978}
3979
3981#ifdef HELIOS_DEBUG
3982 if (!doesObjectExist(ObjID)) {
3983 helios_runtime_error("ERROR (Context::hideObject): Object ID of " + std::to_string(ObjID) + " does not exist in the Context.");
3984 }
3985#endif
3986 objects.at(ObjID)->ishidden = true;
3987 for (uint UUID: objects.at(ObjID)->getPrimitiveUUIDs()) {
3988#ifdef HELIOS_DEBUG
3989 if (!doesPrimitiveExist(UUID)) {
3990 helios_runtime_error("ERROR (Context::hideObject): Primitive UUID of " + std::to_string(UUID) + " does not exist in the Context.");
3991 }
3992#endif
3993 primitives.at(UUID)->ishidden = true;
3994 }
3995}
3996
3997void Context::hideObject(const std::vector<uint> &ObjIDs) {
3998 for (uint ObjID: ObjIDs) {
3999 hideObject(ObjID);
4000 }
4001}
4002
4004#ifdef HELIOS_DEBUG
4005 if (!doesObjectExist(ObjID)) {
4006 helios_runtime_error("ERROR (Context::showObject): Object ID of " + std::to_string(ObjID) + " does not exist in the Context.");
4007 }
4008#endif
4009 objects.at(ObjID)->ishidden = false;
4010 for (uint UUID: objects.at(ObjID)->getPrimitiveUUIDs()) {
4011#ifdef HELIOS_DEBUG
4012 if (!doesPrimitiveExist(UUID)) {
4013 helios_runtime_error("ERROR (Context::showObject): Primitive UUID of " + std::to_string(UUID) + " does not exist in the Context.");
4014 }
4015#endif
4016 primitives.at(UUID)->ishidden = false;
4017 }
4018}
4019
4020void Context::showObject(const std::vector<uint> &ObjIDs) {
4021 for (uint ObjID: ObjIDs) {
4022 showObject(ObjID);
4023 }
4024}
4025
4027 if (!doesObjectExist(ObjID)) {
4028 helios_runtime_error("ERROR (Context::isObjectHidden): Object ID of " + std::to_string(ObjID) + " does not exist in the Context.");
4029 }
4030 return objects.at(ObjID)->ishidden;
4031}
4032
4033float Context::getObjectArea(uint ObjID) const {
4034 return getObjectPointer_private(ObjID)->getArea();
4035}
4036
4038#ifdef HELIOS_DEBUG
4039 if (objects.find(ObjID) == objects.end()) {
4040 helios_runtime_error("ERROR (Context::getObjectAverageNormal): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4041 }
4042#endif
4043
4044 const std::vector<uint> &UUIDs = objects.at(ObjID)->getPrimitiveUUIDs();
4045
4046 vec3 norm_avg;
4047 for (uint UUID: UUIDs) {
4048 norm_avg += getPrimitiveNormal(UUID);
4049 }
4050 norm_avg.normalize();
4051
4052 return norm_avg;
4053}
4054
4056 return getObjectPointer_private(ObjID)->getPrimitiveCount();
4057}
4058
4060 return getObjectPointer_private(ObjID)->getObjectCenter();
4061}
4062
4063std::string Context::getObjectTextureFile(uint ObjID) const {
4064 return getObjectPointer_private(ObjID)->getTextureFile();
4065}
4066
4067void Context::getObjectTransformationMatrix(uint ObjID, float (&T)[16]) const {
4068 getObjectPointer_private(ObjID)->getTransformationMatrix(T);
4069}
4070
4071void Context::setObjectTransformationMatrix(uint ObjID, float (&T)[16]) const {
4072 getObjectPointer_private(ObjID)->setTransformationMatrix(T);
4073}
4074
4075void Context::setObjectTransformationMatrix(const std::vector<uint> &ObjIDs, float (&T)[16]) const {
4076 for (uint ObjID: ObjIDs) {
4077 getObjectPointer_private(ObjID)->setTransformationMatrix(T);
4078 }
4079}
4080
4081void Context::setObjectAverageNormal(uint ObjID, const vec3 &origin, const vec3 &new_normal) const {
4082#ifdef HELIOS_DEBUG
4083 if (!doesObjectExist(ObjID)) {
4084 helios_runtime_error("setObjectAverageNormal: invalid objectID");
4085 }
4086#endif
4087
4088 // 1) Compute unit old & new normals
4089 vec3 oldN = normalize(getObjectAverageNormal(ObjID));
4090 vec3 newN = normalize(new_normal);
4091
4092 // 2) Minimal‐angle axis & angle
4093 float d = std::clamp(oldN * newN, -1.f, 1.f);
4094 float angle = acosf(d);
4095 vec3 axis = cross(oldN, newN);
4096 if (axis.magnitude() < 1e-6f) {
4097 // pick any vector ⟂ oldN
4098 axis = (std::abs(oldN.x) < std::abs(oldN.z)) ? cross(oldN, {1, 0, 0}) : cross(oldN, {0, 0, 1});
4099 }
4100 axis = axis.normalize();
4101
4102 // 3) Apply that minimal‐angle rotation to the compound (no pizza‐spin yet)
4103 // NOTE: correct argument order is (objectID, angle, origin, axis)
4104 rotateObject(ObjID, angle, origin, axis);
4105
4106 // 4) Fetch the updated transform and extract the world‐space “forward” (local +X)
4107 float M_mid[16];
4108 getObjectPointer_private(ObjID)->getTransformationMatrix(M_mid);
4109
4110 vec3 localX{1, 0, 0};
4111 vec3 t1;
4112 // vecmult multiplies the 4×4 M_mid by v3 (w=0), writing into t1
4113 vecmult(M_mid, localX, t1);
4114 t1 = normalize(t1);
4115
4116 // 5) Compute desired forward = world‐X projected into the new plane
4117 vec3 worldX{1, 0, 0};
4118 vec3 targ = worldX - newN * (newN * worldX);
4119 targ = normalize(targ);
4120
4121 // 6) Compute signed twist about newN that carries t1→targ
4122 float twist = atan2f(newN * cross(t1, targ), // dot(newN, t1×targ)
4123 t1 * targ // dot(t1, targ)
4124 );
4125
4126 // 7) Apply that compensating twist about the same origin
4127 rotateObject(ObjID, twist, origin, newN);
4128}
4129
4130void Context::setObjectOrigin(uint ObjID, const vec3 &origin) const {
4131#ifdef HELIOS_DEBUG
4132 if (!doesObjectExist(ObjID)) {
4133 helios_runtime_error("ERROR (Context::setObjectOrigin): invalid objectID");
4134 }
4135#endif
4136 objects.at(ObjID)->object_origin = origin;
4137}
4138
4140 return getObjectPointer_private(ObjID)->hasTexture();
4141}
4142
4143void Context::setObjectColor(uint ObjID, const RGBcolor &color) const {
4144 getObjectPointer_private(ObjID)->setColor(color);
4145}
4146
4147void Context::setObjectColor(const std::vector<uint> &ObjIDs, const RGBcolor &color) const {
4148 for (const uint ObjID: ObjIDs) {
4149 getObjectPointer_private(ObjID)->setColor(color);
4150 }
4151}
4152
4153void Context::setObjectColor(uint ObjID, const RGBAcolor &color) const {
4154 getObjectPointer_private(ObjID)->setColor(color);
4155}
4156
4157void Context::setObjectColor(const std::vector<uint> &ObjIDs, const RGBAcolor &color) const {
4158 for (const uint ObjID: ObjIDs) {
4159 getObjectPointer_private(ObjID)->setColor(color);
4160 }
4161}
4162
4164 return getObjectPointer_private(ObjID)->doesObjectContainPrimitive(UUID);
4165}
4166
4168 getObjectPointer_private(ObjID)->overrideTextureColor();
4169}
4170
4171void Context::overrideObjectTextureColor(const std::vector<uint> &ObjIDs) const {
4172 for (uint ObjID: ObjIDs) {
4173 getObjectPointer_private(ObjID)->overrideTextureColor();
4174 }
4175}
4176
4178 getObjectPointer_private(ObjID)->useTextureColor();
4179}
4180
4181void Context::useObjectTextureColor(const std::vector<uint> &ObjIDs) {
4182 for (uint ObjID: ObjIDs) {
4183 getObjectPointer_private(ObjID)->useTextureColor();
4184 }
4185}
4186
4187void Context::getObjectBoundingBox(uint ObjID, vec3 &min_corner, vec3 &max_corner) const {
4188 const std::vector ObjIDs{ObjID};
4189 getObjectBoundingBox(ObjIDs, min_corner, max_corner);
4190}
4191
4192void Context::getObjectBoundingBox(const std::vector<uint> &ObjIDs, vec3 &min_corner, vec3 &max_corner) const {
4193 uint o = 0;
4194 for (uint ObjID: ObjIDs) {
4195 if (objects.find(ObjID) == objects.end()) {
4196 helios_runtime_error("ERROR (Context::getObjectBoundingBox): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4197 }
4198
4199 const std::vector<uint> &UUIDs = objects.at(ObjID)->getPrimitiveUUIDs();
4200
4201 uint p = 0;
4202 for (const uint UUID: UUIDs) {
4203 const std::vector<vec3> &vertices = getPrimitiveVertices(UUID);
4204
4205 if (p == 0 && o == 0) {
4206 min_corner = vertices.front();
4207 max_corner = min_corner;
4208 p++;
4209 continue;
4210 }
4211
4212 for (const vec3 &vert: vertices) {
4213 if (vert.x < min_corner.x) {
4214 min_corner.x = vert.x;
4215 }
4216 if (vert.y < min_corner.y) {
4217 min_corner.y = vert.y;
4218 }
4219 if (vert.z < min_corner.z) {
4220 min_corner.z = vert.z;
4221 }
4222 if (vert.x > max_corner.x) {
4223 max_corner.x = vert.x;
4224 }
4225 if (vert.y > max_corner.y) {
4226 max_corner.y = vert.y;
4227 }
4228 if (vert.z > max_corner.z) {
4229 max_corner.z = vert.z;
4230 }
4231 }
4232 }
4233
4234 o++;
4235 }
4236}
4237
4238Tile *Context::getTileObjectPointer_private(uint ObjID) const {
4239#ifdef HELIOS_DEBUG
4240 if (objects.find(ObjID) == objects.end()) {
4241 helios_runtime_error("ERROR (Context::getTileObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4242 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_TILE) {
4243 helios_runtime_error("ERROR (Context::getTileObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Tile Object.");
4244 }
4245#endif
4246 return dynamic_cast<Tile *>(objects.at(ObjID));
4247}
4248
4249Sphere *Context::getSphereObjectPointer_private(uint ObjID) const {
4250#ifdef HELIOS_DEBUG
4251 if (objects.find(ObjID) == objects.end()) {
4252 helios_runtime_error("ERROR (Context::getSphereObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4253 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_SPHERE) {
4254 helios_runtime_error("ERROR (Context::getSphereObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Sphere Object.");
4255 }
4256#endif
4257 return dynamic_cast<Sphere *>(objects.at(ObjID));
4258}
4259
4260Tube *Context::getTubeObjectPointer_private(uint ObjID) const {
4261#ifdef HELIOS_DEBUG
4262 if (objects.find(ObjID) == objects.end()) {
4263 helios_runtime_error("ERROR (Context::getTubeObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4264 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_TUBE) {
4265 helios_runtime_error("ERROR (Context::getTubeObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Tube Object.");
4266 }
4267#endif
4268 return dynamic_cast<Tube *>(objects.at(ObjID));
4269}
4270
4271Box *Context::getBoxObjectPointer_private(uint ObjID) const {
4272#ifdef HELIOS_DEBUG
4273 if (objects.find(ObjID) == objects.end()) {
4274 helios_runtime_error("ERROR (Context::getBoxObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4275 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_BOX) {
4276 helios_runtime_error("ERROR (Context::getBoxObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Box Object.");
4277 }
4278#endif
4279 return dynamic_cast<Box *>(objects.at(ObjID));
4280}
4281
4282Disk *Context::getDiskObjectPointer_private(uint ObjID) const {
4283#ifdef HELIOS_DEBUG
4284 if (objects.find(ObjID) == objects.end()) {
4285 helios_runtime_error("ERROR (Context::getDiskObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4286 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_DISK) {
4287 helios_runtime_error("ERROR (Context::getDiskObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Disk Object.");
4288 }
4289#endif
4290 return dynamic_cast<Disk *>(objects.at(ObjID));
4291}
4292
4293Polymesh *Context::getPolymeshObjectPointer_private(uint ObjID) const {
4294#ifdef HELIOS_DEBUG
4295 if (objects.find(ObjID) == objects.end()) {
4296 helios_runtime_error("ERROR (Context::getPolymeshObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4297 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_POLYMESH) {
4298 helios_runtime_error("ERROR (Context::getPolymeshObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Polymesh Object.");
4299 }
4300#endif
4301 return dynamic_cast<Polymesh *>(objects.at(ObjID));
4302}
4303
4304Cone *Context::getConeObjectPointer_private(uint ObjID) const {
4305#ifdef HELIOS_DEBUG
4306 if (objects.find(ObjID) == objects.end()) {
4307 helios_runtime_error("ERROR (Context::getConeObjectPointer): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4308 } else if (objects.at(ObjID)->getObjectType() != OBJECT_TYPE_CONE) {
4309 helios_runtime_error("ERROR (Context::getConeObjectPointer): ObjectID of " + std::to_string(ObjID) + " is not a Cone Object.");
4310 }
4311#endif
4312 return dynamic_cast<Cone *>(objects.at(ObjID));
4313}
4314
4316 return getTileObjectPointer_private(ObjID)->getCenter();
4317}
4318
4320 return getTileObjectPointer_private(ObjID)->getSize();
4321}
4322
4324 return getTileObjectPointer_private(ObjID)->getSubdivisionCount();
4325}
4326
4328 return getTileObjectPointer_private(ObjID)->getNormal();
4329}
4330
4331std::vector<helios::vec2> Context::getTileObjectTextureUV(uint ObjID) const {
4332 return getTileObjectPointer_private(ObjID)->getTextureUV();
4333}
4334
4335std::vector<helios::vec3> Context::getTileObjectVertices(uint ObjID) const {
4336 return getTileObjectPointer_private(ObjID)->getVertices();
4337}
4338
4340 return getSphereObjectPointer_private(ObjID)->getCenter();
4341}
4342
4344 return getSphereObjectPointer_private(ObjID)->getRadius();
4345}
4346
4348 return getSphereObjectPointer_private(ObjID)->getSubdivisionCount();
4349}
4350
4352 return getSphereObjectPointer_private(ObjID)->getVolume();
4353}
4354
4356 return getTubeObjectPointer_private(ObjID)->getSubdivisionCount();
4357}
4358
4359std::vector<helios::vec3> Context::getTubeObjectNodes(uint ObjID) const {
4360 return getTubeObjectPointer_private(ObjID)->getNodes();
4361}
4362
4364 return getTubeObjectPointer_private(ObjID)->getNodeCount();
4365}
4366
4367std::vector<float> Context::getTubeObjectNodeRadii(uint ObjID) const {
4368 return getTubeObjectPointer_private(ObjID)->getNodeRadii();
4369}
4370
4371std::vector<RGBcolor> Context::getTubeObjectNodeColors(uint ObjID) const {
4372 return getTubeObjectPointer_private(ObjID)->getNodeColors();
4373}
4374
4376 return getTubeObjectPointer_private(ObjID)->getVolume();
4377}
4378
4379float Context::getTubeObjectSegmentVolume(uint ObjID, uint segment_index) const {
4380 return getTubeObjectPointer_private(ObjID)->getSegmentVolume(segment_index);
4381}
4382
4383void Context::appendTubeSegment(uint ObjID, const helios::vec3 &node_position, float node_radius, const RGBcolor &node_color) {
4384#ifdef HELIOS_DEBUG
4385 if (objects.find(ObjID) == objects.end()) {
4386 helios_runtime_error("ERROR (Context::appendTubeSegment): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4387 }
4388#endif
4389 dynamic_cast<Tube *>(objects.at(ObjID))->appendTubeSegment(node_position, node_radius, node_color);
4390}
4391
4392void Context::appendTubeSegment(uint ObjID, const helios::vec3 &node_position, float node_radius, const char *texturefile, const helios::vec2 &textureuv_ufrac) {
4393#ifdef HELIOS_DEBUG
4394 if (objects.find(ObjID) == objects.end()) {
4395 helios_runtime_error("ERROR (Context::appendTubeSegment): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4396 }
4397#endif
4398 dynamic_cast<Tube *>(objects.at(ObjID))->appendTubeSegment(node_position, node_radius, texturefile, textureuv_ufrac);
4399}
4400
4401void Context::scaleTubeGirth(uint ObjID, float scale_factor) {
4402#ifdef HELIOS_DEBUG
4403 if (objects.find(ObjID) == objects.end()) {
4404 helios_runtime_error("ERROR (Context::scaleTubeGirth): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4405 }
4406#endif
4407 dynamic_cast<Tube *>(objects.at(ObjID))->scaleTubeGirth(scale_factor);
4408}
4409
4410void Context::setTubeRadii(uint ObjID, const std::vector<float> &node_radii) {
4411#ifdef HELIOS_DEBUG
4412 if (objects.find(ObjID) == objects.end()) {
4413 helios_runtime_error("ERROR (Context::setTubeRadii): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4414 }
4415#endif
4416 dynamic_cast<Tube *>(objects.at(ObjID))->setTubeRadii(node_radii);
4417}
4418
4419void Context::scaleTubeLength(uint ObjID, float scale_factor) {
4420#ifdef HELIOS_DEBUG
4421 if (objects.find(ObjID) == objects.end()) {
4422 helios_runtime_error("ERROR (Context::scaleTubeLength): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4423 }
4424#endif
4425 dynamic_cast<Tube *>(objects.at(ObjID))->scaleTubeLength(scale_factor);
4426}
4427
4428void Context::pruneTubeNodes(uint ObjID, uint node_index) {
4429#ifdef HELIOS_DEBUG
4430 if (objects.find(ObjID) == objects.end()) {
4431 helios_runtime_error("ERROR (Context::pruneTubeNodes): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4432 }
4433#endif
4434 dynamic_cast<Tube *>(objects.at(ObjID))->pruneTubeNodes(node_index);
4435}
4436
4437void Context::setTubeNodes(uint ObjID, const std::vector<helios::vec3> &node_xyz) {
4438#ifdef HELIOS_DEBUG
4439 if (objects.find(ObjID) == objects.end()) {
4440 helios_runtime_error("ERROR (Context::setTubeNodes): ObjectID of " + std::to_string(ObjID) + " does not exist in the Context.");
4441 }
4442#endif
4443 dynamic_cast<Tube *>(objects.at(ObjID))->setTubeNodes(node_xyz);
4444}
4445
4447 return getBoxObjectPointer_private(ObjID)->getCenter();
4448}
4449
4451 return getBoxObjectPointer_private(ObjID)->getSize();
4452}
4453
4455 return getBoxObjectPointer_private(ObjID)->getSubdivisionCount();
4456}
4457
4459 return getBoxObjectPointer_private(ObjID)->getVolume();
4460}
4461
4463 return getDiskObjectPointer_private(ObjID)->getCenter();
4464}
4465
4467 return getDiskObjectPointer_private(ObjID)->getSize();
4468}
4469
4471 return getDiskObjectPointer_private(ObjID)->getSubdivisionCount().x;
4472}
4473
4475 return getConeObjectPointer_private(ObjID)->getSubdivisionCount();
4476}
4477
4478std::vector<helios::vec3> Context::getConeObjectNodes(uint ObjID) const {
4479 return getConeObjectPointer_private(ObjID)->getNodeCoordinates();
4480}
4481
4482std::vector<float> Context::getConeObjectNodeRadii(uint ObjID) const {
4483 return getConeObjectPointer_private(ObjID)->getNodeRadii();
4484}
4485
4487 return getConeObjectPointer_private(ObjID)->getNodeCoordinate(number);
4488}
4489
4490float Context::getConeObjectNodeRadius(uint ObjID, int number) const {
4491 return getConeObjectPointer_private(ObjID)->getNodeRadius(number);
4492}
4493
4495 return getConeObjectPointer_private(ObjID)->getAxisUnitVector();
4496}
4497
4499 return getConeObjectPointer_private(ObjID)->getLength();
4500}
4501
4503 return getConeObjectPointer_private(ObjID)->getVolume();
4504}
4505
4507 return getPolymeshObjectPointer_private(ObjID)->getVolume();
4508}