8TEST_CASE(
"Core Context State and Configuration") {
9 SUBCASE(
"Constructor and basic setup") {
11 DOCTEST_CHECK(ctx.getPrimitiveCount() == 0);
12 DOCTEST_CHECK(ctx.getObjectCount() == 0);
13 DOCTEST_CHECK(!ctx.isGeometryDirty());
15 Date d = ctx.getDate();
16 DOCTEST_CHECK(d.day == 1);
17 DOCTEST_CHECK(d.month == 6);
18 DOCTEST_CHECK(d.year == 2000);
20 Time t = ctx.getTime();
21 DOCTEST_CHECK(t.hour == 12);
22 DOCTEST_CHECK(t.minute == 0);
23 DOCTEST_CHECK(t.second == 0);
25 Location l = ctx.getLocation();
26 DOCTEST_CHECK(l.latitude_deg == doctest::Approx(38.55));
27 DOCTEST_CHECK(l.longitude_deg == doctest::Approx(121.76));
28 DOCTEST_CHECK(l.UTC_offset == doctest::Approx(8));
31 SUBCASE(
"Random number generator") {
33 ctx.seedRandomGenerator(12345);
34 std::minstd_rand0 *gen1 = ctx.getRandomGenerator();
35 float rand1 = (*gen1)();
37 ctx.seedRandomGenerator(12345);
38 std::minstd_rand0 *gen2 = ctx.getRandomGenerator();
39 float rand2 = (*gen2)();
41 DOCTEST_CHECK(rand1 == rand2);
43 float r_uniform = ctx.randu();
44 DOCTEST_CHECK(r_uniform >= 0.f);
45 DOCTEST_CHECK(r_uniform <= 1.f);
47 float r_norm = ctx.randn();
49 DOCTEST_CHECK(!std::isnan(r_norm));
52 SUBCASE(
"Random number ranges") {
54 ctx.seedRandomGenerator(6789);
55 float r = ctx.randu(-1.f, 1.f);
56 DOCTEST_CHECK(r >= -1.f);
57 DOCTEST_CHECK(r <= 1.f);
58 int ri = ctx.randu(0, 5);
59 DOCTEST_CHECK(ri >= 0);
60 DOCTEST_CHECK(ri <= 5);
61 float rn = ctx.randn(2.f, 0.5f);
62 DOCTEST_CHECK(!std::isnan(rn));
65 SUBCASE(
"Texture utility methods") {
68 capture_cerr cerr_buffer;
69 DOCTEST_CHECK_NOTHROW(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/solid.jpg"));
70 DOCTEST_CHECK_THROWS(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/missing.png"));
71 DOCTEST_CHECK_THROWS(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/invalid.txt"));
74 Texture tex(
"lib/images/solid.jpg");
75 DOCTEST_CHECK(tex.getTextureFile() ==
"lib/images/solid.jpg");
76 int2 res = tex.getImageResolution();
77 DOCTEST_CHECK(res.x == 5);
78 DOCTEST_CHECK(res.y == 5);
79 DOCTEST_CHECK(!tex.hasTransparencyChannel());
80 const auto *alpha = tex.getTransparencyData();
81 DOCTEST_CHECK(alpha->empty());
82 std::vector<vec2> uv{{0.f, 0.f}, {1.f, 0.f}, {1.f, 1.f}};
83 float sf = tex.getSolidFraction(uv);
84 DOCTEST_CHECK(sf == doctest::Approx(1.f));
87 SUBCASE(
"Geometry dirty flags") {
89 uint p = ctx.addPatch();
90 DOCTEST_CHECK(ctx.isGeometryDirty());
91 DOCTEST_CHECK(ctx.isPrimitiveDirty(p));
93 ctx.markGeometryClean();
94 DOCTEST_CHECK(!ctx.isGeometryDirty());
95 DOCTEST_CHECK(!ctx.isPrimitiveDirty(p));
97 ctx.markPrimitiveDirty(p);
98 DOCTEST_CHECK(ctx.isGeometryDirty());
99 DOCTEST_CHECK(ctx.isPrimitiveDirty(p));
101 ctx.markPrimitiveClean(p);
102 DOCTEST_CHECK(!ctx.isGeometryDirty());
103 DOCTEST_CHECK(!ctx.isPrimitiveDirty(p));
105 ctx.markGeometryDirty();
106 DOCTEST_CHECK(ctx.isGeometryDirty());
109 SUBCASE(
"Geometry dirty flags vector") {
111 std::vector<uint> ids{ctx.addPatch(), ctx.addPatch()};
112 ctx.markGeometryClean();
113 ctx.markPrimitiveDirty(ids);
115 DOCTEST_CHECK(ctx.isPrimitiveDirty(
id));
117 ctx.markPrimitiveClean(ids);
119 DOCTEST_CHECK(!ctx.isPrimitiveDirty(
id));
123 ctx.translatePrimitive(ids, shift);
125 vec3 c = ctx.getPatchCenter(
id);
126 DOCTEST_CHECK(c.x == doctest::Approx(shift.x).epsilon(errtol));
130 SUBCASE(
"Date and Time Manipulation") {
132 ctx.setDate(15, 7, 2025);
133 Date d = ctx.getDate();
134 DOCTEST_CHECK(d.day == 15);
135 DOCTEST_CHECK(d.month == 7);
136 DOCTEST_CHECK(d.year == 2025);
137 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"JUL") == 0);
138 DOCTEST_CHECK(ctx.getJulianDate() == 196);
140 ctx.setTime(45, 30, 10);
141 Time t = ctx.getTime();
142 DOCTEST_CHECK(t.hour == 10);
143 DOCTEST_CHECK(t.minute == 30);
144 DOCTEST_CHECK(t.second == 45);
146 capture_cerr cerr_buffer;
147 DOCTEST_CHECK_THROWS(ctx.setDate(32, 1, 2025));
148 DOCTEST_CHECK_THROWS(ctx.setTime(60, 0, 0));
151 SUBCASE(
"Location Manipulation") {
153 Location loc = {40.7128, -74.0060, 10.0};
154 ctx.setLocation(loc);
155 Location l = ctx.getLocation();
156 DOCTEST_CHECK(l.latitude_deg == doctest::Approx(40.7128));
157 DOCTEST_CHECK(l.longitude_deg == doctest::Approx(-74.0060));
158 DOCTEST_CHECK(l.UTC_offset == doctest::Approx(10.0));
161 SUBCASE(
"primitive orientation and transforms") {
164 ctx.markGeometryClean();
166 vec3 n = ctx.getPrimitiveNormal(
id);
167 DOCTEST_CHECK(n == vec3(0.f, 0.f, 1.f));
169 ctx.setPrimitiveElevation(
id,
make_vec3(0, 0, 0), 0.f);
170 n = ctx.getPrimitiveNormal(
id);
171 DOCTEST_CHECK(n.x == doctest::Approx(0.f).epsilon(errtol));
172 DOCTEST_CHECK(n.y == doctest::Approx(1.f).epsilon(errtol));
173 DOCTEST_CHECK(n.z == doctest::Approx(0.f).epsilon(errtol));
175 ctx.setPrimitiveAzimuth(
id,
make_vec3(0, 0, 0), 0.5f *
PI_F);
176 n = ctx.getPrimitiveNormal(
id);
177 DOCTEST_CHECK(n.x == doctest::Approx(1.f).epsilon(errtol));
178 DOCTEST_CHECK(n.y == doctest::Approx(0.f).epsilon(errtol));
181 n = ctx.getPrimitiveNormal(
id);
182 DOCTEST_CHECK(n.z == doctest::Approx(1.f).epsilon(errtol));
186 ctx.setPrimitiveTransformationMatrix(
id, M);
188 ctx.getPrimitiveTransformationMatrix(
id, out);
189 for (
int i = 0; i < 16; ++i) {
190 DOCTEST_CHECK(out[i] == doctest::Approx(M[i]));
192 DOCTEST_CHECK(ctx.isPrimitiveDirty(
id));
196TEST_CASE(
"Primitive Management: Creation, Properties, and Operations") {
197 SUBCASE(
"addPatch") {
198 vec3 center, center_r;
200 std::vector<vec3> vertices, vertices_r;
201 SphericalCoord rotation, rotation_r;
202 vec3 normal, normal_r;
203 RGBcolor color, color_r;
205 std::vector<uint> UUIDs;
210 Context context_test;
216 vertices.at(0) = center +
make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
217 vertices.at(1) = center +
make_vec3(0.5f * size.x, -0.5f * size.y, 0.f);
218 vertices.at(2) = center +
make_vec3(0.5f * size.x, 0.5f * size.y, 0.f);
219 vertices.at(3) = center +
make_vec3(-0.5f * size.x, 0.5f * size.y, 0.f);
221 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
222 DOCTEST_CHECK_NOTHROW(type = context_test.getPrimitiveType(UUID));
223 DOCTEST_CHECK_NOTHROW(center_r = context_test.getPatchCenter(UUID));
224 DOCTEST_CHECK_NOTHROW(size_r = context_test.getPatchSize(UUID));
225 DOCTEST_CHECK_NOTHROW(normal_r = context_test.getPrimitiveNormal(UUID));
226 DOCTEST_CHECK_NOTHROW(vertices_r = context_test.getPrimitiveVertices(UUID));
227 DOCTEST_CHECK_NOTHROW(area_r = context_test.getPrimitiveArea(UUID));
228 DOCTEST_CHECK_NOTHROW(color_r = context_test.getPrimitiveColor(UUID));
230 DOCTEST_CHECK(type == PRIMITIVE_TYPE_PATCH);
231 DOCTEST_CHECK(center_r.x == center.x);
232 DOCTEST_CHECK(center_r.y == center.y);
233 DOCTEST_CHECK(center_r.z == center.z);
234 DOCTEST_CHECK(size_r.x == size.x);
235 DOCTEST_CHECK(size_r.y == size.y);
236 DOCTEST_CHECK(normal_r.x == 0.f);
237 DOCTEST_CHECK(normal_r.y == 0.f);
238 DOCTEST_CHECK(normal_r.z == 1.f);
239 DOCTEST_CHECK(vertices_r.size() == 4);
240 DOCTEST_CHECK(vertices_r.at(0).x == vertices.at(0).x);
241 DOCTEST_CHECK(vertices_r.at(0).y == vertices.at(0).y);
242 DOCTEST_CHECK(vertices_r.at(0).z == vertices.at(0).z);
243 DOCTEST_CHECK(vertices_r.at(1).x == vertices.at(1).x);
244 DOCTEST_CHECK(vertices_r.at(1).y == vertices.at(1).y);
245 DOCTEST_CHECK(vertices_r.at(1).z == vertices.at(1).z);
246 DOCTEST_CHECK(vertices_r.at(2).x == vertices.at(2).x);
247 DOCTEST_CHECK(vertices_r.at(2).y == vertices.at(2).y);
248 DOCTEST_CHECK(vertices_r.at(2).z == vertices.at(2).z);
249 DOCTEST_CHECK(vertices_r.at(3).x == vertices.at(3).x);
250 DOCTEST_CHECK(vertices_r.at(3).y == vertices.at(3).y);
251 DOCTEST_CHECK(vertices_r.at(3).z == vertices.at(3).z);
252 CHECK(area_r == doctest::Approx(size.x * size.y).epsilon(errtol));
253 DOCTEST_CHECK(color_r.r == 0.f);
254 DOCTEST_CHECK(color_r.g == 0.f);
255 DOCTEST_CHECK(color_r.b == 0.f);
256 DOCTEST_CHECK(context_test.getPrimitiveTextureFile(UUID).empty());
258 SUBCASE(
"rotated patch") {
259 Context context_test;
264 rotation.azimuth = 0.5f *
PI_F;
267 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size, rotation));
270 DOCTEST_CHECK_NOTHROW(normal_r = context_test.getPrimitiveNormal(UUID));
272 SphericalCoord rotation_r;
273 DOCTEST_CHECK_NOTHROW(rotation_r =
make_SphericalCoord(0.5f *
PI_F - asinf(normal_r.z), atan2f(normal_r.x, normal_r.y)));
275 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
277 DOCTEST_CHECK(rotation_r.elevation == doctest::Approx(rotation.elevation).epsilon(errtol));
278 DOCTEST_CHECK(rotation_r.azimuth == doctest::Approx(rotation.azimuth).epsilon(errtol));
280 SUBCASE(
"addTriangle") {
281 Context context_test;
292 std::vector<vec3> vertices{v0, v1, v2};
293 RGBcolor color = RGB::red;
295 DOCTEST_CHECK_NOTHROW(UUID = context_test.addTriangle(v0, v1, v2, color));
296 DOCTEST_CHECK(context_test.getPrimitiveType(UUID) == PRIMITIVE_TYPE_TRIANGLE);
298 vec3 normal = normalize(
cross(v1 - v0, v2 - v1));
299 vec3 normal_r = context_test.getPrimitiveNormal(UUID);
300 DOCTEST_CHECK(normal_r.x == doctest::Approx(normal.x).epsilon(errtol));
301 DOCTEST_CHECK(normal_r.y == doctest::Approx(normal.y).epsilon(errtol));
302 DOCTEST_CHECK(normal_r.z == doctest::Approx(normal.z).epsilon(errtol));
304 std::vector<vec3> vertices_r;
305 DOCTEST_CHECK_NOTHROW(vertices_r = context_test.getPrimitiveVertices(UUID));
306 DOCTEST_CHECK(vertices_r.size() == 3);
307 DOCTEST_CHECK(vertices_r.at(0).x == v0.x);
308 DOCTEST_CHECK(vertices_r.at(0).y == v0.y);
309 DOCTEST_CHECK(vertices_r.at(0).z == v0.z);
312 DOCTEST_CHECK_NOTHROW(color_r = context_test.getPrimitiveColor(UUID));
313 DOCTEST_CHECK(color_r.r == color.r);
314 DOCTEST_CHECK(color_r.g == color.g);
315 DOCTEST_CHECK(color_r.b == color.b);
316 DOCTEST_CHECK(context_test.getPrimitiveTextureFile(UUID).empty());
318 float a = (v1 - v0).magnitude();
319 float b = (v2 - v0).magnitude();
320 float c = (v2 - v1).magnitude();
321 float s = 0.5f * (a + b + c);
322 float area = sqrtf(s * (s - a) * (s - b) * (s - c));
324 DOCTEST_CHECK_NOTHROW(area_r = context_test.getPrimitiveArea(UUID));
325 DOCTEST_CHECK(area_r == doctest::Approx(area).epsilon(errtol));
327 SUBCASE(
"copyPrimitive (patch)") {
328 Context context_test;
331 std::vector<float> cpdata{5.2f, 2.5f, 3.1f};
336 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
338 DOCTEST_CHECK_NOTHROW(context_test.setPrimitiveData(UUID,
"somedata", cpdata));
340 DOCTEST_CHECK_NOTHROW(UUID_cpy = context_test.copyPrimitive(UUID));
343 DOCTEST_CHECK_NOTHROW(center_cpy = context_test.getPatchCenter(UUID_cpy));
345 DOCTEST_CHECK_NOTHROW(size_cpy = context_test.getPatchSize(UUID_cpy));
347 DOCTEST_CHECK(UUID_cpy == 1);
348 DOCTEST_CHECK(center_cpy.x == center.x);
349 DOCTEST_CHECK(center_cpy.y == center.y);
350 DOCTEST_CHECK(center_cpy.z == center.z);
351 DOCTEST_CHECK(size_cpy.x == size.x);
352 DOCTEST_CHECK(size_cpy.y == size.y);
354 std::vector<float> cpdata_copy;
355 context_test.getPrimitiveData(UUID_cpy,
"somedata", cpdata_copy);
357 DOCTEST_CHECK(cpdata.size() == cpdata_copy.size());
358 for (
uint i = 0; i < cpdata.size(); i++) {
359 DOCTEST_CHECK(cpdata.at(i) == cpdata_copy.at(i));
364 DOCTEST_CHECK_NOTHROW(context_test.translatePrimitive(UUID_cpy, shift));
365 DOCTEST_CHECK_NOTHROW(center_cpy = context_test.getPatchCenter(UUID_cpy));
367 DOCTEST_CHECK_NOTHROW(center_r = context_test.getPatchCenter(UUID));
369 DOCTEST_CHECK(center_cpy.x == doctest::Approx(center.x + shift.x).epsilon(errtol));
370 DOCTEST_CHECK(center_cpy.y == doctest::Approx(center.y + shift.y).epsilon(errtol));
371 DOCTEST_CHECK(center_cpy.z == doctest::Approx(center.z + shift.z).epsilon(errtol));
372 DOCTEST_CHECK(center_r.x == center.x);
373 DOCTEST_CHECK(center_r.y == center.y);
374 DOCTEST_CHECK(center_r.z == center.z);
376 SUBCASE(
"copyPrimitive (triangle)") {
377 Context context_test;
384 DOCTEST_CHECK_NOTHROW(UUID = context_test.addTriangle(v0, v1, v2, RGB::blue));
385 DOCTEST_CHECK_NOTHROW(UUID_cpy = context_test.copyPrimitive(UUID));
387 std::vector<vec3> verts_org, verts_cpy;
388 DOCTEST_CHECK_NOTHROW(verts_org = context_test.getPrimitiveVertices(UUID));
389 DOCTEST_CHECK_NOTHROW(verts_cpy = context_test.getPrimitiveVertices(UUID_cpy));
390 DOCTEST_CHECK(verts_org == verts_cpy);
393 DOCTEST_CHECK_NOTHROW(context_test.translatePrimitive(UUID_cpy, shift));
394 DOCTEST_CHECK_NOTHROW(verts_cpy = context_test.getPrimitiveVertices(UUID_cpy));
395 DOCTEST_CHECK(verts_cpy.at(0) == verts_org.at(0) + shift);
396 DOCTEST_CHECK(verts_cpy.at(1) == verts_org.at(1) + shift);
397 DOCTEST_CHECK(verts_cpy.at(2) == verts_org.at(2) + shift);
399 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
400 DOCTEST_CHECK(!context_test.doesPrimitiveExist(UUID));
402 SUBCASE(
"deletePrimitive") {
403 Context context_test;
408 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
410 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
412 uint primitive_count;
413 DOCTEST_CHECK_NOTHROW(primitive_count = context_test.getPrimitiveCount(UUID));
414 DOCTEST_CHECK(primitive_count == 0);
415 DOCTEST_CHECK(!context_test.doesPrimitiveExist(UUID));
417 SUBCASE(
"primitive bounding box") {
418 Context context_test;
419 std::vector<uint> UUIDs;
424 DOCTEST_CHECK_NOTHROW(context_test.getPrimitiveBoundingBox(UUIDs, bmin, bmax));
425 DOCTEST_CHECK(bmin ==
make_vec3(-1.25f, -0.25f, 0.f));
426 DOCTEST_CHECK(bmax ==
make_vec3(1.25f, 0.25f, 0.f));
428 SUBCASE(
"primitive scale and data") {
429 Context context_test;
431 float area0 = sz_0.x * sz_0.y;
434 context_test.scalePrimitive(UUID,
make_vec3(scale, scale, scale));
435 float area1 = context_test.getPrimitiveArea(UUID);
436 DOCTEST_CHECK(area1 == doctest::Approx(scale * scale * area0).epsilon(1e-5));
439 context_test.setPrimitiveData(UUID,
"some_data", data);
440 DOCTEST_CHECK(context_test.doesPrimitiveDataExist(UUID,
"some_data"));
442 context_test.getPrimitiveData(UUID,
"some_data", data_r);
443 DOCTEST_CHECK(data_r == data);
445 std::vector<float> vec = {0, 1, 2, 3, 4};
446 context_test.setPrimitiveData(UUID,
"vec_data", vec);
447 std::vector<float> vec_r;
448 context_test.getPrimitiveData(UUID,
"vec_data", vec_r);
449 DOCTEST_CHECK(vec_r == vec);
451 std::vector<uint> UUIDs_filter;
452 std::vector<uint> UUIDs_multi;
453 for (
uint i = 0; i < 4; i++) {
454 UUIDs_multi.push_back(context_test.addPatch());
456 context_test.setPrimitiveData(UUIDs_multi[0],
"val", 4.f);
457 context_test.setPrimitiveData(UUIDs_multi[0],
"str",
"cat");
458 context_test.setPrimitiveData(UUIDs_multi[1],
"val", 3.f);
459 context_test.setPrimitiveData(UUIDs_multi[1],
"str",
"cat");
460 context_test.setPrimitiveData(UUIDs_multi[2],
"val", 2.f);
461 context_test.setPrimitiveData(UUIDs_multi[2],
"str",
"dog");
462 context_test.setPrimitiveData(UUIDs_multi[3],
"val", 1.f);
463 context_test.setPrimitiveData(UUIDs_multi[3],
"str",
"dog");
465 UUIDs_filter = context_test.filterPrimitivesByData(UUIDs_multi,
"val", 2.f,
"<=");
466 DOCTEST_CHECK(UUIDs_filter.size() == 2);
467 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[2]) != UUIDs_filter.end());
468 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[3]) != UUIDs_filter.end());
470 UUIDs_filter = context_test.filterPrimitivesByData(UUIDs_multi,
"str",
"cat");
471 DOCTEST_CHECK(UUIDs_filter.size() == 2);
472 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[0]) != UUIDs_filter.end());
473 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[1]) != UUIDs_filter.end());
475 SUBCASE(
"texture uv and solid fraction") {
476 Context context_test;
479 const char *texture =
"lib/images/disk_texture.png";
484 uint UUIDp = context_test.addPatch(
make_vec3(2, 3, 4), sizep, nullrotation, texture, 0.5f * (uv0 + uv2), uv2 - uv0);
485 DOCTEST_CHECK(!context_test.getPrimitiveTextureFile(UUIDp).empty());
486 float Ap = context_test.getPrimitiveArea(UUIDp);
487 DOCTEST_CHECK(Ap == doctest::Approx(0.25f *
PI_F * sizep.x * sizep.y).epsilon(0.01));
488 std::vector<vec2> uv = context_test.getPrimitiveTextureUV(UUIDp);
489 DOCTEST_CHECK(uv.size() == 4);
490 DOCTEST_CHECK(uv.at(0) == uv0);
491 DOCTEST_CHECK(uv.at(1) == uv1);
492 DOCTEST_CHECK(uv.at(2) == uv2);
493 DOCTEST_CHECK(uv.at(3) == uv3);
495 uint UUIDt = context_test.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(1, 1, 0),
"lib/images/diamond_texture.png",
make_vec2(0, 0),
make_vec2(1, 0),
make_vec2(1, 1));
496 float solid_fraction = context_test.getPrimitiveSolidFraction(UUIDt);
497 DOCTEST_CHECK(solid_fraction == doctest::Approx(0.5f).epsilon(errtol));
500 SUBCASE(
"advanced primitive transforms") {
504 std::vector<uint> ids{p1, p2};
505 ctx.markGeometryClean();
507 ctx.rotatePrimitive(p1, 0.5f *
PI_F,
"x");
508 vec3 n = ctx.getPrimitiveNormal(p1);
509 DOCTEST_CHECK(n.y == doctest::Approx(-1.f).epsilon(errtol));
510 DOCTEST_CHECK(n.z == doctest::Approx(0.f).epsilon(errtol));
513 vec3 c = ctx.getPatchCenter(p2);
514 DOCTEST_CHECK(c.x == doctest::Approx(-1.f).epsilon(errtol));
519 vec2 sz = ctx.getPatchSize(p2);
520 DOCTEST_CHECK(sz.x == doctest::Approx(2.f).epsilon(errtol));
523 sz = ctx.getPatchSize(p2);
524 DOCTEST_CHECK(sz.x == doctest::Approx(1.f).epsilon(errtol));
528TEST_CASE(
"Triangle Scaling") {
530 const float errtol = 0.0001f;
532 SUBCASE(
"scalePrimitive basic test") {
537 uint tri = ctx.addTriangle(v0, v1, v2);
540 std::vector<vec3> verts_before = ctx.getPrimitiveVertices(tri);
541 float area_before = ctx.getPrimitiveArea(tri);
544 ctx.scalePrimitive(tri,
make_vec3(2, 2, 2));
547 std::vector<vec3> verts_after = ctx.getPrimitiveVertices(tri);
548 float area_after = ctx.getPrimitiveArea(tri);
556 DOCTEST_CHECK(verts_after[0].x == doctest::Approx(0.0f).epsilon(errtol));
557 DOCTEST_CHECK(verts_after[0].y == doctest::Approx(0.0f).epsilon(errtol));
558 DOCTEST_CHECK(verts_after[1].x == doctest::Approx(2.0f).epsilon(errtol));
559 DOCTEST_CHECK(verts_after[1].y == doctest::Approx(0.0f).epsilon(errtol));
560 DOCTEST_CHECK(verts_after[2].x == doctest::Approx(0.0f).epsilon(errtol));
561 DOCTEST_CHECK(verts_after[2].y == doctest::Approx(2.0f).epsilon(errtol));
563 DOCTEST_CHECK(area_after == doctest::Approx(4.0f * area_before).epsilon(errtol));
566 SUBCASE(
"scalePrimitiveAboutPoint test") {
571 uint tri = ctx.addTriangle(v0, v1, v2);
574 float area_before = ctx.getPrimitiveArea(tri);
580 float area_after = ctx.getPrimitiveArea(tri);
583 DOCTEST_CHECK(area_after == doctest::Approx(4.0f * area_before).epsilon(errtol));
586 SUBCASE(
"scalePrimitiveAboutPoint - scale about centroid") {
591 uint tri = ctx.addTriangle(v0, v1, v2);
594 std::vector<vec3> verts_before = ctx.getPrimitiveVertices(tri);
596 for (
const auto &v: verts_before) {
599 center = center / float(verts_before.size());
601 float area_before = ctx.getPrimitiveArea(tri);
604 ctx.scalePrimitiveAboutPoint(tri,
make_vec3(0.5f, 0.5f, 0.5f), center);
607 float area_after = ctx.getPrimitiveArea(tri);
610 DOCTEST_CHECK(area_after == doctest::Approx(0.25f * area_before).epsilon(errtol));
613 SUBCASE(
"triangle in compound object") {
615 std::vector<uint> UUIDs;
617 uint objID = ctx.addPolymeshObject(UUIDs);
620 float area_before = ctx.getPrimitiveArea(tri);
625 capture_cerr cerr_buffer;
627 has_warning = cerr_buffer.has_output();
629 DOCTEST_CHECK(has_warning);
631 float area_after = ctx.getPrimitiveArea(tri);
634 DOCTEST_CHECK(area_after == doctest::Approx(area_before).epsilon(errtol));
638TEST_CASE(
"Object Management") {
639 SUBCASE(
"addBoxObject") {
640 Context context_test;
644 int3 subdiv(1, 1, 1);
647 DOCTEST_CHECK_NOTHROW(objID = context_test.addBoxObject(center, size, subdiv));
648 std::vector<uint> UUIDs = context_test.getObjectPrimitiveUUIDs(objID);
650 DOCTEST_CHECK(UUIDs.size() == 6);
651 vec3 normal_r = context_test.getPrimitiveNormal(UUIDs.at(0));
652 DOCTEST_CHECK(doctest::Approx(normal_r.magnitude()).epsilon(errtol) == 1.f);
653 normal_r = context_test.getPrimitiveNormal(UUIDs.at(2));
654 DOCTEST_CHECK(doctest::Approx(normal_r.magnitude()).epsilon(errtol) == 1.f);
656 vec2 size_r = context_test.getPatchSize(UUIDs.at(0));
657 DOCTEST_CHECK(size_r.x == doctest::Approx(size.x).epsilon(errtol));
658 DOCTEST_CHECK(size_r.y == doctest::Approx(size.z).epsilon(errtol));
660 size_r = context_test.getPatchSize(UUIDs.at(2));
661 DOCTEST_CHECK(size_r.x == doctest::Approx(size.y).epsilon(errtol));
662 DOCTEST_CHECK(size_r.y == doctest::Approx(size.z).epsilon(errtol));
664 float volume = context_test.getBoxObjectVolume(objID);
665 DOCTEST_CHECK(volume == doctest::Approx(size.x * size.y * size.z).epsilon(errtol));
667 SUBCASE(
"addTileObject rotated") {
668 Context context_test;
674 uint objID = context_test.addTileObject(center, size, rotation, subdiv);
676 std::vector<uint> UUIDs = context_test.getObjectPrimitiveUUIDs(objID);
677 for (
uint UUIDp: UUIDs) {
678 vec3 n = context_test.getPrimitiveNormal(UUIDp);
680 DOCTEST_CHECK(rot.zenith == doctest::Approx(rotation.zenith).epsilon(errtol));
681 DOCTEST_CHECK(rot.azimuth == doctest::Approx(rotation.azimuth).epsilon(errtol));
684 SUBCASE(
"textured tile area") {
685 Context context_test;
692 uint objID = context_test.addTileObject(center, size, rotation, subdiv,
"lib/images/disk_texture.png");
693 std::vector<uint> UUIDs = context_test.getObjectPrimitiveUUIDs(objID);
694 float area_sum = 0.f;
695 for (
uint UUID: UUIDs) {
696 area_sum += context_test.getPrimitiveArea(UUID);
698 float area_exact = 0.25f *
PI_F * size.x * size.y;
699 DOCTEST_CHECK(area_sum == doctest::Approx(area_exact).epsilon(5e-3));
701 SUBCASE(
"cone object transforms") {
702 Context context_test;
703 float r0 = 0.5f, r1 = 1.f, len = 2.f;
706 uint cone = context_test.addConeObject(50, node0, node1, r0, r1);
707 context_test.translateObject(cone,
make_vec3(1, 1, 1));
708 std::vector<vec3> nodes = context_test.getConeObjectNodes(cone);
709 DOCTEST_CHECK(nodes.at(0) ==
make_vec3(1, 1, 1));
710 DOCTEST_CHECK(nodes.at(1) ==
make_vec3(1, 1, 1 + len));
713 context_test.translateObject(cone, -nodes.at(0));
714 context_test.rotateObject(cone, ang, axis);
715 context_test.translateObject(cone, nodes.at(0));
716 nodes = context_test.getConeObjectNodes(cone);
717 DOCTEST_CHECK(nodes.at(1).x == doctest::Approx(nodes.at(0).x + len).epsilon(errtol));
718 context_test.scaleConeObjectLength(cone, 2.0);
719 nodes = context_test.getConeObjectNodes(cone);
720 DOCTEST_CHECK(nodes.at(1).x == doctest::Approx(nodes.at(0).x + 2 * len).epsilon(errtol));
721 context_test.scaleConeObjectGirth(cone, 2.0);
722 std::vector<float> radii = context_test.getConeObjectNodeRadii(cone);
723 DOCTEST_CHECK(radii.at(0) == doctest::Approx(2 * r0).epsilon(errtol));
724 DOCTEST_CHECK(radii.at(1) == doctest::Approx(2 * r1).epsilon(errtol));
727 SUBCASE(
"rotate and scale objects") {
730 ctx.rotateObject(
obj, 0.5f *
PI_F,
"z");
732 ctx.getObjectBoundingBox(
obj, bmin, bmax);
733 DOCTEST_CHECK(bmax.x == doctest::Approx(0.5f).epsilon(errtol));
736 ctx.getObjectBoundingBox(
obj, bmin, bmax);
737 DOCTEST_CHECK(bmax.x > 0.5f);
740 SUBCASE(
"domain bounding sphere") {
742 std::vector<uint> ids;
747 ctx.getDomainBoundingSphere(ids, c, r);
748 DOCTEST_CHECK(c.x == doctest::Approx(0.f).epsilon(errtol));
749 DOCTEST_CHECK(r > 1.f);
752 SUBCASE(
"copy and delete objects") {
755 uint obj2 = ctx.copyObject(obj1);
756 DOCTEST_CHECK(ctx.doesObjectExist(obj1));
757 DOCTEST_CHECK(ctx.doesObjectExist(obj2));
758 ctx.deleteObject(obj2);
759 DOCTEST_CHECK(!ctx.doesObjectExist(obj2));
762 SUBCASE(
"copy object with texture override preserves color") {
763 capture_cerr cerr_buffer;
767 std::vector<uint> UUIDs = ctx.addTile(nullorigin,
make_vec2(1, 1), nullrotation,
make_int2(2, 2),
"lib/images/disk_texture.png");
771 ctx.setPrimitiveColor(UUIDs, green_color);
772 ctx.overridePrimitiveTextureColor(UUIDs);
775 uint objID = ctx.addPolymeshObject(UUIDs);
778 DOCTEST_CHECK(ctx.getPrimitiveColor(UUIDs[0]) == green_color);
779 DOCTEST_CHECK(ctx.isPrimitiveTextureColorOverridden(UUIDs[0]));
782 uint objID_copy = ctx.copyObject(objID);
783 std::vector<uint> UUIDs_copy = ctx.getObjectPrimitiveUUIDs(objID_copy);
786 DOCTEST_CHECK(ctx.getPrimitiveColor(UUIDs_copy[0]) == green_color);
787 DOCTEST_CHECK(ctx.isPrimitiveTextureColorOverridden(UUIDs_copy[0]));
790 uint triangle = ctx.addTriangle(
make_vec3(0, 0, 0),
make_vec3(1, 0, 0),
make_vec3(0, 1, 0),
"lib/images/disk_texture.png",
make_vec2(0, 0),
make_vec2(1, 0),
make_vec2(0, 1));
792 ctx.setPrimitiveColor(triangle, blue_color);
793 ctx.overridePrimitiveTextureColor(triangle);
795 std::vector<uint> triangle_UUIDs = {triangle};
796 uint triangle_obj = ctx.addPolymeshObject(triangle_UUIDs);
797 uint triangle_obj_copy = ctx.copyObject(triangle_obj);
798 std::vector<uint> triangle_UUIDs_copy = ctx.getObjectPrimitiveUUIDs(triangle_obj_copy);
800 DOCTEST_CHECK(ctx.getPrimitiveColor(triangle_UUIDs_copy[0]) == blue_color);
801 DOCTEST_CHECK(ctx.isPrimitiveTextureColorOverridden(triangle_UUIDs_copy[0]));
804 SUBCASE(
"domain cropping") {
811 bool has_output1, has_output2;
813 capture_cerr cerr_buffer;
815 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p1));
817 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p3));
819 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p4));
820 has_output1 = cerr_buffer.has_output();
822 DOCTEST_CHECK(has_output1);
825 capture_cerr cerr_buffer;
826 std::vector<uint> ids_rem = ctx.getAllUUIDs();
828 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p2));
829 has_output2 = cerr_buffer.has_output();
831 DOCTEST_CHECK(has_output2);
835TEST_CASE(
"Data Management") {
836 SUBCASE(
"global and object data") {
837 Context context_test;
839 context_test.setGlobalData(
"some_data", gdata);
841 DOCTEST_CHECK(context_test.doesGlobalDataExist(
"some_data"));
842 context_test.getGlobalData(
"some_data", gdata_r);
843 DOCTEST_CHECK(gdata_r == gdata);
845 std::vector<float> gvec{0, 1, 2, 3, 4};
846 context_test.setGlobalData(
"vec", gvec);
847 std::vector<float> gvec_r;
848 context_test.getGlobalData(
"vec", gvec_r);
849 DOCTEST_CHECK(gvec_r == gvec);
853 context_test.setObjectData(objID,
"obj", objdata);
855 context_test.getObjectData(objID,
"obj", objdata_r);
856 DOCTEST_CHECK(objdata_r == objdata);
858 SUBCASE(
"timeseries") {
864 Time time1 =
make_Time(time0.hour, 49, 14);
865 ctx.addTimeseriesData(
"ts", 302.3f, date, time0);
866 ctx.addTimeseriesData(
"ts", 305.3f, date, time1);
867 ctx.setCurrentTimeseriesPoint(
"ts", 0);
868 DOCTEST_CHECK(ctx.getTimeseriesLength(
"ts") == 2);
869 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", 0) == doctest::Approx(302.3f));
870 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", 1) == doctest::Approx(305.3f));
871 float val = ctx.queryTimeseriesData(
"ts", date, time1);
872 DOCTEST_CHECK(val == doctest::Approx(305.3f));
873 DOCTEST_CHECK(ctx.doesTimeseriesVariableExist(
"ts"));
874 std::vector<std::string> labels = ctx.listTimeseriesVariables();
875 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"ts") != labels.end());
876 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", ctx.getTimeseriesLength(
"ts") - 1) == doctest::Approx(305.3f));
877 Time t1_r = ctx.queryTimeseriesTime(
"ts", 1);
878 Date d1_r = ctx.queryTimeseriesDate(
"ts", 1);
879 DOCTEST_CHECK(t1_r.minute == time1.minute);
880 DOCTEST_CHECK(d1_r.day == date.day);
881 ctx.setCurrentTimeseriesPoint(
"ts", 1);
882 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts") == doctest::Approx(305.3f));
885 SUBCASE(
"Primitive data") {
886 capture_cerr cerr_buffer;
889 uint p = ctx.addPatch();
890 ctx.setPrimitiveData(p,
"test_int", 5);
891 ctx.setPrimitiveData(p,
"test_float", 3.14f);
894 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_int") == HELIOS_TYPE_INT);
895 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
898 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_int") == 1);
901 ctx.clearPrimitiveData(p,
"test_int");
902 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p,
"test_int"));
905 std::vector<std::string> data_labels = ctx.listPrimitiveData(p);
906 DOCTEST_CHECK(std::find(data_labels.begin(), data_labels.end(),
"test_float") != data_labels.end());
909 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataSize(p,
"test_int"));
912 ctx.clearPrimitiveData(p,
"test_int");
913 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p,
"test_int"));
916 ctx.setPrimitiveData(p,
"test_int", 5);
917 ctx.setPrimitiveData(p,
"test_float", 3.14f);
918 std::vector<std::string> labels = ctx.listPrimitiveData(p);
919 DOCTEST_CHECK(labels.size() == 2);
920 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"test_int") != labels.end());
921 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"test_float") != labels.end());
922 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
926TEST_CASE(
"Data and Object Management") {
928 SUBCASE(
"Global data management") {
930 ctx.setGlobalData(
"test_double", 1.23);
931 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_double") == 1);
932 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_double") == HELIOS_TYPE_DOUBLE);
933 ctx.clearGlobalData(
"test_double");
934 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"test_double"));
935 ctx.setGlobalData(
"test_string",
"hello");
936 std::vector<std::string> global_data_labels = ctx.listGlobalData();
937 DOCTEST_CHECK(std::find(global_data_labels.begin(), global_data_labels.end(),
"test_string") != global_data_labels.end());
940 SUBCASE(
"Object data management") {
943 ctx.setObjectData(
obj,
"test_vec", vec3(1, 2, 3));
944 DOCTEST_CHECK(ctx.getObjectDataSize(
obj,
"test_vec") == 1);
945 DOCTEST_CHECK(ctx.getObjectDataType(
"test_vec") == HELIOS_TYPE_VEC3);
946 ctx.clearObjectData(
obj,
"test_vec");
947 DOCTEST_CHECK(!ctx.doesObjectDataExist(
obj,
"test_vec"));
948 ctx.setObjectData(
obj,
"test_int", 42);
949 std::vector<std::string> object_data_labels = ctx.listObjectData(
obj);
950 DOCTEST_CHECK(std::find(object_data_labels.begin(), object_data_labels.end(),
"test_int") != object_data_labels.end());
953 SUBCASE(
"Object creation and manipulation") {
956 DOCTEST_CHECK(ctx.getObjectType(disk) == OBJECT_TYPE_DISK);
957 DOCTEST_CHECK(ctx.getObjectArea(disk) > 0);
958 DOCTEST_CHECK(ctx.getDiskObjectCenter(disk) ==
make_vec3(0, 0, 0));
959 DOCTEST_CHECK(ctx.getDiskObjectSubdivisionCount(disk) == 10);
960 DOCTEST_CHECK(ctx.getDiskObjectSize(disk).x == doctest::Approx(1.f));
962 uint sphere = ctx.addSphereObject(10,
make_vec3(1, 1, 1), 0.5f);
963 DOCTEST_CHECK(ctx.getObjectType(sphere) == OBJECT_TYPE_SPHERE);
964 DOCTEST_CHECK(ctx.getObjectArea(sphere) > 0);
965 DOCTEST_CHECK(ctx.getSphereObjectCenter(sphere) ==
make_vec3(1, 1, 1));
966 DOCTEST_CHECK(ctx.getSphereObjectSubdivisionCount(sphere) == 10);
967 DOCTEST_CHECK(ctx.getSphereObjectRadius(sphere).x == doctest::Approx(0.5f));
969 std::vector<uint> p_uuids;
971 uint polymesh = ctx.addPolymeshObject(p_uuids);
972 DOCTEST_CHECK(ctx.getObjectType(polymesh) == OBJECT_TYPE_POLYMESH);
973 DOCTEST_CHECK(ctx.getObjectArea(polymesh) > 0);
974 DOCTEST_CHECK(ctx.getObjectCenter(polymesh).z == doctest::Approx(0.f));
977 std::vector<float> radii = {0.2f, 0.1f};
978 uint tube = ctx.addTubeObject(10, nodes, radii);
979 DOCTEST_CHECK(ctx.getObjectType(tube) == OBJECT_TYPE_TUBE);
980 DOCTEST_CHECK(ctx.getObjectArea(tube) > 0);
981 DOCTEST_CHECK(ctx.getObjectCenter(tube).z == doctest::Approx(0.5f));
982 DOCTEST_CHECK(ctx.getTubeObjectSubdivisionCount(tube) == 10);
983 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 2);
984 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube).size() == 2);
985 DOCTEST_CHECK(ctx.getTubeObjectNodeColors(tube).size() == 2);
986 DOCTEST_CHECK(ctx.getTubeObjectVolume(tube) > 0);
987 ctx.appendTubeSegment(tube,
make_vec3(0, 0, 2), 0.05f, RGB::red);
988 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 3);
989 ctx.scaleTubeGirth(tube, 2.f);
990 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube)[0] == doctest::Approx(0.4f));
991 std::vector<float> new_radii = {0.3f, 0.2f, 0.1f};
992 ctx.setTubeRadii(tube, new_radii);
993 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube)[0] == doctest::Approx(0.3f));
994 ctx.scaleTubeLength(tube, 2.f);
996 ctx.setTubeNodes(tube, new_nodes);
997 ctx.pruneTubeNodes(tube, 1);
998 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 1);
1001 SUBCASE(
"Object appearance and visibility") {
1004 ctx.overrideObjectTextureColor(box);
1006 ctx.useObjectTextureColor(box);
1008 ctx.hideObject(box);
1009 DOCTEST_CHECK(ctx.isObjectHidden(box));
1010 ctx.showObject(box);
1011 DOCTEST_CHECK(!ctx.isObjectHidden(box));
1013 std::vector<uint> prims = ctx.getObjectPrimitiveUUIDs(box);
1014 ctx.hidePrimitive(prims);
1015 DOCTEST_CHECK(ctx.isPrimitiveHidden(prims[0]));
1016 ctx.showPrimitive(prims);
1017 DOCTEST_CHECK(!ctx.isPrimitiveHidden(prims[0]));
1020 SUBCASE(
"Primitive color and parent object") {
1021 capture_cerr cerr_buffer;
1023 uint p = ctx.addPatch();
1024 ctx.setPrimitiveColor(p, RGB::red);
1025 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::red);
1026 ctx.overridePrimitiveTextureColor(p);
1027 DOCTEST_CHECK(ctx.isPrimitiveTextureColorOverridden(p));
1028 ctx.usePrimitiveTextureColor(p);
1029 DOCTEST_CHECK(!ctx.isPrimitiveTextureColorOverridden(p));
1032 ctx.setPrimitiveParentObjectID(p,
obj);
1033 DOCTEST_CHECK(ctx.getPrimitiveParentObjectID(p) ==
obj);
1036TEST_CASE(
"Object Management: Creation and Properties") {
1038 SUBCASE(
"addSphereObject") {
1040 uint objID = ctx.addSphereObject(10,
make_vec3(1, 2, 3), 5.f);
1041 DOCTEST_CHECK(ctx.doesObjectExist(objID));
1042 DOCTEST_CHECK(ctx.getSphereObjectCenter(objID) ==
make_vec3(1, 2, 3));
1043 DOCTEST_CHECK(ctx.getSphereObjectRadius(objID) ==
make_vec3(5.f, 5.f, 5.f));
1044 DOCTEST_CHECK(ctx.getSphereObjectSubdivisionCount(objID) == 10);
1047 SUBCASE(
"addDiskObject") {
1050 DOCTEST_CHECK(ctx.doesObjectExist(objID));
1051 DOCTEST_CHECK(ctx.getDiskObjectCenter(objID) ==
make_vec3(1, 2, 3));
1052 DOCTEST_CHECK(ctx.getDiskObjectSize(objID) ==
make_vec2(4, 5));
1053 DOCTEST_CHECK(ctx.getDiskObjectSubdivisionCount(objID) == 8u);
1056 SUBCASE(
"addConeObject") {
1059 DOCTEST_CHECK(ctx.doesObjectExist(objID));
1060 DOCTEST_CHECK(ctx.getConeObjectNode(objID, 0) ==
make_vec3(0, 0, 0));
1061 DOCTEST_CHECK(ctx.getConeObjectNode(objID, 1) ==
make_vec3(0, 0, 5));
1062 DOCTEST_CHECK(ctx.getConeObjectNodeRadius(objID, 0) == 2.f);
1063 DOCTEST_CHECK(ctx.getConeObjectNodeRadius(objID, 1) == 1.f);
1064 DOCTEST_CHECK(ctx.getConeObjectSubdivisionCount(objID) == 10);
1068TEST_CASE(
"Global Data Management") {
1069 SUBCASE(
"Integer Data") {
1071 ctx.setGlobalData(
"test_int", 123);
1072 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"test_int"));
1074 ctx.getGlobalData(
"test_int", val);
1075 DOCTEST_CHECK(val == 123);
1076 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_int") == 1);
1077 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_int") == HELIOS_TYPE_INT);
1078 ctx.clearGlobalData(
"test_int");
1079 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"test_int"));
1082 SUBCASE(
"Vector Data") {
1084 std::vector<vec3> vec_data = {{1, 2, 3}, {4, 5, 6}};
1085 ctx.setGlobalData(
"test_vec", vec_data);
1086 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"test_vec"));
1087 std::vector<vec3> read_vec;
1088 ctx.getGlobalData(
"test_vec", read_vec);
1089 DOCTEST_CHECK(read_vec.size() == 2);
1090 DOCTEST_CHECK(read_vec[1] ==
make_vec3(4, 5, 6));
1091 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_vec") == 2);
1092 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_vec") == HELIOS_TYPE_VEC3);
1095 SUBCASE(
"List Data") {
1097 ctx.setGlobalData(
"d1", 1);
1098 ctx.setGlobalData(
"d2", 2.f);
1099 std::vector<std::string> labels = ctx.listGlobalData();
1100 DOCTEST_CHECK(labels.size() == 2);
1101 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"d1") != labels.end());
1105TEST_CASE(
"Context primitive data management") {
1107 uint p1 = ctx.addPatch();
1108 uint p2 = ctx.addPatch();
1109 ctx.setPrimitiveData(p1,
"my_data", 10);
1112 ctx.copyPrimitiveData(p1, p2);
1113 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2,
"my_data"));
1115 ctx.getPrimitiveData(p2,
"my_data", val);
1116 DOCTEST_CHECK(val == 10);
1119 ctx.renamePrimitiveData(p1,
"my_data",
"new_data_name");
1120 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p1,
"my_data"));
1121 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1,
"new_data_name"));
1124 ctx.duplicatePrimitiveData(p2,
"my_data",
"my_data_copy");
1125 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2,
"my_data_copy"));
1126 ctx.getPrimitiveData(p2,
"my_data_copy", val);
1127 DOCTEST_CHECK(val == 10);
1130 ctx.setPrimitiveData(p1,
"global_copy_test", 5.5f);
1131 ctx.duplicatePrimitiveData(
"global_copy_test",
"global_copy_test_new");
1132 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1,
"global_copy_test_new"));
1133 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p2,
"global_copy_test_new"));
1135 ctx.clearPrimitiveData(p1,
"new_data_name");
1136 ctx.setPrimitiveData(p2,
"my_data_copy", 15);
1137 ctx.setPrimitiveData(p2,
"my_data_copy", 20);
1138 ctx.clearPrimitiveData(p2,
"my_data_copy");
1139 std::vector<std::string> all_labels = ctx.listAllPrimitiveDataLabels();
1140 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"my_data") != all_labels.end());
1141 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"my_data_copy") == all_labels.end());
1142 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"new_data_name") == all_labels.end());
1145TEST_CASE(
"Context primitive data calculations") {
1147 std::vector<uint> uuids;
1148 for (
int i = 0; i < 5; ++i) {
1150 ctx.setPrimitiveData(p,
"float_val", (
float) i);
1151 ctx.setPrimitiveData(p,
"double_val", (
double) i);
1152 ctx.setPrimitiveData(p,
"vec2_val",
make_vec2((
float) i, (
float) i));
1158 ctx.calculatePrimitiveDataMean(uuids,
"float_val", float_mean);
1159 DOCTEST_CHECK(float_mean == doctest::Approx(2.0f));
1161 ctx.calculatePrimitiveDataMean(uuids,
"double_val", double_mean);
1162 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
1164 ctx.calculatePrimitiveDataMean(uuids,
"vec2_val", vec2_mean);
1165 DOCTEST_CHECK(vec2_mean.x == doctest::Approx(2.0f));
1169 ctx.calculatePrimitiveDataAreaWeightedMean(uuids,
"float_val", awt_mean_f);
1170 DOCTEST_CHECK(awt_mean_f == doctest::Approx(2.0f));
1174 ctx.calculatePrimitiveDataSum(uuids,
"float_val", float_sum);
1175 DOCTEST_CHECK(float_sum == doctest::Approx(10.0f));
1179 ctx.calculatePrimitiveDataAreaWeightedSum(uuids,
"float_val", awt_sum_f);
1180 DOCTEST_CHECK(awt_sum_f == doctest::Approx(10.0f));
1183 ctx.scalePrimitiveData(uuids,
"float_val", 2.0f);
1184 ctx.getPrimitiveData(uuids[2],
"float_val", float_mean);
1185 DOCTEST_CHECK(float_mean == doctest::Approx(4.0f));
1186 ctx.scalePrimitiveData(
"double_val", 0.5f);
1187 ctx.getPrimitiveData(uuids[4],
"double_val", double_mean);
1188 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
1191 ctx.setPrimitiveData(uuids,
"int_val", 10);
1192 ctx.incrementPrimitiveData(uuids,
"int_val", 5);
1194 ctx.getPrimitiveData(uuids[0],
"int_val", int_val);
1195 DOCTEST_CHECK(int_val == 15);
1198 capture_cerr cerr_buffer;
1199 ctx.incrementPrimitiveData(uuids,
"float_val", 1);
1200 has_warning = cerr_buffer.has_output();
1202 DOCTEST_CHECK(has_warning);
1205TEST_CASE(
"Context primitive data aggregation and filtering") {
1207 std::vector<uint> uuids;
1208 for (
int i = 0; i < 3; ++i) {
1209 uint p = ctx.addPatch();
1210 ctx.setPrimitiveData(p,
"d1", (
float) i);
1211 ctx.setPrimitiveData(p,
"d2", (
float) i * 2.0f);
1212 ctx.setPrimitiveData(p,
"d3", (
float) i * 3.0f);
1213 ctx.setPrimitiveData(p,
"filter_me", i);
1218 std::vector<std::string> labels = {
"d1",
"d2",
"d3"};
1219 ctx.aggregatePrimitiveDataSum(uuids, labels,
"sum_data");
1221 ctx.getPrimitiveData(uuids[1],
"sum_data", sum_val);
1222 DOCTEST_CHECK(sum_val == doctest::Approx(1.f + 2.f + 3.f));
1225 ctx.aggregatePrimitiveDataProduct(uuids, labels,
"prod_data");
1227 ctx.getPrimitiveData(uuids[2],
"prod_data", prod_val);
1228 DOCTEST_CHECK(prod_val == doctest::Approx(2.f * 4.f * 6.f));
1231 std::vector<uint> filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
">=");
1232 DOCTEST_CHECK(filtered.size() == 2);
1233 filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
"==");
1234 DOCTEST_CHECK(filtered.size() == 1);
1235 DOCTEST_CHECK(filtered[0] == uuids[1]);
1236 capture_cerr cerr_buffer;
1237 DOCTEST_CHECK_THROWS_AS(filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
"!!"), std::runtime_error);
1240TEST_CASE(
"Object data") {
1243 ctx.setObjectData(o,
"test_int", 5);
1244 ctx.setObjectData(o,
"test_float", 3.14f);
1247 DOCTEST_CHECK(ctx.getObjectDataType(
"test_int") == HELIOS_TYPE_INT);
1249 capture_cerr cerr_buffer;
1250 DOCTEST_CHECK_THROWS_AS(ctx.getObjectDataType(
"non_existent"), std::runtime_error);
1254 DOCTEST_CHECK(ctx.getObjectDataSize(o,
"test_int") == 1);
1257 ctx.clearObjectData(o,
"test_int");
1258 DOCTEST_CHECK(!ctx.doesObjectDataExist(o,
"test_int"));
1261 std::vector<std::string> data_labels = ctx.listObjectData(o);
1262 DOCTEST_CHECK(std::find(data_labels.begin(), data_labels.end(),
"test_float") != data_labels.end());
1265TEST_CASE(
"Context object data management") {
1269 ctx.setObjectData(o1,
"my_data", 10);
1272 ctx.copyObjectData(o1, o2);
1273 DOCTEST_CHECK(ctx.doesObjectDataExist(o2,
"my_data"));
1276 ctx.renameObjectData(o1,
"my_data",
"new_name");
1277 DOCTEST_CHECK(!ctx.doesObjectDataExist(o1,
"my_data"));
1278 DOCTEST_CHECK(ctx.doesObjectDataExist(o1,
"new_name"));
1281 ctx.duplicateObjectData(o2,
"my_data",
"my_data_copy");
1282 DOCTEST_CHECK(ctx.doesObjectDataExist(o2,
"my_data_copy"));
1284 std::vector<std::string> all_obj_labels = ctx.listAllObjectDataLabels();
1285 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"my_data") != all_obj_labels.end());
1286 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"my_data_copy") != all_obj_labels.end());
1287 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"new_name") != all_obj_labels.end());
1290TEST_CASE(
"Global data") {
1292 ctx.setGlobalData(
"g_int", 5);
1293 ctx.setGlobalData(
"g_float", 3.14f);
1296 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int"));
1297 DOCTEST_CHECK(ctx.getGlobalDataType(
"g_int") == HELIOS_TYPE_INT);
1298 DOCTEST_CHECK(ctx.getGlobalDataSize(
"g_int") == 1);
1301 ctx.duplicateGlobalData(
"g_int",
"g_int_copy");
1302 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int_copy"));
1303 ctx.renameGlobalData(
"g_int",
"g_int_new");
1304 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"g_int"));
1305 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int_new"));
1306 ctx.clearGlobalData(
"g_int_new");
1307 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"g_int_new"));
1310 std::vector<std::string> g_labels = ctx.listGlobalData();
1311 DOCTEST_CHECK(g_labels.size() > 0);
1314 ctx.setGlobalData(
"inc_me", 10);
1315 ctx.incrementGlobalData(
"inc_me", 5);
1317 ctx.getGlobalData(
"inc_me", val);
1318 DOCTEST_CHECK(val == 15);
1321 capture_cerr cerr_buffer;
1322 ctx.incrementGlobalData(
"g_float", 1);
1323 has_warning = cerr_buffer.has_output();
1325 DOCTEST_CHECK(has_warning);
1328TEST_CASE(
"Voxel Management") {
1329 SUBCASE(
"addVoxel and voxel properties") {
1334 float rotation = 0.5f *
PI_F;
1336 uint vox1 = ctx.addVoxel(center, size);
1337 DOCTEST_CHECK(ctx.getPrimitiveType(vox1) == PRIMITIVE_TYPE_VOXEL);
1338 DOCTEST_CHECK(ctx.getVoxelCenter(vox1) == center);
1339 DOCTEST_CHECK(ctx.getVoxelSize(vox1) == size);
1341 uint vox2 = ctx.addVoxel(center, size, rotation);
1342 DOCTEST_CHECK(ctx.getVoxelCenter(vox2) == center);
1343 DOCTEST_CHECK(ctx.getVoxelSize(vox2) == size);
1345 uint vox3 = ctx.addVoxel(center, size, rotation, RGB::red);
1346 DOCTEST_CHECK(ctx.getPrimitiveColor(vox3) == RGB::red);
1348 uint vox4 = ctx.addVoxel(center, size, rotation, RGBA::red);
1349 RGBAcolor color_rgba = ctx.getPrimitiveColorRGBA(vox4);
1350 DOCTEST_CHECK(color_rgba.r == RGBA::red.r);
1351 DOCTEST_CHECK(color_rgba.a == RGBA::red.a);
1353 DOCTEST_CHECK(ctx.getPrimitiveCount() >= 4);
1355 float area = ctx.getPrimitiveArea(vox1);
1356 DOCTEST_CHECK(area == doctest::Approx(2.f * (size.x * size.y + size.y * size.z + size.x * size.z)));
1360TEST_CASE(
"Texture Management") {
1361 SUBCASE(
"texture validation and properties") {
1362 capture_cerr cerr_buffer;
1366 ctx.setPrimitiveTextureFile(patch,
"lib/images/solid.jpg");
1367 DOCTEST_CHECK(ctx.getPrimitiveTextureFile(patch) ==
"lib/images/solid.jpg");
1368 DOCTEST_CHECK(!ctx.primitiveTextureHasTransparencyChannel(patch));
1370 Texture tex(
"lib/images/solid.jpg");
1371 std::vector<vec2> uv = {{0, 0}, {1, 0}, {1, 1}};
1372 float solid_frac = tex.getSolidFraction(uv);
1373 DOCTEST_CHECK(solid_frac == doctest::Approx(1.f));
1377TEST_CASE(
"Triangle Management") {
1378 SUBCASE(
"setTriangleVertices") {
1383 uint tri = ctx.addTriangle(v0, v1, v2);
1388 ctx.setTriangleVertices(tri, new_v0, new_v1, new_v2);
1390 std::vector<vec3> vertices = ctx.getPrimitiveVertices(tri);
1391 DOCTEST_CHECK(vertices[0] == new_v0);
1392 DOCTEST_CHECK(vertices[1] == new_v1);
1393 DOCTEST_CHECK(vertices[2] == new_v2);
1397TEST_CASE(
"UUID and Object Management") {
1398 SUBCASE(
"getAllUUIDs and cleanDeletedUUIDs") {
1400 uint p1 = ctx.addPatch();
1401 uint p2 = ctx.addPatch();
1402 uint p3 = ctx.addPatch();
1404 std::vector<uint> all_uuids = ctx.getAllUUIDs();
1405 DOCTEST_CHECK(all_uuids.size() == 3);
1406 DOCTEST_CHECK(std::find(all_uuids.begin(), all_uuids.end(), p1) != all_uuids.end());
1408 ctx.deletePrimitive(p2);
1409 std::vector<uint> uuids_with_deleted = {p1, p2, p3};
1410 ctx.cleanDeletedUUIDs(uuids_with_deleted);
1411 DOCTEST_CHECK(uuids_with_deleted.size() == 2);
1412 DOCTEST_CHECK(std::find(uuids_with_deleted.begin(), uuids_with_deleted.end(), p2) == uuids_with_deleted.end());
1414 std::vector<std::vector<uint>> nested_uuids = {{p1, p2}, {p3, p2}};
1415 ctx.cleanDeletedUUIDs(nested_uuids);
1416 DOCTEST_CHECK(nested_uuids[0].size() == 1);
1417 DOCTEST_CHECK(nested_uuids[1].size() == 1);
1419 std::vector<std::vector<std::vector<uint>>> triple_nested = {{{p1, p2, p3}}};
1420 ctx.cleanDeletedUUIDs(triple_nested);
1421 DOCTEST_CHECK(triple_nested[0][0].size() == 2);
1424 SUBCASE(
"object management utilities") {
1428 DOCTEST_CHECK(ctx.areObjectPrimitivesComplete(
obj));
1430 std::vector<uint> obj_ids = {
obj, 999};
1431 ctx.cleanDeletedObjectIDs(obj_ids);
1432 DOCTEST_CHECK(obj_ids.size() == 1);
1433 DOCTEST_CHECK(obj_ids[0] ==
obj);
1435 std::vector<std::vector<uint>> nested_obj_ids = {{
obj, 999}, {
obj}};
1436 ctx.cleanDeletedObjectIDs(nested_obj_ids);
1437 DOCTEST_CHECK(nested_obj_ids[0].size() == 1);
1438 DOCTEST_CHECK(nested_obj_ids[1].size() == 1);
1440 std::vector<std::vector<std::vector<uint>>> triple_nested_obj = {{{
obj, 999}}};
1441 ctx.cleanDeletedObjectIDs(triple_nested_obj);
1442 DOCTEST_CHECK(triple_nested_obj[0][0].size() == 1);
1444 DOCTEST_CHECK(ctx.doesObjectExist(
obj));
1447 ctx.setObjectOrigin(
obj, new_origin);
1450 ctx.setObjectAverageNormal(
obj,
make_vec3(0, 0, 0), new_normal);
1454TEST_CASE(
"Tile Object Advanced Features") {
1455 SUBCASE(
"tile object subdivision management") {
1459 float area_ratio = ctx.getTileObjectAreaRatio(tile);
1460 DOCTEST_CHECK(area_ratio > 0.f);
1462 ctx.setTileObjectSubdivisionCount({tile},
make_int2(4, 4));
1464 ctx.setTileObjectSubdivisionCount({tile}, 0.5f);
1468TEST_CASE(
"Pseudocolor Visualization") {
1469 SUBCASE(
"colorPrimitiveByDataPseudocolor") {
1471 std::vector<uint> patches;
1472 for (
int i = 0; i < 5; i++) {
1473 uint p = ctx.addPatch();
1474 ctx.setPrimitiveData(p,
"value",
float(i));
1475 patches.push_back(p);
1478 DOCTEST_CHECK_NOTHROW(ctx.colorPrimitiveByDataPseudocolor(patches,
"value",
"hot", 10));
1479 DOCTEST_CHECK_NOTHROW(ctx.colorPrimitiveByDataPseudocolor(patches,
"value",
"rainbow", 5, 0.f, 4.f));
1483TEST_CASE(
"Date and Time Extensions") {
1484 SUBCASE(
"getMonthString") {
1486 ctx.setDate(15, 1, 2025);
1487 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"JAN") == 0);
1488 ctx.setDate(15, 2, 2025);
1489 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"FEB") == 0);
1490 ctx.setDate(15, 12, 2025);
1491 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"DEC") == 0);
1495TEST_CASE(
"Tube Object Management") {
1496 SUBCASE(
"appendTubeSegment with texture") {
1499 std::vector<float> radii = {0.2f, 0.1f};
1500 uint tube = ctx.addTubeObject(10, nodes, radii);
1502 ctx.appendTubeSegment(tube,
make_vec3(0, 0, 2), 0.05f,
"lib/images/solid.jpg",
make_vec2(0.5f, 1.0f));
1503 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 3);
1507TEST_CASE(
"Edge Cases and Additional Coverage") {
1508 SUBCASE(
"Julian date edge cases") {
1510 ctx.setDate(1, 1, 2025);
1511 DOCTEST_CHECK(ctx.getJulianDate() == 1);
1513 ctx.setDate(31, 12, 2025);
1514 DOCTEST_CHECK(ctx.getJulianDate() == 365);
1516 ctx.setDate(100, 2025);
1517 Date d = ctx.getDate();
1518 DOCTEST_CHECK(d.day == 10);
1519 DOCTEST_CHECK(d.month == 4);
1522 SUBCASE(
"time edge cases") {
1524 ctx.setTime(0, 0, 0);
1525 Time t = ctx.getTime();
1526 DOCTEST_CHECK(t.hour == 0);
1527 DOCTEST_CHECK(t.minute == 0);
1528 DOCTEST_CHECK(t.second == 0);
1530 ctx.setTime(59, 59, 23);
1532 DOCTEST_CHECK(t.hour == 23);
1533 DOCTEST_CHECK(t.minute == 59);
1534 DOCTEST_CHECK(t.second == 59);
1537 SUBCASE(
"random number edge cases") {
1539 ctx.seedRandomGenerator(0);
1541 float r1 = ctx.randu(5.f, 5.f);
1542 DOCTEST_CHECK(r1 == doctest::Approx(5.f));
1544 int ri = ctx.randu(10, 10);
1545 DOCTEST_CHECK(ri == 10);
1547 float rn = ctx.randn(0.f, 0.f);
1548 DOCTEST_CHECK(rn == doctest::Approx(0.f));
1551 SUBCASE(
"texture edge cases") {
1552 capture_cerr cerr_buffer;
1554 uint patch = ctx.addPatch();
1556 ctx.overridePrimitiveTextureColor(patch);
1557 ctx.usePrimitiveTextureColor(patch);
1559 std::vector<uint> patches = {patch};
1560 ctx.overridePrimitiveTextureColor(patches);
1561 ctx.usePrimitiveTextureColor(patches);
1563 DOCTEST_CHECK(!ctx.isPrimitiveTextureColorOverridden(patch));
1566 SUBCASE(
"primitive existence checks") {
1568 uint p1 = ctx.addPatch();
1569 uint p2 = ctx.addPatch();
1571 DOCTEST_CHECK(ctx.doesPrimitiveExist(p1));
1572 DOCTEST_CHECK(ctx.doesPrimitiveExist({p1, p2}));
1574 ctx.deletePrimitive(p1);
1575 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p1));
1576 DOCTEST_CHECK(!ctx.doesPrimitiveExist({p1, p2}));
1577 DOCTEST_CHECK(ctx.doesPrimitiveExist(std::vector<uint>{p2}));
1580 SUBCASE(
"object containment checks") {
1583 std::vector<uint> prims = ctx.getObjectPrimitiveUUIDs(
obj);
1585 DOCTEST_CHECK(ctx.doesObjectContainPrimitive(
obj, prims[0]));
1587 uint independent_patch = ctx.addPatch();
1588 DOCTEST_CHECK(!ctx.doesObjectContainPrimitive(
obj, independent_patch));
1591 SUBCASE(
"transformation matrix operations") {
1593 uint p = ctx.addPatch();
1595 float identity[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
1596 ctx.setPrimitiveTransformationMatrix(p, identity);
1598 std::vector<uint> patches = {p};
1599 ctx.setPrimitiveTransformationMatrix(patches, identity);
1602 ctx.setObjectTransformationMatrix(
obj, identity);
1604 std::vector<uint> objs = {
obj};
1605 ctx.setObjectTransformationMatrix(objs, identity);
1607 float retrieved[16];
1608 ctx.getObjectTransformationMatrix(
obj, retrieved);
1609 for (
int i = 0; i < 16; i++) {
1610 DOCTEST_CHECK(retrieved[i] == doctest::Approx(identity[i]));
1614 SUBCASE(
"object type and texture checks") {
1618 DOCTEST_CHECK(!ctx.objectHasTexture(
obj));
1621 DOCTEST_CHECK(ctx.objectHasTexture(textured_obj));
1624 SUBCASE(
"tube object segment operations") {
1627 std::vector<float> radii = {0.2f, 0.15f, 0.1f};
1628 uint tube = ctx.addTubeObject(10, nodes, radii);
1630 float seg_volume = ctx.getTubeObjectSegmentVolume(tube, 0);
1631 DOCTEST_CHECK(seg_volume > 0.f);
1633 seg_volume = ctx.getTubeObjectSegmentVolume(tube, 1);
1634 DOCTEST_CHECK(seg_volume > 0.f);
1637 SUBCASE(
"cone object advanced properties") {
1641 float radius0 = ctx.getConeObjectNodeRadius(cone, 0);
1642 DOCTEST_CHECK(radius0 == doctest::Approx(1.f));
1644 float radius1 = ctx.getConeObjectNodeRadius(cone, 1);
1645 DOCTEST_CHECK(radius1 == doctest::Approx(0.5f));
1647 float length = ctx.getConeObjectLength(cone);
1648 DOCTEST_CHECK(length == doctest::Approx(2.f));
1650 DOCTEST_CHECK(ctx.getConeObjectSubdivisionCount(cone) == 10);
1653 SUBCASE(
"primitive color operations") {
1654 capture_cerr cerr_buffer;
1656 uint p = ctx.addPatch();
1658 ctx.setPrimitiveColor(p, RGB::blue);
1659 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::blue);
1661 ctx.setPrimitiveColor(p, RGBA::green);
1662 RGBAcolor rgba = ctx.getPrimitiveColorRGBA(p);
1663 DOCTEST_CHECK(rgba.r == RGBA::green.r);
1664 DOCTEST_CHECK(rgba.a == RGBA::green.a);
1666 std::vector<uint> patches = {p};
1667 ctx.setPrimitiveColor(patches, RGB::red);
1668 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::red);
1670 ctx.setPrimitiveColor(patches, RGBA::yellow);
1671 rgba = ctx.getPrimitiveColorRGBA(p);
1672 DOCTEST_CHECK(rgba.r == RGBA::yellow.r);
1675 SUBCASE(
"object color operations") {
1676 capture_cerr cerr_buffer;
1680 ctx.setObjectColor(
obj, RGB::cyan);
1681 ctx.setObjectColor(
obj, RGBA::magenta);
1683 std::vector<uint> objs = {
obj};
1684 ctx.setObjectColor(objs, RGB::white);
1685 ctx.setObjectColor(objs, RGBA::black);
1689TEST_CASE(
"Print and Information Functions") {
1690 SUBCASE(
"printPrimitiveInfo and printObjectInfo") {
1699 capture_cout cout_buffer;
1700 DOCTEST_CHECK_NOTHROW(ctx.printPrimitiveInfo(patch));
1701 DOCTEST_CHECK_NOTHROW(ctx.printObjectInfo(
obj));
1702 has_output = cout_buffer.has_output();
1703 output = cout_buffer.get_captured_output();
1707 DOCTEST_CHECK(has_output);
1708 DOCTEST_CHECK(output.find(
"Info for UUID") != std::string::npos);
1709 DOCTEST_CHECK(output.find(
"Info for ObjID") != std::string::npos);
1713TEST_CASE(
"Object Pointer Access") {
1714 SUBCASE(
"getObjectPointer functions") {
1718 DOCTEST_CHECK(ctx.doesObjectExist(box));
1721 DOCTEST_CHECK(ctx.doesObjectExist(disk));
1723 uint sphere = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1724 DOCTEST_CHECK(ctx.doesObjectExist(sphere));
1727 std::vector<float> radii = {0.2f, 0.1f};
1728 uint tube = ctx.addTubeObject(10, nodes, radii);
1729 DOCTEST_CHECK(ctx.doesObjectExist(tube));
1732 DOCTEST_CHECK(ctx.doesObjectExist(cone));
1735 uint polymesh = ctx.addPolymeshObject(prim_uuids);
1736 DOCTEST_CHECK(ctx.doesObjectExist(polymesh));
1740TEST_CASE(
"Advanced Primitive Operations") {
1741 SUBCASE(
"primitive visibility and print operations") {
1743 uint p1 = ctx.addPatch();
1744 uint p2 = ctx.addPatch();
1747 ctx.hidePrimitive(p1);
1748 DOCTEST_CHECK(ctx.isPrimitiveHidden(p1));
1749 ctx.showPrimitive(p1);
1750 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p1));
1752 std::vector<uint> patches = {p1, p2};
1753 ctx.hidePrimitive(patches);
1754 DOCTEST_CHECK(ctx.isPrimitiveHidden(p1));
1755 DOCTEST_CHECK(ctx.isPrimitiveHidden(p2));
1756 ctx.showPrimitive(patches);
1757 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p1));
1758 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p2));
1761 SUBCASE(
"primitive counts by type") {
1763 uint initial_patch_count = ctx.getPatchCount();
1764 uint initial_triangle_count = ctx.getTriangleCount();
1766 uint p1 = ctx.addPatch();
1767 uint p2 = ctx.addPatch();
1770 DOCTEST_CHECK(ctx.getPatchCount() == initial_patch_count + 2);
1771 DOCTEST_CHECK(ctx.getTriangleCount() == initial_triangle_count + 1);
1774 ctx.hidePrimitive(p1);
1775 DOCTEST_CHECK(ctx.getPatchCount(
false) == initial_patch_count + 1);
1776 DOCTEST_CHECK(ctx.getPatchCount(
true) == initial_patch_count + 2);
1780TEST_CASE(
"Data Type and Size Functions") {
1781 SUBCASE(
"primitive data type operations") {
1783 uint p = ctx.addPatch();
1785 ctx.setPrimitiveData(p,
"test_int", 42);
1786 ctx.setPrimitiveData(p,
"test_float", 3.14f);
1787 ctx.setPrimitiveData(p,
"test_vec3",
make_vec3(1, 2, 3));
1789 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_int") == HELIOS_TYPE_INT);
1790 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
1791 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_vec3") == HELIOS_TYPE_VEC3);
1793 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_int") == 1);
1794 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_vec3") == 1);
1796 std::vector<float> vec_data = {1.0f, 2.0f, 3.0f};
1797 ctx.setPrimitiveData(p,
"test_vector", vec_data);
1798 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_vector") == 3);
1802TEST_CASE(
"Additional Missing Coverage") {
1803 SUBCASE(
"getDirtyUUIDs function") {
1805 uint p1 = ctx.addPatch();
1806 uint p2 = ctx.addPatch();
1808 ctx.markGeometryClean();
1809 std::vector<uint> dirty_uuids = ctx.getDirtyUUIDs();
1810 DOCTEST_CHECK(dirty_uuids.empty());
1812 ctx.markPrimitiveDirty(p1);
1813 dirty_uuids = ctx.getDirtyUUIDs();
1814 DOCTEST_CHECK(dirty_uuids.size() == 1);
1815 DOCTEST_CHECK(std::find(dirty_uuids.begin(), dirty_uuids.end(), p1) != dirty_uuids.end());
1819TEST_CASE(
"Advanced Object Operations") {
1820 SUBCASE(
"object primitive count and area calculations") {
1824 DOCTEST_CHECK(ctx.getObjectPrimitiveCount(
obj) == 6);
1826 float area = ctx.getObjectArea(
obj);
1827 float expected_area = 2 * (2 * 3 + 3 * 4 + 2 * 4);
1828 DOCTEST_CHECK(area == doctest::Approx(expected_area).epsilon(0.01));
1831 SUBCASE(
"object bounding box operations") {
1835 vec3 min_corner, max_corner;
1836 ctx.getObjectBoundingBox(
obj, min_corner, max_corner);
1838 DOCTEST_CHECK(min_corner.x == doctest::Approx(0.f).epsilon(0.01));
1839 DOCTEST_CHECK(max_corner.x == doctest::Approx(2.f).epsilon(0.01));
1840 DOCTEST_CHECK(min_corner.y == doctest::Approx(0.f).epsilon(0.01));
1841 DOCTEST_CHECK(max_corner.y == doctest::Approx(4.f).epsilon(0.01));
1843 std::vector<uint> objs = {
obj};
1844 ctx.getObjectBoundingBox(objs, min_corner, max_corner);
1845 DOCTEST_CHECK(min_corner.x == doctest::Approx(0.f).epsilon(0.01));
1846 DOCTEST_CHECK(max_corner.x == doctest::Approx(2.f).epsilon(0.01));
1850TEST_CASE(
"Additional Object Features") {
1851 SUBCASE(
"getAllObjectIDs") {
1854 uint obj2 = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1856 std::vector<uint> all_ids = ctx.getAllObjectIDs();
1857 DOCTEST_CHECK(all_ids.size() >= 2);
1858 DOCTEST_CHECK(std::find(all_ids.begin(), all_ids.end(), obj1) != all_ids.end());
1859 DOCTEST_CHECK(std::find(all_ids.begin(), all_ids.end(), obj2) != all_ids.end());
1862 SUBCASE(
"object type checks") {
1865 uint sphere = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1868 DOCTEST_CHECK(ctx.getObjectType(box) == OBJECT_TYPE_BOX);
1869 DOCTEST_CHECK(ctx.getObjectType(sphere) == OBJECT_TYPE_SPHERE);
1870 DOCTEST_CHECK(ctx.getObjectType(disk) == OBJECT_TYPE_DISK);
1874TEST_CASE(
"Comprehensive Object Property Tests") {
1875 SUBCASE(
"rotation operations on objects") {
1877 std::vector<uint> objs;
1881 DOCTEST_CHECK_NOTHROW(ctx.rotateObject(objs, 0.5f *
PI_F,
"z"));
1882 DOCTEST_CHECK_NOTHROW(ctx.rotateObject(objs, 0.5f *
PI_F,
make_vec3(0, 0, 1)));
1884 DOCTEST_CHECK_NOTHROW(ctx.rotateObjectAboutOrigin(objs, 0.5f *
PI_F,
make_vec3(0, 0, 1)));
1887 SUBCASE(
"scaling operations on objects") {
1889 std::vector<uint> objs;
1892 DOCTEST_CHECK_NOTHROW(ctx.scaleObject(objs,
make_vec3(2, 2, 2)));
1893 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutCenter(objs,
make_vec3(0.5f, 0.5f, 0.5f)));
1894 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutPoint(objs,
make_vec3(2, 2, 2),
make_vec3(0, 0, 0)));
1895 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutOrigin(objs,
make_vec3(0.5f, 0.5f, 0.5f)));
1898 SUBCASE(
"translation operations on objects") {
1900 std::vector<uint> objs;
1903 DOCTEST_CHECK_NOTHROW(ctx.translateObject(objs,
make_vec3(1, 2, 3)));
1907TEST_CASE(
"Domain and Bounding Operations") {
1908 SUBCASE(
"domain bounding sphere") {
1915 ctx.getDomainBoundingSphere(center, radius);
1916 DOCTEST_CHECK(center.x == doctest::Approx(0.f).epsilon(0.1));
1917 DOCTEST_CHECK(radius > 2.f);
1921TEST_CASE(
"Missing Data and State Functions") {
1922 SUBCASE(
"listTimeseriesVariables") {
1927 ctx.addTimeseriesData(
"temp", 25.5f, date, time);
1928 ctx.addTimeseriesData(
"humidity", 60.0f, date, time);
1930 std::vector<std::string> vars = ctx.listTimeseriesVariables();
1931 DOCTEST_CHECK(vars.size() >= 2);
1932 DOCTEST_CHECK(std::find(vars.begin(), vars.end(),
"temp") != vars.end());
1933 DOCTEST_CHECK(std::find(vars.begin(), vars.end(),
"humidity") != vars.end());
1936 SUBCASE(
"getUniquePrimitiveParentObjectIDs") {
1939 uint obj2 = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1941 std::vector<uint> all_prims = ctx.getAllUUIDs();
1942 std::vector<uint> obj_ids = ctx.getUniquePrimitiveParentObjectIDs(all_prims);
1943 DOCTEST_CHECK(obj_ids.size() >= 2);
1944 DOCTEST_CHECK(std::find(obj_ids.begin(), obj_ids.end(), obj1) != obj_ids.end());
1945 DOCTEST_CHECK(std::find(obj_ids.begin(), obj_ids.end(), obj2) != obj_ids.end());
1949TEST_CASE(
"Comprehensive Coverage Tests") {
1950 SUBCASE(
"additional object operations with vectors") {
1952 std::vector<uint> obj_ids;
1956 std::vector<uint> all_uuids = ctx.getObjectPrimitiveUUIDs(obj_ids);
1957 DOCTEST_CHECK(all_uuids.size() == 12);
1959 std::vector<std::vector<uint>> nested_obj_ids = {{obj_ids[0]}, {obj_ids[1]}};
1960 std::vector<uint> nested_uuids = ctx.getObjectPrimitiveUUIDs(nested_obj_ids);
1961 DOCTEST_CHECK(nested_uuids.size() == 12);
1963 ctx.hideObject(obj_ids);
1964 DOCTEST_CHECK(ctx.isObjectHidden(obj_ids[0]));
1965 DOCTEST_CHECK(ctx.isObjectHidden(obj_ids[1]));
1967 ctx.showObject(obj_ids);
1968 DOCTEST_CHECK(!ctx.isObjectHidden(obj_ids[0]));
1969 DOCTEST_CHECK(!ctx.isObjectHidden(obj_ids[1]));
1972 SUBCASE(
"object texture color overrides") {
1974 std::vector<uint> obj_ids;
1977 ctx.overrideObjectTextureColor(obj_ids);
1978 ctx.useObjectTextureColor(obj_ids);
1982TEST_CASE(
"getAllUUIDs Cache Performance") {
1983 SUBCASE(
"Cache invalidation on primitive add/delete") {
1987 std::vector<uint> empty_uuids = ctx.getAllUUIDs();
1988 DOCTEST_CHECK(empty_uuids.empty());
1991 uint p1 = ctx.addPatch();
1992 std::vector<uint> one_uuid = ctx.getAllUUIDs();
1993 DOCTEST_CHECK(one_uuid.size() == 1);
1994 DOCTEST_CHECK(one_uuid[0] == p1);
1997 std::vector<uint> same_uuid = ctx.getAllUUIDs();
1998 DOCTEST_CHECK(same_uuid.size() == 1);
1999 DOCTEST_CHECK(same_uuid[0] == p1);
2005 std::vector<uint> three_uuids = ctx.getAllUUIDs();
2006 DOCTEST_CHECK(three_uuids.size() == 3);
2007 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), p1) != three_uuids.end());
2008 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), t1) != three_uuids.end());
2009 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), v1) != three_uuids.end());
2012 ctx.deletePrimitive(t1);
2013 std::vector<uint> two_uuids = ctx.getAllUUIDs();
2014 DOCTEST_CHECK(two_uuids.size() == 2);
2015 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), t1) == two_uuids.end());
2016 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), p1) != two_uuids.end());
2017 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), v1) != two_uuids.end());
2020 SUBCASE(
"Cache invalidation on hide/show primitives") {
2022 uint p1 = ctx.addPatch();
2023 uint p2 = ctx.addPatch();
2024 uint p3 = ctx.addPatch();
2027 std::vector<uint> all_visible = ctx.getAllUUIDs();
2028 DOCTEST_CHECK(all_visible.size() == 3);
2031 ctx.hidePrimitive(p2);
2032 std::vector<uint> two_visible = ctx.getAllUUIDs();
2033 DOCTEST_CHECK(two_visible.size() == 2);
2034 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p2) == two_visible.end());
2035 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p1) != two_visible.end());
2036 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p3) != two_visible.end());
2039 std::vector<uint> to_hide = {p1, p3};
2040 ctx.hidePrimitive(to_hide);
2041 std::vector<uint> none_visible = ctx.getAllUUIDs();
2042 DOCTEST_CHECK(none_visible.empty());
2045 ctx.showPrimitive(p1);
2046 std::vector<uint> one_visible = ctx.getAllUUIDs();
2047 DOCTEST_CHECK(one_visible.size() == 1);
2048 DOCTEST_CHECK(one_visible[0] == p1);
2051 std::vector<uint> to_show = {p2, p3};
2052 ctx.showPrimitive(to_show);
2053 std::vector<uint> all_back = ctx.getAllUUIDs();
2054 DOCTEST_CHECK(all_back.size() == 3);
2057 SUBCASE(
"Cache invalidation on copy primitives") {
2059 uint original = ctx.addPatch();
2061 std::vector<uint> before_copy = ctx.getAllUUIDs();
2062 DOCTEST_CHECK(before_copy.size() == 1);
2064 uint copied = ctx.copyPrimitive(original);
2065 std::vector<uint> after_copy = ctx.getAllUUIDs();
2066 DOCTEST_CHECK(after_copy.size() == 2);
2067 DOCTEST_CHECK(std::find(after_copy.begin(), after_copy.end(), original) != after_copy.end());
2068 DOCTEST_CHECK(std::find(after_copy.begin(), after_copy.end(), copied) != after_copy.end());
2071 std::vector<uint> originals = {original, copied};
2072 std::vector<uint> copies = ctx.copyPrimitive(originals);
2073 std::vector<uint> after_multi_copy = ctx.getAllUUIDs();
2074 DOCTEST_CHECK(after_multi_copy.size() == 4);
2075 for (
uint copy_id: copies) {
2076 DOCTEST_CHECK(std::find(after_multi_copy.begin(), after_multi_copy.end(), copy_id) != after_multi_copy.end());
2080 SUBCASE(
"Cache consistency across mixed operations") {
2084 uint p1 = ctx.addPatch();
2087 std::vector<uint> step1 = ctx.getAllUUIDs();
2088 DOCTEST_CHECK(step1.size() == 2);
2090 ctx.hidePrimitive(p1);
2091 std::vector<uint> step2 = ctx.getAllUUIDs();
2092 DOCTEST_CHECK(step2.size() == 1);
2093 DOCTEST_CHECK(step2[0] == p2);
2096 std::vector<uint> step3 = ctx.getAllUUIDs();
2097 DOCTEST_CHECK(step3.size() == 2);
2099 ctx.showPrimitive(p1);
2100 std::vector<uint> step4 = ctx.getAllUUIDs();
2101 DOCTEST_CHECK(step4.size() == 3);
2103 ctx.deletePrimitive(p2);
2104 std::vector<uint> step5 = ctx.getAllUUIDs();
2105 DOCTEST_CHECK(step5.size() == 2);
2106 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p2) == step5.end());
2107 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p1) != step5.end());
2108 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p3) != step5.end());
2112TEST_CASE(
"Error Handling") {
2113 SUBCASE(
"Context error handling") {
2114 Context context_test;
2116 capture_cerr cerr_buffer;
2119 DOCTEST_CHECK_THROWS_AS(center = context_test.getPatchCenter(tri), std::runtime_error);
2123 std::vector<uint> vlist{vox};
2124 DOCTEST_CHECK_THROWS_AS(context_test.rotatePrimitive(vlist,
PI_F / 4.f,
"a"), std::runtime_error);
2128TEST_CASE(
"Zero Area Triangle Detection") {
2129 SUBCASE(
"addTubeObject with nearly identical vertices should not create zero-area triangles") {
2133 std::vector<vec3> nodes = {
make_vec3(0.300000012f, -0.112000048f, 0.00999999978f),
make_vec3(0.29995966f, -0.111979447f, 0.0109989736f),
make_vec3(0.299919307f, -0.111958846f, 0.0119979475f)};
2135 std::vector<float> radii = {0.000500000024f, 0.000500000024f, 0.000500000024f};
2136 std::vector<RGBcolor> colors = {RGB::green, RGB::green, RGB::green};
2139 uint tube_obj = ctx.addTubeObject(7, nodes, radii, colors);
2142 DOCTEST_CHECK(ctx.doesObjectExist(tube_obj));
2145 std::vector<uint> tube_primitives = ctx.getObjectPrimitiveUUIDs(tube_obj);
2146 DOCTEST_CHECK(tube_primitives.size() > 0);
2148 for (
uint uuid: tube_primitives) {
2149 float area = ctx.getPrimitiveArea(uuid);
2150 DOCTEST_CHECK(area > 0.0f);
2151 DOCTEST_CHECK(area > 1e-12f);
2155 SUBCASE(
"addTubeObject with extremely small displacements") {
2161 std::vector<float> radii = {1e-4f, 1e-4f, 1e-4f};
2163 uint tube_obj = ctx.addTubeObject(6, nodes, radii);
2164 DOCTEST_CHECK(ctx.doesObjectExist(tube_obj));
2166 std::vector<uint> tube_primitives = ctx.getObjectPrimitiveUUIDs(tube_obj);
2167 for (
uint uuid: tube_primitives) {
2168 float area = ctx.getPrimitiveArea(uuid);
2169 DOCTEST_CHECK(area > 0.0f);
2174TEST_CASE(
"Transparent Texture Zero Area Validation") {
2175 SUBCASE(
"addSphere with transparent texture should filter zero-area triangles") {
2179 std::vector<uint> sphere_uuids = ctx.addSphere(20,
make_vec3(0, 0, 0), 1.0f,
"lib/images/diamond_texture.png");
2182 DOCTEST_CHECK(sphere_uuids.size() > 0);
2183 for (
uint uuid: sphere_uuids) {
2184 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2185 float area = ctx.getPrimitiveArea(uuid);
2186 DOCTEST_CHECK(area > 0.0f);
2190 std::vector<uint> sphere_disk_uuids = ctx.addSphere(30,
make_vec3(2, 0, 0), 1.0f,
"lib/images/disk_texture.png");
2192 DOCTEST_CHECK(sphere_disk_uuids.size() > 0);
2193 for (
uint uuid: sphere_disk_uuids) {
2194 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2195 float area = ctx.getPrimitiveArea(uuid);
2196 DOCTEST_CHECK(area > 0.0f);
2200 int zero_area_count_diamond = 0;
2201 for (
uint uuid: sphere_uuids) {
2202 float area = ctx.getPrimitiveArea(uuid);
2204 zero_area_count_diamond++;
2207 DOCTEST_CHECK(zero_area_count_diamond == 0);
2209 int zero_area_count_disk = 0;
2210 for (
uint uuid: sphere_disk_uuids) {
2211 float area = ctx.getPrimitiveArea(uuid);
2213 zero_area_count_disk++;
2216 DOCTEST_CHECK(zero_area_count_disk == 0);
2219 std::vector<uint> solid_sphere_uuids = ctx.addSphere(20,
make_vec3(4, 0, 0), 1.0f, RGB::green);
2221 int zero_area_count_solid = 0;
2222 for (
uint uuid: solid_sphere_uuids) {
2223 float area = ctx.getPrimitiveArea(uuid);
2225 zero_area_count_solid++;
2228 DOCTEST_CHECK(zero_area_count_solid == 0);
2231 SUBCASE(
"texture transparency validation preserves object integrity") {
2235 std::vector<uint> sphere_uuids = ctx.addSphere(15,
make_vec3(0, 0, 0), 1.0f,
"lib/images/diamond_texture.png");
2238 for (
uint uuid: sphere_uuids) {
2239 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2240 DOCTEST_CHECK(ctx.getPrimitiveType(uuid) == PRIMITIVE_TYPE_TRIANGLE);
2242 float area = ctx.getPrimitiveArea(uuid);
2243 DOCTEST_CHECK(area > 0.0f);
2244 DOCTEST_CHECK(area > 1e-10f);
2247 float solid_fraction = ctx.getPrimitiveSolidFraction(uuid);
2248 DOCTEST_CHECK(solid_fraction > 0.0f);
2249 DOCTEST_CHECK(solid_fraction <= 1.0f);
2253 std::vector<uint> all_uuids = ctx.getAllUUIDs();
2254 int total_zero_area = 0;
2255 int total_negative_area = 0;
2257 for (
uint uuid: all_uuids) {
2258 float area = ctx.getPrimitiveArea(uuid);
2263 total_negative_area++;
2268 DOCTEST_CHECK(total_zero_area == 0);
2269 DOCTEST_CHECK(total_negative_area == 0);
2272 for (
uint uuid: sphere_uuids) {
2273 float solid_fraction = ctx.getPrimitiveSolidFraction(uuid);
2274 DOCTEST_CHECK(solid_fraction >= 0.0f);
2275 DOCTEST_CHECK(solid_fraction <= 1.0f);
2278 if (ctx.getPrimitiveType(uuid) == PRIMITIVE_TYPE_TRIANGLE) {
2279 vec3 v0 = ctx.getTriangleVertex(uuid, 0);
2280 vec3 v1 = ctx.getTriangleVertex(uuid, 1);
2281 vec3 v2 = ctx.getTriangleVertex(uuid, 2);
2283 float effective_area = ctx.getPrimitiveArea(uuid);
2286 DOCTEST_CHECK(effective_area <= geometric_area + 1e-6f);
2287 DOCTEST_CHECK(effective_area > 0.0f);
2292 DOCTEST_SUBCASE(
"Test Other Primitive Methods Zero Area Validation") {
2297 std::vector<float> tube_radii = {0.1f, 0.15f, 0.1f};
2298 std::vector<uint> tube_uuids = ctx_other.addTube(8, tube_nodes, tube_radii,
"lib/images/diamond_texture.png");
2301 int tube_positive_area = 0, tube_zero_area = 0;
2302 for (
uint uuid: tube_uuids) {
2303 float area = ctx_other.getPrimitiveArea(uuid);
2304 DOCTEST_CHECK(area >= 0.0f);
2306 tube_positive_area++;
2312 DOCTEST_CHECK(tube_positive_area > 0);
2313 DOCTEST_CHECK(tube_zero_area == 0);
2319 int disk_positive_area = 0, disk_zero_area = 0;
2320 for (
uint uuid: disk_uuids) {
2321 float area = ctx_other.getPrimitiveArea(uuid);
2322 DOCTEST_CHECK(area >= 0.0f);
2324 disk_positive_area++;
2330 DOCTEST_CHECK(disk_positive_area > 0);
2331 DOCTEST_CHECK(disk_zero_area == 0);
2334 std::vector<uint> cone_uuids = ctx_other.addCone(8,
make_vec3(0, 0, 0),
make_vec3(0, 0, 1), 0.1f, 0.2f,
"lib/images/diamond_texture.png");
2337 int cone_positive_area = 0, cone_zero_area = 0;
2338 for (
uint uuid: cone_uuids) {
2339 float area = ctx_other.getPrimitiveArea(uuid);
2340 DOCTEST_CHECK(area >= 0.0f);
2342 cone_positive_area++;
2348 DOCTEST_CHECK(cone_positive_area > 0);
2349 DOCTEST_CHECK(cone_zero_area == 0);
2355 int tile_positive_area = 0, tile_zero_area = 0;
2356 for (
uint uuid: tile_uuids) {
2357 float area = ctx_other.getPrimitiveArea(uuid);
2358 DOCTEST_CHECK(area >= 0.0f);
2360 tile_positive_area++;
2366 DOCTEST_CHECK(tile_positive_area > 0);
2367 DOCTEST_CHECK(tile_zero_area == 0);
2371 DOCTEST_SUBCASE(
"Test Compound Object Methods Zero Area Validation") {
2372 Context ctx_compound;
2375 uint sphere_obj = ctx_compound.addSphereObject(8,
make_vec3(0, 0, 0), 0.5f,
"lib/images/diamond_texture.png");
2376 std::vector<uint> sphere_primitives = ctx_compound.getObjectPrimitiveUUIDs(sphere_obj);
2379 int sphere_positive_area = 0, sphere_zero_area = 0;
2380 for (
uint uuid: sphere_primitives) {
2381 float area = ctx_compound.getPrimitiveArea(uuid);
2382 DOCTEST_CHECK(area >= 0.0f);
2384 sphere_positive_area++;
2390 DOCTEST_CHECK(sphere_positive_area > 0);
2391 DOCTEST_CHECK(sphere_zero_area == 0);
2395 std::vector<float> tube_radii = {0.1f, 0.15f, 0.1f};
2396 uint tube_obj = ctx_compound.addTubeObject(8, tube_nodes, tube_radii,
"lib/images/diamond_texture.png");
2397 std::vector<uint> tube_primitives = ctx_compound.getObjectPrimitiveUUIDs(tube_obj);
2400 int tube_positive_area = 0, tube_zero_area = 0;
2401 for (
uint uuid: tube_primitives) {
2402 float area = ctx_compound.getPrimitiveArea(uuid);
2403 DOCTEST_CHECK(area >= 0.0f);
2405 tube_positive_area++;
2411 DOCTEST_CHECK(tube_positive_area > 0);
2412 DOCTEST_CHECK(tube_zero_area == 0);
2416 std::vector<uint> disk_primitives = ctx_compound.getObjectPrimitiveUUIDs(disk_obj);
2419 int disk_positive_area = 0, disk_zero_area = 0;
2420 for (
uint uuid: disk_primitives) {
2421 float area = ctx_compound.getPrimitiveArea(uuid);
2422 DOCTEST_CHECK(area >= 0.0f);
2424 disk_positive_area++;
2430 DOCTEST_CHECK(disk_positive_area > 0);
2431 DOCTEST_CHECK(disk_zero_area == 0);
2434 uint cone_obj = ctx_compound.addConeObject(8,
make_vec3(0, 0, 0),
make_vec3(0, 0, 1), 0.1f, 0.2f,
"lib/images/diamond_texture.png");
2435 std::vector<uint> cone_primitives = ctx_compound.getObjectPrimitiveUUIDs(cone_obj);
2438 int cone_positive_area = 0, cone_zero_area = 0;
2439 for (
uint uuid: cone_primitives) {
2440 float area = ctx_compound.getPrimitiveArea(uuid);
2441 DOCTEST_CHECK(area >= 0.0f);
2443 cone_positive_area++;
2449 DOCTEST_CHECK(cone_positive_area > 0);
2450 DOCTEST_CHECK(cone_zero_area == 0);
2455TEST_CASE(
"File path resolution priority") {
2456 SUBCASE(
"resolveFilePath current directory priority") {
2461 std::string testFileName =
"test_file_resolution.jpg";
2462 std::filesystem::path currentDirFile = std::filesystem::current_path() / testFileName;
2465 std::filesystem::path sourceTexture =
"core/lib/models/texture.jpg";
2467 if (std::filesystem::exists(sourceTexture)) {
2469 std::filesystem::copy_file(sourceTexture, currentDirFile, std::filesystem::copy_options::overwrite_existing);
2470 DOCTEST_CHECK(std::filesystem::exists(currentDirFile));
2474 DOCTEST_CHECK(resolved == std::filesystem::canonical(currentDirFile));
2477 std::filesystem::remove(currentDirFile);
2481 SUBCASE(
"addPatch with texture from current directory") {
2485 std::filesystem::create_directories(
"test_models");
2486 std::string testTexture =
"test_models/test_texture.jpg";
2487 std::filesystem::path testTexturePath = std::filesystem::current_path() / testTexture;
2490 std::filesystem::path sourceTexture =
"core/lib/models/texture.jpg";
2492 if (std::filesystem::exists(sourceTexture)) {
2493 std::filesystem::copy_file(sourceTexture, testTexturePath, std::filesystem::copy_options::overwrite_existing);
2499 DOCTEST_CHECK_NOTHROW({ patch_id = ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), rotation, testTexture.c_str()); });
2500 DOCTEST_CHECK(patch_id > 0);
2503 bool has_transparency = ctx.primitiveTextureHasTransparencyChannel(patch_id);
2504 DOCTEST_CHECK((has_transparency || !has_transparency));
2507 std::filesystem::remove(testTexturePath);
2508 std::filesystem::remove(
"test_models");
2512 SUBCASE(
"Material System - Label-Based Creation") {
2516 DOCTEST_CHECK(ctx.doesMaterialExist(
"__default__"));
2517 DOCTEST_CHECK(ctx.getMaterialCount() == 0);
2520 ctx.addMaterial(
"leaf_material");
2521 DOCTEST_CHECK(ctx.doesMaterialExist(
"leaf_material"));
2522 DOCTEST_CHECK(ctx.getMaterialCount() == 1);
2524 ctx.addMaterial(
"bark_material");
2525 DOCTEST_CHECK(ctx.doesMaterialExist(
"bark_material"));
2526 DOCTEST_CHECK(ctx.getMaterialCount() == 2);
2529 std::vector<std::string> labels = ctx.listMaterials();
2530 DOCTEST_CHECK(labels.size() == 2);
2533 DOCTEST_CHECK_THROWS(ctx.addMaterial(
"__reserved"));
2536 SUBCASE(
"Material System - Properties") {
2540 ctx.addMaterial(
"test_mat");
2543 ctx.setMaterialColor(
"test_mat", purple);
2545 RGBAcolor color = ctx.getMaterialColor(
"test_mat");
2546 DOCTEST_CHECK(color.r == doctest::Approx(0.5f).epsilon(0.001));
2547 DOCTEST_CHECK(color.g == doctest::Approx(0.0f).epsilon(0.001));
2548 DOCTEST_CHECK(color.b == doctest::Approx(0.5f).epsilon(0.001));
2551 ctx.setMaterialTexture(
"test_mat",
"lib/images/disk_texture.png");
2552 std::string tex = ctx.getMaterialTexture(
"test_mat");
2553 DOCTEST_CHECK(tex ==
"lib/images/disk_texture.png");
2556 ctx.setMaterialTextureColorOverride(
"test_mat",
true);
2557 DOCTEST_CHECK(ctx.isMaterialTextureColorOverridden(
"test_mat"));
2559 ctx.setMaterialTextureColorOverride(
"test_mat",
false);
2560 DOCTEST_CHECK(!ctx.isMaterialTextureColorOverridden(
"test_mat"));
2563 DOCTEST_CHECK(ctx.getMaterialTwosidedFlag(
"test_mat") == 1);
2566 ctx.setMaterialTwosidedFlag(
"test_mat", 0);
2567 DOCTEST_CHECK(ctx.getMaterialTwosidedFlag(
"test_mat") == 0);
2570 ctx.setMaterialTwosidedFlag(
"test_mat", 1);
2571 DOCTEST_CHECK(ctx.getMaterialTwosidedFlag(
"test_mat") == 1);
2574 SUBCASE(
"Material System - Assignment to Primitives") {
2578 ctx.addMaterial(
"red_mat");
2586 ctx.assignMaterialToPrimitive(p1,
"red_mat");
2587 ctx.assignMaterialToPrimitive(p2,
"red_mat");
2590 DOCTEST_CHECK(ctx.getPrimitiveMaterialLabel(p1) ==
"red_mat");
2591 DOCTEST_CHECK(ctx.getPrimitiveMaterialLabel(p2) ==
"red_mat");
2594 RGBcolor c1 = ctx.getPrimitiveColor(p1);
2595 DOCTEST_CHECK(c1.r == doctest::Approx(1.0f).epsilon(0.001));
2596 DOCTEST_CHECK(c1.g == doctest::Approx(0.0f).epsilon(0.001));
2601 c1 = ctx.getPrimitiveColor(p1);
2602 RGBcolor c2 = ctx.getPrimitiveColor(p2);
2603 DOCTEST_CHECK(c1.g == doctest::Approx(1.0f).epsilon(0.001));
2604 DOCTEST_CHECK(c2.g == doctest::Approx(1.0f).epsilon(0.001));
2607 std::vector<uint> users = ctx.getPrimitivesUsingMaterial(
"red_mat");
2608 DOCTEST_CHECK(users.size() == 2);
2611 SUBCASE(
"Material System - Batch Assignment") {
2614 ctx.addMaterial(
"batch_mat");
2615 ctx.setMaterialColor(
"batch_mat",
make_RGBAcolor(0.5f, 0.5f, 0.5f, 1));
2617 std::vector<uint> UUIDs;
2618 for (
int i = 0; i < 10; i++) {
2623 ctx.assignMaterialToPrimitive(UUIDs,
"batch_mat");
2626 for (
uint uuid: UUIDs) {
2627 DOCTEST_CHECK(ctx.getPrimitiveMaterialLabel(uuid) ==
"batch_mat");
2631 SUBCASE(
"Material System - Deletion") {
2634 ctx.addMaterial(
"temp_mat");
2638 ctx.assignMaterialToPrimitive(p1,
"temp_mat");
2642 ctx.deleteMaterial(
"temp_mat");
2644 DOCTEST_CHECK(!ctx.doesMaterialExist(
"temp_mat"));
2645 DOCTEST_CHECK(ctx.getPrimitiveMaterialLabel(p1) ==
"__default__");
2648 SUBCASE(
"Material System - XML Round-Trip") {
2652 ctx.addMaterial(
"red_mat");
2655 ctx.addMaterial(
"textured_mat");
2656 ctx.setMaterialColor(
"textured_mat",
make_RGBAcolor(0, 1, 0, 1));
2657 ctx.setMaterialTexture(
"textured_mat",
"lib/images/disk_texture.png");
2660 ctx.addMaterial(
"onesided_mat");
2661 ctx.setMaterialColor(
"onesided_mat",
make_RGBAcolor(0, 0, 1, 1));
2662 ctx.setMaterialTwosidedFlag(
"onesided_mat", 0);
2670 ctx.assignMaterialToPrimitive(p1,
"red_mat");
2671 ctx.assignMaterialToPrimitive(p2,
"textured_mat");
2672 ctx.assignMaterialToPrimitive(p3,
"red_mat");
2673 ctx.assignMaterialToPrimitive(p4,
"onesided_mat");
2676 ctx.writeXML(
"test_materials.xml", {p1, p2, p3, p4},
true);
2680 std::vector<uint> loaded_UUIDs = ctx2.loadXML(
"test_materials.xml",
true);
2682 DOCTEST_CHECK(loaded_UUIDs.size() == 4);
2685 DOCTEST_CHECK(ctx2.doesMaterialExist(
"red_mat"));
2686 DOCTEST_CHECK(ctx2.doesMaterialExist(
"textured_mat"));
2687 DOCTEST_CHECK(ctx2.doesMaterialExist(
"onesided_mat"));
2689 RGBcolor loaded_color1 = ctx2.getPrimitiveColor(loaded_UUIDs[0]);
2690 DOCTEST_CHECK(loaded_color1.r == doctest::Approx(1.0f).epsilon(0.001));
2692 DOCTEST_CHECK(ctx2.getPrimitiveTextureFile(loaded_UUIDs[1]) ==
"lib/images/disk_texture.png");
2695 DOCTEST_CHECK(ctx2.getMaterialTwosidedFlag(
"red_mat") == 1);
2696 DOCTEST_CHECK(ctx2.getMaterialTwosidedFlag(
"textured_mat") == 1);
2697 DOCTEST_CHECK(ctx2.getMaterialTwosidedFlag(
"onesided_mat") == 0);
2700 std::filesystem::remove(
"test_materials.xml");
2703 SUBCASE(
"getPrimitiveTwosidedFlag helper function") {
2707 ctx.addMaterial(
"onesided_mat");
2708 ctx.setMaterialTwosidedFlag(
"onesided_mat", 0);
2710 ctx.addMaterial(
"twosided_mat");
2711 ctx.setMaterialTwosidedFlag(
"twosided_mat", 1);
2713 ctx.addMaterial(
"transparent_mat");
2714 ctx.setMaterialTwosidedFlag(
"transparent_mat", 2);
2724 ctx.assignMaterialToPrimitive(UUID_mat_onesided,
"onesided_mat");
2725 ctx.assignMaterialToPrimitive(UUID_mat_twosided,
"twosided_mat");
2726 ctx.assignMaterialToPrimitive(UUID_mat_transparent,
"transparent_mat");
2729 ctx.setPrimitiveData(UUID_prim_data,
"twosided_flag",
uint(0));
2732 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_mat_onesided) == 0);
2735 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_mat_twosided) == 1);
2738 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_mat_transparent) == 2);
2741 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_prim_data) == 0);
2744 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_default) == 1);
2747 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_default, 2) == 2);
2751 ctx.setPrimitiveData(UUID_mat_onesided,
"twosided_flag",
uint(1));
2752 DOCTEST_CHECK(ctx.getPrimitiveTwosidedFlag(UUID_mat_onesided) == 0);
2755 SUBCASE(
"Material Data - Setting and Getting with Labels") {
2759 ctx.addMaterial(
"data_mat");
2762 ctx.setMaterialData(
"data_mat",
"twosided_flag", 1u);
2763 DOCTEST_CHECK(ctx.doesMaterialDataExist(
"data_mat",
"twosided_flag"));
2764 DOCTEST_CHECK(ctx.getMaterialDataType(
"data_mat",
"twosided_flag") == HELIOS_TYPE_UINT);
2766 ctx.getMaterialData(
"data_mat",
"twosided_flag", flag_val);
2767 DOCTEST_CHECK(flag_val == 1u);
2770 ctx.setMaterialData(
"data_mat",
"test_int", -42);
2772 ctx.getMaterialData(
"data_mat",
"test_int", int_val);
2773 DOCTEST_CHECK(int_val == -42);
2776 ctx.setMaterialData(
"data_mat",
"test_float", 3.14f);
2778 ctx.getMaterialData(
"data_mat",
"test_float", float_val);
2779 DOCTEST_CHECK(float_val == doctest::Approx(3.14f).epsilon(0.001));
2783 ctx.setMaterialData(
"data_mat",
"test_vec3", test_vec);
2785 ctx.getMaterialData(
"data_mat",
"test_vec3", vec_val);
2786 DOCTEST_CHECK(vec_val.x == doctest::Approx(1.0f).epsilon(0.001));
2787 DOCTEST_CHECK(vec_val.y == doctest::Approx(2.0f).epsilon(0.001));
2788 DOCTEST_CHECK(vec_val.z == doctest::Approx(3.0f).epsilon(0.001));
2791 ctx.setMaterialData(
"data_mat",
"test_string", std::string(
"hello"));
2792 std::string str_val;
2793 ctx.getMaterialData(
"data_mat",
"test_string", str_val);
2794 DOCTEST_CHECK(str_val ==
"hello");
2797 ctx.clearMaterialData(
"data_mat",
"test_int");
2798 DOCTEST_CHECK(!ctx.doesMaterialDataExist(
"data_mat",
"test_int"));
2801 SUBCASE(
"Material Data - Fallback Helper Method") {
2805 ctx.addMaterial(
"fallback_mat");
2806 ctx.setMaterialData(
"fallback_mat",
"twosided_flag", 0u);
2810 ctx.assignMaterialToPrimitive(p1,
"fallback_mat");
2814 ctx.getDataWithMaterialFallback(p1,
"twosided_flag", flag_val);
2815 DOCTEST_CHECK(flag_val == 0u);
2819 ctx.assignMaterialToPrimitive(p2,
"fallback_mat");
2820 ctx.setPrimitiveData(p2,
"custom_data", 42);
2824 ctx.getDataWithMaterialFallback(p2,
"custom_data", custom_val);
2825 DOCTEST_CHECK(custom_val == 42);
2829 ctx.assignMaterialToPrimitive(p3,
"fallback_mat");
2832 int nonexistent_val;
2833 DOCTEST_CHECK_THROWS(ctx.getDataWithMaterialFallback(p3,
"nonexistent", nonexistent_val));
2836 SUBCASE(
"Material Data - XML Round-Trip with Labels") {
2840 ctx.addMaterial(
"data_round_trip_mat");
2841 ctx.setMaterialColor(
"data_round_trip_mat",
make_RGBAcolor(0.5f, 0.25f, 0.75f, 1));
2842 ctx.setMaterialData(
"data_round_trip_mat",
"twosided_flag", 1u);
2843 ctx.setMaterialData(
"data_round_trip_mat",
"reflectance", 0.8f);
2844 ctx.setMaterialData(
"data_round_trip_mat",
"normal",
make_vec3(0, 0, 1));
2849 ctx.assignMaterialToPrimitive(p1,
"data_round_trip_mat");
2850 ctx.assignMaterialToPrimitive(p2,
"data_round_trip_mat");
2853 ctx.writeXML(
"test_material_data.xml",
true);
2857 ctx2.loadXML(
"test_material_data.xml",
true);
2860 DOCTEST_CHECK(ctx2.doesMaterialExist(
"data_round_trip_mat"));
2862 DOCTEST_CHECK(ctx2.doesMaterialDataExist(
"data_round_trip_mat",
"twosided_flag"));
2864 ctx2.getMaterialData(
"data_round_trip_mat",
"twosided_flag", flag_val);
2865 DOCTEST_CHECK(flag_val == 1u);
2867 DOCTEST_CHECK(ctx2.doesMaterialDataExist(
"data_round_trip_mat",
"reflectance"));
2869 ctx2.getMaterialData(
"data_round_trip_mat",
"reflectance", refl_val);
2870 DOCTEST_CHECK(refl_val == doctest::Approx(0.8f).epsilon(0.001));
2872 DOCTEST_CHECK(ctx2.doesMaterialDataExist(
"data_round_trip_mat",
"normal"));
2874 ctx2.getMaterialData(
"data_round_trip_mat",
"normal", norm_val);
2875 DOCTEST_CHECK(norm_val.x == doctest::Approx(0.0f).epsilon(0.001));
2876 DOCTEST_CHECK(norm_val.y == doctest::Approx(0.0f).epsilon(0.001));
2877 DOCTEST_CHECK(norm_val.z == doctest::Approx(1.0f).epsilon(0.001));
2880 std::filesystem::remove(
"test_material_data.xml");
2883 SUBCASE(
"Material Methods - getPrimitiveMaterialID and getMaterial") {
2887 ctx.addMaterial(
"test_mat_1");
2889 uint mat1_id = ctx.getMaterialIDFromLabel(
"test_mat_1");
2891 ctx.addMaterial(
"test_mat_2");
2893 uint mat2_id = ctx.getMaterialIDFromLabel(
"test_mat_2");
2900 ctx.assignMaterialToPrimitive(p1,
"test_mat_1");
2901 ctx.assignMaterialToPrimitive(p2,
"test_mat_2");
2902 ctx.assignMaterialToPrimitive(p3,
"test_mat_1");
2905 DOCTEST_CHECK(ctx.getPrimitiveMaterialID(p1) == mat1_id);
2906 DOCTEST_CHECK(ctx.getPrimitiveMaterialID(p2) == mat2_id);
2907 DOCTEST_CHECK(ctx.getPrimitiveMaterialID(p3) == mat1_id);
2910 const Material &mat1 = ctx.getMaterial(mat1_id);
2911 DOCTEST_CHECK(mat1.label ==
"test_mat_1");
2912 DOCTEST_CHECK(mat1.color.r == doctest::Approx(1.0f));
2913 DOCTEST_CHECK(mat1.color.g == doctest::Approx(0.0f));
2914 DOCTEST_CHECK(mat1.color.b == doctest::Approx(0.0f));
2916 const Material &mat2 = ctx.getMaterial(mat2_id);
2917 DOCTEST_CHECK(mat2.label ==
"test_mat_2");
2918 DOCTEST_CHECK(mat2.color.r == doctest::Approx(0.0f));
2919 DOCTEST_CHECK(mat2.color.g == doctest::Approx(1.0f));
2920 DOCTEST_CHECK(mat2.color.b == doctest::Approx(0.0f));
2923 DOCTEST_CHECK_THROWS((
void) ctx.getMaterial(99999));
2926 SUBCASE(
"Material Methods - getMaterialIDFromLabel") {
2930 ctx.addMaterial(
"material_a");
2931 ctx.addMaterial(
"material_b");
2932 ctx.addMaterial(
"material_c");
2935 uint id_a = ctx.getMaterialIDFromLabel(
"material_a");
2936 uint id_b = ctx.getMaterialIDFromLabel(
"material_b");
2937 uint id_c = ctx.getMaterialIDFromLabel(
"material_c");
2940 DOCTEST_CHECK(id_a != id_b);
2941 DOCTEST_CHECK(id_b != id_c);
2942 DOCTEST_CHECK(id_a != id_c);
2945 DOCTEST_CHECK(ctx.getMaterialIDFromLabel(
"material_a") == id_a);
2946 DOCTEST_CHECK(ctx.getMaterialIDFromLabel(
"material_b") == id_b);
2949 DOCTEST_CHECK_THROWS((
void) ctx.getMaterialIDFromLabel(
"nonexistent_material"));
2952 SUBCASE(
"Material copy-on-write - basic color modification") {
2960 std::string mat1_before = context.getPrimitiveMaterialLabel(uuid1);
2961 std::string mat2_before = context.getPrimitiveMaterialLabel(uuid2);
2962 DOCTEST_CHECK(mat1_before == mat2_before);
2965 context.setPrimitiveColor(uuid1, RGB::blue);
2968 std::string mat1_after = context.getPrimitiveMaterialLabel(uuid1);
2969 std::string mat2_after = context.getPrimitiveMaterialLabel(uuid2);
2970 DOCTEST_CHECK(mat1_after != mat2_after);
2973 RGBcolor color1 = context.getPrimitiveColor(uuid1);
2974 RGBcolor color2 = context.getPrimitiveColor(uuid2);
2975 DOCTEST_CHECK(color1 == RGB::blue);
2976 DOCTEST_CHECK(color2 == RGB::red);
2979 SUBCASE(
"Material copy-on-write - object-level modification") {
2983 uint obj1 = context.addSphereObject(10,
make_vec3(0, 0, 0), 1.f, RGB::green);
2984 uint obj2 = context.addSphereObject(10,
make_vec3(3, 0, 0), 1.f, RGB::green);
2987 context.setObjectColor(obj1, RGB::yellow);
2990 auto prims1 = context.getObjectPrimitiveUUIDs(obj1);
2991 auto prims2 = context.getObjectPrimitiveUUIDs(obj2);
2993 RGBcolor color1 = context.getPrimitiveColor(prims1[0]);
2994 RGBcolor color2 = context.getPrimitiveColor(prims2[0]);
2996 DOCTEST_CHECK(color1 == RGB::yellow);
2997 DOCTEST_CHECK(color2 == RGB::green);
3000 SUBCASE(
"Material copy-on-write - non-shared optimization") {
3006 std::string mat1 = context.getPrimitiveMaterialLabel(uuid);
3009 context.setPrimitiveColor(uuid, RGB::magenta);
3011 std::string mat2 = context.getPrimitiveMaterialLabel(uuid);
3014 DOCTEST_CHECK(mat1 == mat2);