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") {
67 capture_cerr cerr_buffer;
68 DOCTEST_CHECK_NOTHROW(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/solid.jpg"));
69 DOCTEST_CHECK_THROWS(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/missing.png"));
70 DOCTEST_CHECK_THROWS(ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), nullrotation,
"lib/images/invalid.txt"));
72 Texture tex(
"lib/images/solid.jpg");
73 DOCTEST_CHECK(tex.getTextureFile() ==
"lib/images/solid.jpg");
74 int2 res = tex.getImageResolution();
75 DOCTEST_CHECK(res.x == 5);
76 DOCTEST_CHECK(res.y == 5);
77 DOCTEST_CHECK(!tex.hasTransparencyChannel());
78 const auto *alpha = tex.getTransparencyData();
79 DOCTEST_CHECK(alpha->empty());
80 std::vector<vec2> uv{{0.f, 0.f}, {1.f, 0.f}, {1.f, 1.f}};
81 float sf = tex.getSolidFraction(uv);
82 DOCTEST_CHECK(sf == doctest::Approx(1.f));
85 SUBCASE(
"Geometry dirty flags") {
87 uint p = ctx.addPatch();
88 DOCTEST_CHECK(ctx.isGeometryDirty());
89 DOCTEST_CHECK(ctx.isPrimitiveDirty(p));
91 ctx.markGeometryClean();
92 DOCTEST_CHECK(!ctx.isGeometryDirty());
93 DOCTEST_CHECK(!ctx.isPrimitiveDirty(p));
95 ctx.markPrimitiveDirty(p);
96 DOCTEST_CHECK(ctx.isGeometryDirty());
97 DOCTEST_CHECK(ctx.isPrimitiveDirty(p));
99 ctx.markPrimitiveClean(p);
100 DOCTEST_CHECK(!ctx.isGeometryDirty());
101 DOCTEST_CHECK(!ctx.isPrimitiveDirty(p));
103 ctx.markGeometryDirty();
104 DOCTEST_CHECK(ctx.isGeometryDirty());
107 SUBCASE(
"Geometry dirty flags vector") {
109 std::vector<uint> ids{ctx.addPatch(), ctx.addPatch()};
110 ctx.markGeometryClean();
111 ctx.markPrimitiveDirty(ids);
113 DOCTEST_CHECK(ctx.isPrimitiveDirty(
id));
115 ctx.markPrimitiveClean(ids);
117 DOCTEST_CHECK(!ctx.isPrimitiveDirty(
id));
121 ctx.translatePrimitive(ids, shift);
123 vec3 c = ctx.getPatchCenter(
id);
124 DOCTEST_CHECK(c.x == doctest::Approx(shift.x).epsilon(errtol));
128 SUBCASE(
"Date and Time Manipulation") {
130 ctx.setDate(15, 7, 2025);
131 Date d = ctx.getDate();
132 DOCTEST_CHECK(d.day == 15);
133 DOCTEST_CHECK(d.month == 7);
134 DOCTEST_CHECK(d.year == 2025);
135 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"JUL") == 0);
136 DOCTEST_CHECK(ctx.getJulianDate() == 196);
138 ctx.setTime(45, 30, 10);
139 Time t = ctx.getTime();
140 DOCTEST_CHECK(t.hour == 10);
141 DOCTEST_CHECK(t.minute == 30);
142 DOCTEST_CHECK(t.second == 45);
144 capture_cerr cerr_buffer;
145 DOCTEST_CHECK_THROWS(ctx.setDate(32, 1, 2025));
146 DOCTEST_CHECK_THROWS(ctx.setTime(60, 0, 0));
149 SUBCASE(
"Location Manipulation") {
151 Location loc = {40.7128, -74.0060, 10.0};
152 ctx.setLocation(loc);
153 Location l = ctx.getLocation();
154 DOCTEST_CHECK(l.latitude_deg == doctest::Approx(40.7128));
155 DOCTEST_CHECK(l.longitude_deg == doctest::Approx(-74.0060));
156 DOCTEST_CHECK(l.UTC_offset == doctest::Approx(10.0));
159 SUBCASE(
"primitive orientation and transforms") {
162 ctx.markGeometryClean();
164 vec3 n = ctx.getPrimitiveNormal(
id);
165 DOCTEST_CHECK(n == vec3(0.f, 0.f, 1.f));
167 ctx.setPrimitiveElevation(
id,
make_vec3(0, 0, 0), 0.f);
168 n = ctx.getPrimitiveNormal(
id);
169 DOCTEST_CHECK(n.x == doctest::Approx(0.f).epsilon(errtol));
170 DOCTEST_CHECK(n.y == doctest::Approx(1.f).epsilon(errtol));
171 DOCTEST_CHECK(n.z == doctest::Approx(0.f).epsilon(errtol));
173 ctx.setPrimitiveAzimuth(
id,
make_vec3(0, 0, 0), 0.5f *
PI_F);
174 n = ctx.getPrimitiveNormal(
id);
175 DOCTEST_CHECK(n.x == doctest::Approx(1.f).epsilon(errtol));
176 DOCTEST_CHECK(n.y == doctest::Approx(0.f).epsilon(errtol));
179 n = ctx.getPrimitiveNormal(
id);
180 DOCTEST_CHECK(n.z == doctest::Approx(1.f).epsilon(errtol));
184 ctx.setPrimitiveTransformationMatrix(
id, M);
186 ctx.getPrimitiveTransformationMatrix(
id, out);
187 for (
int i = 0; i < 16; ++i) {
188 DOCTEST_CHECK(out[i] == doctest::Approx(M[i]));
190 DOCTEST_CHECK(ctx.isPrimitiveDirty(
id));
194TEST_CASE(
"Primitive Management: Creation, Properties, and Operations") {
195 SUBCASE(
"addPatch") {
196 vec3 center, center_r;
198 std::vector<vec3> vertices, vertices_r;
199 SphericalCoord rotation, rotation_r;
200 vec3 normal, normal_r;
201 RGBcolor color, color_r;
203 std::vector<uint> UUIDs;
208 Context context_test;
214 vertices.at(0) = center +
make_vec3(-0.5f * size.x, -0.5f * size.y, 0.f);
215 vertices.at(1) = center +
make_vec3(0.5f * size.x, -0.5f * size.y, 0.f);
216 vertices.at(2) = center +
make_vec3(0.5f * size.x, 0.5f * size.y, 0.f);
217 vertices.at(3) = center +
make_vec3(-0.5f * size.x, 0.5f * size.y, 0.f);
219 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
220 DOCTEST_CHECK_NOTHROW(type = context_test.getPrimitiveType(UUID));
221 DOCTEST_CHECK_NOTHROW(center_r = context_test.getPatchCenter(UUID));
222 DOCTEST_CHECK_NOTHROW(size_r = context_test.getPatchSize(UUID));
223 DOCTEST_CHECK_NOTHROW(normal_r = context_test.getPrimitiveNormal(UUID));
224 DOCTEST_CHECK_NOTHROW(vertices_r = context_test.getPrimitiveVertices(UUID));
225 DOCTEST_CHECK_NOTHROW(area_r = context_test.getPrimitiveArea(UUID));
226 DOCTEST_CHECK_NOTHROW(color_r = context_test.getPrimitiveColor(UUID));
228 DOCTEST_CHECK(type == PRIMITIVE_TYPE_PATCH);
229 DOCTEST_CHECK(center_r.x == center.x);
230 DOCTEST_CHECK(center_r.y == center.y);
231 DOCTEST_CHECK(center_r.z == center.z);
232 DOCTEST_CHECK(size_r.x == size.x);
233 DOCTEST_CHECK(size_r.y == size.y);
234 DOCTEST_CHECK(normal_r.x == 0.f);
235 DOCTEST_CHECK(normal_r.y == 0.f);
236 DOCTEST_CHECK(normal_r.z == 1.f);
237 DOCTEST_CHECK(vertices_r.size() == 4);
238 DOCTEST_CHECK(vertices_r.at(0).x == vertices.at(0).x);
239 DOCTEST_CHECK(vertices_r.at(0).y == vertices.at(0).y);
240 DOCTEST_CHECK(vertices_r.at(0).z == vertices.at(0).z);
241 DOCTEST_CHECK(vertices_r.at(1).x == vertices.at(1).x);
242 DOCTEST_CHECK(vertices_r.at(1).y == vertices.at(1).y);
243 DOCTEST_CHECK(vertices_r.at(1).z == vertices.at(1).z);
244 DOCTEST_CHECK(vertices_r.at(2).x == vertices.at(2).x);
245 DOCTEST_CHECK(vertices_r.at(2).y == vertices.at(2).y);
246 DOCTEST_CHECK(vertices_r.at(2).z == vertices.at(2).z);
247 DOCTEST_CHECK(vertices_r.at(3).x == vertices.at(3).x);
248 DOCTEST_CHECK(vertices_r.at(3).y == vertices.at(3).y);
249 DOCTEST_CHECK(vertices_r.at(3).z == vertices.at(3).z);
250 CHECK(area_r == doctest::Approx(size.x * size.y).epsilon(errtol));
251 DOCTEST_CHECK(color_r.r == 0.f);
252 DOCTEST_CHECK(color_r.g == 0.f);
253 DOCTEST_CHECK(color_r.b == 0.f);
254 DOCTEST_CHECK(context_test.getPrimitiveTextureFile(UUID).empty());
256 SUBCASE(
"rotated patch") {
257 Context context_test;
262 rotation.azimuth = 0.5f *
PI_F;
265 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size, rotation));
268 DOCTEST_CHECK_NOTHROW(normal_r = context_test.getPrimitiveNormal(UUID));
270 SphericalCoord rotation_r;
271 DOCTEST_CHECK_NOTHROW(rotation_r =
make_SphericalCoord(0.5f *
PI_F - asinf(normal_r.z), atan2f(normal_r.x, normal_r.y)));
273 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
275 DOCTEST_CHECK(rotation_r.elevation == doctest::Approx(rotation.elevation).epsilon(errtol));
276 DOCTEST_CHECK(rotation_r.azimuth == doctest::Approx(rotation.azimuth).epsilon(errtol));
278 SUBCASE(
"addTriangle") {
279 Context context_test;
290 std::vector<vec3> vertices{v0, v1, v2};
291 RGBcolor color = RGB::red;
293 DOCTEST_CHECK_NOTHROW(UUID = context_test.addTriangle(v0, v1, v2, color));
294 DOCTEST_CHECK(context_test.getPrimitiveType(UUID) == PRIMITIVE_TYPE_TRIANGLE);
296 vec3 normal = normalize(
cross(v1 - v0, v2 - v1));
297 vec3 normal_r = context_test.getPrimitiveNormal(UUID);
298 DOCTEST_CHECK(normal_r.x == doctest::Approx(normal.x).epsilon(errtol));
299 DOCTEST_CHECK(normal_r.y == doctest::Approx(normal.y).epsilon(errtol));
300 DOCTEST_CHECK(normal_r.z == doctest::Approx(normal.z).epsilon(errtol));
302 std::vector<vec3> vertices_r;
303 DOCTEST_CHECK_NOTHROW(vertices_r = context_test.getPrimitiveVertices(UUID));
304 DOCTEST_CHECK(vertices_r.size() == 3);
305 DOCTEST_CHECK(vertices_r.at(0).x == v0.x);
306 DOCTEST_CHECK(vertices_r.at(0).y == v0.y);
307 DOCTEST_CHECK(vertices_r.at(0).z == v0.z);
310 DOCTEST_CHECK_NOTHROW(color_r = context_test.getPrimitiveColor(UUID));
311 DOCTEST_CHECK(color_r.r == color.r);
312 DOCTEST_CHECK(color_r.g == color.g);
313 DOCTEST_CHECK(color_r.b == color.b);
314 DOCTEST_CHECK(context_test.getPrimitiveTextureFile(UUID).empty());
316 float a = (v1 - v0).magnitude();
317 float b = (v2 - v0).magnitude();
318 float c = (v2 - v1).magnitude();
319 float s = 0.5f * (a + b + c);
320 float area = sqrtf(s * (s - a) * (s - b) * (s - c));
322 DOCTEST_CHECK_NOTHROW(area_r = context_test.getPrimitiveArea(UUID));
323 DOCTEST_CHECK(area_r == doctest::Approx(area).epsilon(errtol));
325 SUBCASE(
"copyPrimitive (patch)") {
326 Context context_test;
329 std::vector<float> cpdata{5.2f, 2.5f, 3.1f};
334 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
336 DOCTEST_CHECK_NOTHROW(context_test.setPrimitiveData(UUID,
"somedata", cpdata));
338 DOCTEST_CHECK_NOTHROW(UUID_cpy = context_test.copyPrimitive(UUID));
341 DOCTEST_CHECK_NOTHROW(center_cpy = context_test.getPatchCenter(UUID_cpy));
343 DOCTEST_CHECK_NOTHROW(size_cpy = context_test.getPatchSize(UUID_cpy));
345 DOCTEST_CHECK(UUID_cpy == 1);
346 DOCTEST_CHECK(center_cpy.x == center.x);
347 DOCTEST_CHECK(center_cpy.y == center.y);
348 DOCTEST_CHECK(center_cpy.z == center.z);
349 DOCTEST_CHECK(size_cpy.x == size.x);
350 DOCTEST_CHECK(size_cpy.y == size.y);
352 std::vector<float> cpdata_copy;
353 context_test.getPrimitiveData(UUID_cpy,
"somedata", cpdata_copy);
355 DOCTEST_CHECK(cpdata.size() == cpdata_copy.size());
356 for (
uint i = 0; i < cpdata.size(); i++) {
357 DOCTEST_CHECK(cpdata.at(i) == cpdata_copy.at(i));
362 DOCTEST_CHECK_NOTHROW(context_test.translatePrimitive(UUID_cpy, shift));
363 DOCTEST_CHECK_NOTHROW(center_cpy = context_test.getPatchCenter(UUID_cpy));
365 DOCTEST_CHECK_NOTHROW(center_r = context_test.getPatchCenter(UUID));
367 DOCTEST_CHECK(center_cpy.x == doctest::Approx(center.x + shift.x).epsilon(errtol));
368 DOCTEST_CHECK(center_cpy.y == doctest::Approx(center.y + shift.y).epsilon(errtol));
369 DOCTEST_CHECK(center_cpy.z == doctest::Approx(center.z + shift.z).epsilon(errtol));
370 DOCTEST_CHECK(center_r.x == center.x);
371 DOCTEST_CHECK(center_r.y == center.y);
372 DOCTEST_CHECK(center_r.z == center.z);
374 SUBCASE(
"copyPrimitive (triangle)") {
375 Context context_test;
382 DOCTEST_CHECK_NOTHROW(UUID = context_test.addTriangle(v0, v1, v2, RGB::blue));
383 DOCTEST_CHECK_NOTHROW(UUID_cpy = context_test.copyPrimitive(UUID));
385 std::vector<vec3> verts_org, verts_cpy;
386 DOCTEST_CHECK_NOTHROW(verts_org = context_test.getPrimitiveVertices(UUID));
387 DOCTEST_CHECK_NOTHROW(verts_cpy = context_test.getPrimitiveVertices(UUID_cpy));
388 DOCTEST_CHECK(verts_org == verts_cpy);
391 DOCTEST_CHECK_NOTHROW(context_test.translatePrimitive(UUID_cpy, shift));
392 DOCTEST_CHECK_NOTHROW(verts_cpy = context_test.getPrimitiveVertices(UUID_cpy));
393 DOCTEST_CHECK(verts_cpy.at(0) == verts_org.at(0) + shift);
394 DOCTEST_CHECK(verts_cpy.at(1) == verts_org.at(1) + shift);
395 DOCTEST_CHECK(verts_cpy.at(2) == verts_org.at(2) + shift);
397 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
398 DOCTEST_CHECK(!context_test.doesPrimitiveExist(UUID));
400 SUBCASE(
"deletePrimitive") {
401 Context context_test;
406 DOCTEST_CHECK_NOTHROW(UUID = context_test.addPatch(center, size));
408 DOCTEST_CHECK_NOTHROW(context_test.deletePrimitive(UUID));
410 uint primitive_count;
411 DOCTEST_CHECK_NOTHROW(primitive_count = context_test.getPrimitiveCount(UUID));
412 DOCTEST_CHECK(primitive_count == 0);
413 DOCTEST_CHECK(!context_test.doesPrimitiveExist(UUID));
415 SUBCASE(
"primitive bounding box") {
416 Context context_test;
417 std::vector<uint> UUIDs;
422 DOCTEST_CHECK_NOTHROW(context_test.getPrimitiveBoundingBox(UUIDs, bmin, bmax));
423 DOCTEST_CHECK(bmin ==
make_vec3(-1.25f, -0.25f, 0.f));
424 DOCTEST_CHECK(bmax ==
make_vec3(1.25f, 0.25f, 0.f));
426 SUBCASE(
"primitive scale and data") {
427 Context context_test;
429 float area0 = sz_0.x * sz_0.y;
432 context_test.scalePrimitive(UUID,
make_vec3(scale, scale, scale));
433 float area1 = context_test.getPrimitiveArea(UUID);
434 DOCTEST_CHECK(area1 == doctest::Approx(scale * scale * area0).epsilon(1e-5));
437 context_test.setPrimitiveData(UUID,
"some_data", data);
438 DOCTEST_CHECK(context_test.doesPrimitiveDataExist(UUID,
"some_data"));
440 context_test.getPrimitiveData(UUID,
"some_data", data_r);
441 DOCTEST_CHECK(data_r == data);
443 std::vector<float> vec = {0, 1, 2, 3, 4};
444 context_test.setPrimitiveData(UUID,
"vec_data", vec);
445 std::vector<float> vec_r;
446 context_test.getPrimitiveData(UUID,
"vec_data", vec_r);
447 DOCTEST_CHECK(vec_r == vec);
449 std::vector<uint> UUIDs_filter;
450 std::vector<uint> UUIDs_multi;
451 for (
uint i = 0; i < 4; i++) {
452 UUIDs_multi.push_back(context_test.addPatch());
454 context_test.setPrimitiveData(UUIDs_multi[0],
"val", 4.f);
455 context_test.setPrimitiveData(UUIDs_multi[0],
"str",
"cat");
456 context_test.setPrimitiveData(UUIDs_multi[1],
"val", 3.f);
457 context_test.setPrimitiveData(UUIDs_multi[1],
"str",
"cat");
458 context_test.setPrimitiveData(UUIDs_multi[2],
"val", 2.f);
459 context_test.setPrimitiveData(UUIDs_multi[2],
"str",
"dog");
460 context_test.setPrimitiveData(UUIDs_multi[3],
"val", 1.f);
461 context_test.setPrimitiveData(UUIDs_multi[3],
"str",
"dog");
463 UUIDs_filter = context_test.filterPrimitivesByData(UUIDs_multi,
"val", 2.f,
"<=");
464 DOCTEST_CHECK(UUIDs_filter.size() == 2);
465 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[2]) != UUIDs_filter.end());
466 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[3]) != UUIDs_filter.end());
468 UUIDs_filter = context_test.filterPrimitivesByData(UUIDs_multi,
"str",
"cat");
469 DOCTEST_CHECK(UUIDs_filter.size() == 2);
470 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[0]) != UUIDs_filter.end());
471 DOCTEST_CHECK(std::find(UUIDs_filter.begin(), UUIDs_filter.end(), UUIDs_multi[1]) != UUIDs_filter.end());
473 SUBCASE(
"texture uv and solid fraction") {
474 Context context_test;
477 const char *texture =
"lib/images/disk_texture.png";
482 uint UUIDp = context_test.addPatch(
make_vec3(2, 3, 4), sizep, nullrotation, texture, 0.5f * (uv0 + uv2), uv2 - uv0);
483 DOCTEST_CHECK(!context_test.getPrimitiveTextureFile(UUIDp).empty());
484 float Ap = context_test.getPrimitiveArea(UUIDp);
485 DOCTEST_CHECK(Ap == doctest::Approx(0.25f *
PI_F * sizep.x * sizep.y).epsilon(0.01));
486 std::vector<vec2> uv = context_test.getPrimitiveTextureUV(UUIDp);
487 DOCTEST_CHECK(uv.size() == 4);
488 DOCTEST_CHECK(uv.at(0) == uv0);
489 DOCTEST_CHECK(uv.at(1) == uv1);
490 DOCTEST_CHECK(uv.at(2) == uv2);
491 DOCTEST_CHECK(uv.at(3) == uv3);
493 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));
494 float solid_fraction = context_test.getPrimitiveSolidFraction(UUIDt);
495 DOCTEST_CHECK(solid_fraction == doctest::Approx(0.5f).epsilon(errtol));
498 SUBCASE(
"advanced primitive transforms") {
502 std::vector<uint> ids{p1, p2};
503 ctx.markGeometryClean();
505 ctx.rotatePrimitive(p1, 0.5f *
PI_F,
"x");
506 vec3 n = ctx.getPrimitiveNormal(p1);
507 DOCTEST_CHECK(n.y == doctest::Approx(-1.f).epsilon(errtol));
508 DOCTEST_CHECK(n.z == doctest::Approx(0.f).epsilon(errtol));
511 vec3 c = ctx.getPatchCenter(p2);
512 DOCTEST_CHECK(c.x == doctest::Approx(-1.f).epsilon(errtol));
517 vec2 sz = ctx.getPatchSize(p2);
518 DOCTEST_CHECK(sz.x == doctest::Approx(2.f).epsilon(errtol));
521 sz = ctx.getPatchSize(p2);
522 DOCTEST_CHECK(sz.x == doctest::Approx(1.f).epsilon(errtol));
526TEST_CASE(
"Object Management") {
527 SUBCASE(
"addBoxObject") {
528 Context context_test;
532 int3 subdiv(1, 1, 1);
535 DOCTEST_CHECK_NOTHROW(objID = context_test.addBoxObject(center, size, subdiv));
536 std::vector<uint> UUIDs = context_test.getObjectPointer(objID)->getPrimitiveUUIDs();
538 DOCTEST_CHECK(UUIDs.size() == 6);
539 vec3 normal_r = context_test.getPrimitiveNormal(UUIDs.at(0));
540 DOCTEST_CHECK(doctest::Approx(normal_r.magnitude()).epsilon(errtol) == 1.f);
541 normal_r = context_test.getPrimitiveNormal(UUIDs.at(2));
542 DOCTEST_CHECK(doctest::Approx(normal_r.magnitude()).epsilon(errtol) == 1.f);
544 vec2 size_r = context_test.getPatchSize(UUIDs.at(0));
545 DOCTEST_CHECK(size_r.x == doctest::Approx(size.x).epsilon(errtol));
546 DOCTEST_CHECK(size_r.y == doctest::Approx(size.z).epsilon(errtol));
548 size_r = context_test.getPatchSize(UUIDs.at(2));
549 DOCTEST_CHECK(size_r.x == doctest::Approx(size.y).epsilon(errtol));
550 DOCTEST_CHECK(size_r.y == doctest::Approx(size.z).epsilon(errtol));
552 float volume = context_test.getBoxObjectVolume(objID);
553 DOCTEST_CHECK(volume == doctest::Approx(size.x * size.y * size.z).epsilon(errtol));
555 SUBCASE(
"addTileObject rotated") {
556 Context context_test;
562 uint objID = context_test.addTileObject(center, size, rotation, subdiv);
564 std::vector<uint> UUIDs = context_test.getObjectPointer(objID)->getPrimitiveUUIDs();
565 for (
uint UUIDp: UUIDs) {
566 vec3 n = context_test.getPrimitiveNormal(UUIDp);
568 DOCTEST_CHECK(rot.zenith == doctest::Approx(rotation.zenith).epsilon(errtol));
569 DOCTEST_CHECK(rot.azimuth == doctest::Approx(rotation.azimuth).epsilon(errtol));
572 SUBCASE(
"textured tile area") {
573 Context context_test;
580 uint objID = context_test.addTileObject(center, size, rotation, subdiv,
"lib/images/disk_texture.png");
581 std::vector<uint> UUIDs = context_test.getObjectPointer(objID)->getPrimitiveUUIDs();
582 float area_sum = 0.f;
583 for (
uint UUID: UUIDs) {
584 area_sum += context_test.getPrimitiveArea(UUID);
586 float area_exact = 0.25f *
PI_F * size.x * size.y;
587 DOCTEST_CHECK(area_sum == doctest::Approx(area_exact).epsilon(5e-3));
589 SUBCASE(
"cone object transforms") {
590 Context context_test;
591 float r0 = 0.5f, r1 = 1.f, len = 2.f;
594 uint cone = context_test.addConeObject(50, node0, node1, r0, r1);
595 context_test.getConeObjectPointer(cone)->translate(
make_vec3(1, 1, 1));
596 std::vector<vec3> nodes = context_test.getConeObjectPointer(cone)->getNodeCoordinates();
597 DOCTEST_CHECK(nodes.at(0) ==
make_vec3(1, 1, 1));
598 DOCTEST_CHECK(nodes.at(1) ==
make_vec3(1, 1, 1 + len));
601 context_test.getConeObjectPointer(cone)->translate(-nodes.at(0));
602 context_test.getConeObjectPointer(cone)->rotate(ang, axis);
603 context_test.getConeObjectPointer(cone)->translate(nodes.at(0));
604 nodes = context_test.getConeObjectPointer(cone)->getNodeCoordinates();
605 DOCTEST_CHECK(nodes.at(1).x == doctest::Approx(nodes.at(0).x + len).epsilon(errtol));
606 context_test.getConeObjectPointer(cone)->scaleLength(2.0);
607 nodes = context_test.getConeObjectPointer(cone)->getNodeCoordinates();
608 DOCTEST_CHECK(nodes.at(1).x == doctest::Approx(nodes.at(0).x + 2 * len).epsilon(errtol));
609 context_test.getConeObjectPointer(cone)->scaleGirth(2.0);
610 std::vector<float> radii = context_test.getConeObjectPointer(cone)->getNodeRadii();
611 DOCTEST_CHECK(radii.at(0) == doctest::Approx(2 * r0).epsilon(errtol));
612 DOCTEST_CHECK(radii.at(1) == doctest::Approx(2 * r1).epsilon(errtol));
615 SUBCASE(
"rotate and scale objects") {
618 ctx.rotateObject(
obj, 0.5f *
PI_F,
"z");
620 ctx.getObjectBoundingBox(
obj, bmin, bmax);
621 DOCTEST_CHECK(bmax.x == doctest::Approx(0.5f).epsilon(errtol));
624 ctx.getObjectBoundingBox(
obj, bmin, bmax);
625 DOCTEST_CHECK(bmax.x > 0.5f);
628 SUBCASE(
"domain bounding sphere") {
630 std::vector<uint> ids;
635 ctx.getDomainBoundingSphere(ids, c, r);
636 DOCTEST_CHECK(c.x == doctest::Approx(0.f).epsilon(errtol));
637 DOCTEST_CHECK(r > 1.f);
640 SUBCASE(
"copy and delete objects") {
643 uint obj2 = ctx.copyObject(obj1);
644 DOCTEST_CHECK(ctx.doesObjectExist(obj1));
645 DOCTEST_CHECK(ctx.doesObjectExist(obj2));
646 ctx.deleteObject(obj2);
647 DOCTEST_CHECK(!ctx.doesObjectExist(obj2));
650 SUBCASE(
"domain cropping") {
651 capture_cerr cerr_buffer;
658 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p1));
660 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p3));
662 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p4));
663 DOCTEST_CHECK(cerr_buffer.has_output());
665 std::vector<uint> ids_rem = ctx.getAllUUIDs();
667 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p2));
668 DOCTEST_CHECK(cerr_buffer.has_output());
672TEST_CASE(
"Data Management") {
673 SUBCASE(
"global and object data") {
674 Context context_test;
676 context_test.setGlobalData(
"some_data", gdata);
678 DOCTEST_CHECK(context_test.doesGlobalDataExist(
"some_data"));
679 context_test.getGlobalData(
"some_data", gdata_r);
680 DOCTEST_CHECK(gdata_r == gdata);
682 std::vector<float> gvec{0, 1, 2, 3, 4};
683 context_test.setGlobalData(
"vec", gvec);
684 std::vector<float> gvec_r;
685 context_test.getGlobalData(
"vec", gvec_r);
686 DOCTEST_CHECK(gvec_r == gvec);
690 context_test.setObjectData(objID,
"obj", objdata);
692 context_test.getObjectData(objID,
"obj", objdata_r);
693 DOCTEST_CHECK(objdata_r == objdata);
695 SUBCASE(
"timeseries") {
701 Time time1 =
make_Time(time0.hour, 49, 14);
702 ctx.addTimeseriesData(
"ts", 302.3f, date, time0);
703 ctx.addTimeseriesData(
"ts", 305.3f, date, time1);
704 ctx.setCurrentTimeseriesPoint(
"ts", 0);
705 DOCTEST_CHECK(ctx.getTimeseriesLength(
"ts") == 2);
706 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", 0) == doctest::Approx(302.3f));
707 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", 1) == doctest::Approx(305.3f));
708 float val = ctx.queryTimeseriesData(
"ts", date, time1);
709 DOCTEST_CHECK(val == doctest::Approx(305.3f));
710 DOCTEST_CHECK(ctx.doesTimeseriesVariableExist(
"ts"));
711 std::vector<std::string> labels = ctx.listTimeseriesVariables();
712 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"ts") != labels.end());
713 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts", ctx.getTimeseriesLength(
"ts") - 1) == doctest::Approx(305.3f));
714 Time t1_r = ctx.queryTimeseriesTime(
"ts", 1);
715 Date d1_r = ctx.queryTimeseriesDate(
"ts", 1);
716 DOCTEST_CHECK(t1_r.minute == time1.minute);
717 DOCTEST_CHECK(d1_r.day == date.day);
718 ctx.setCurrentTimeseriesPoint(
"ts", 1);
719 DOCTEST_CHECK(ctx.queryTimeseriesData(
"ts") == doctest::Approx(305.3f));
722 SUBCASE(
"Primitive data") {
723 capture_cerr cerr_buffer;
726 uint p = ctx.addPatch();
727 ctx.setPrimitiveData(p,
"test_int", 5);
728 ctx.setPrimitiveData(p,
"test_float", 3.14f);
731 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_int") == HELIOS_TYPE_INT);
732 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
735 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_int") == 1);
738 ctx.clearPrimitiveData(p,
"test_int");
739 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p,
"test_int"));
742 std::vector<std::string> data_labels = ctx.listPrimitiveData(p);
743 DOCTEST_CHECK(std::find(data_labels.begin(), data_labels.end(),
"test_float") != data_labels.end());
746 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataSize(p,
"test_int"));
749 ctx.clearPrimitiveData(p,
"test_int");
750 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p,
"test_int"));
753 ctx.setPrimitiveData(p,
"test_int", 5);
754 ctx.setPrimitiveData(p,
"test_float", 3.14f);
755 std::vector<std::string> labels = ctx.listPrimitiveData(p);
756 DOCTEST_CHECK(labels.size() == 2);
757 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"test_int") != labels.end());
758 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"test_float") != labels.end());
759 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
763TEST_CASE(
"Data and Object Management") {
765 SUBCASE(
"Global data management") {
767 ctx.setGlobalData(
"test_double", 1.23);
768 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_double") == 1);
769 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_double") == HELIOS_TYPE_DOUBLE);
770 ctx.clearGlobalData(
"test_double");
771 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"test_double"));
772 ctx.setGlobalData(
"test_string",
"hello");
773 std::vector<std::string> global_data_labels = ctx.listGlobalData();
774 DOCTEST_CHECK(std::find(global_data_labels.begin(), global_data_labels.end(),
"test_string") != global_data_labels.end());
777 SUBCASE(
"Object data management") {
780 ctx.setObjectData(
obj,
"test_vec", vec3(1, 2, 3));
781 DOCTEST_CHECK(ctx.getObjectDataSize(
obj,
"test_vec") == 1);
782 DOCTEST_CHECK(ctx.getObjectDataType(
"test_vec") == HELIOS_TYPE_VEC3);
783 ctx.clearObjectData(
obj,
"test_vec");
784 DOCTEST_CHECK(!ctx.doesObjectDataExist(
obj,
"test_vec"));
785 ctx.setObjectData(
obj,
"test_int", 42);
786 std::vector<std::string> object_data_labels = ctx.listObjectData(
obj);
787 DOCTEST_CHECK(std::find(object_data_labels.begin(), object_data_labels.end(),
"test_int") != object_data_labels.end());
790 SUBCASE(
"Object creation and manipulation") {
793 DOCTEST_CHECK(ctx.getDiskObjectPointer(disk)->getObjectType() == OBJECT_TYPE_DISK);
794 DOCTEST_CHECK(ctx.getObjectArea(disk) > 0);
795 DOCTEST_CHECK(ctx.getDiskObjectCenter(disk) ==
make_vec3(0, 0, 0));
796 DOCTEST_CHECK(ctx.getDiskObjectSubdivisionCount(disk) == 10);
797 DOCTEST_CHECK(ctx.getDiskObjectSize(disk).x == doctest::Approx(1.f));
799 uint sphere = ctx.addSphereObject(10,
make_vec3(1, 1, 1), 0.5f);
800 DOCTEST_CHECK(ctx.getSphereObjectPointer(sphere)->getObjectType() == OBJECT_TYPE_SPHERE);
801 DOCTEST_CHECK(ctx.getObjectArea(sphere) > 0);
802 DOCTEST_CHECK(ctx.getSphereObjectCenter(sphere) ==
make_vec3(1, 1, 1));
803 DOCTEST_CHECK(ctx.getSphereObjectSubdivisionCount(sphere) == 10);
804 DOCTEST_CHECK(ctx.getSphereObjectRadius(sphere).x == doctest::Approx(0.5f));
806 std::vector<uint> p_uuids;
808 uint polymesh = ctx.addPolymeshObject(p_uuids);
809 DOCTEST_CHECK(ctx.getPolymeshObjectPointer(polymesh)->getObjectType() == OBJECT_TYPE_POLYMESH);
810 DOCTEST_CHECK(ctx.getObjectArea(polymesh) > 0);
811 DOCTEST_CHECK(ctx.getObjectCenter(polymesh).z == doctest::Approx(0.f));
814 std::vector<float> radii = {0.2f, 0.1f};
815 uint tube = ctx.addTubeObject(10, nodes, radii);
816 DOCTEST_CHECK(ctx.getTubeObjectPointer(tube)->getObjectType() == OBJECT_TYPE_TUBE);
817 DOCTEST_CHECK(ctx.getObjectArea(tube) > 0);
818 DOCTEST_CHECK(ctx.getObjectCenter(tube).z == doctest::Approx(0.5f));
819 DOCTEST_CHECK(ctx.getTubeObjectSubdivisionCount(tube) == 10);
820 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 2);
821 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube).size() == 2);
822 DOCTEST_CHECK(ctx.getTubeObjectNodeColors(tube).size() == 2);
823 DOCTEST_CHECK(ctx.getTubeObjectVolume(tube) > 0);
824 ctx.appendTubeSegment(tube,
make_vec3(0, 0, 2), 0.05f, RGB::red);
825 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 3);
826 ctx.scaleTubeGirth(tube, 2.f);
827 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube)[0] == doctest::Approx(0.4f));
828 std::vector<float> new_radii = {0.3f, 0.2f, 0.1f};
829 ctx.setTubeRadii(tube, new_radii);
830 DOCTEST_CHECK(ctx.getTubeObjectNodeRadii(tube)[0] == doctest::Approx(0.3f));
831 ctx.scaleTubeLength(tube, 2.f);
833 ctx.setTubeNodes(tube, new_nodes);
834 ctx.pruneTubeNodes(tube, 1);
835 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 1);
838 SUBCASE(
"Object appearance and visibility") {
841 ctx.overrideObjectTextureColor(box);
843 ctx.useObjectTextureColor(box);
846 DOCTEST_CHECK(ctx.isObjectHidden(box));
848 DOCTEST_CHECK(!ctx.isObjectHidden(box));
850 std::vector<uint> prims = ctx.getObjectPrimitiveUUIDs(box);
851 ctx.hidePrimitive(prims);
852 DOCTEST_CHECK(ctx.isPrimitiveHidden(prims[0]));
853 ctx.showPrimitive(prims);
854 DOCTEST_CHECK(!ctx.isPrimitiveHidden(prims[0]));
857 SUBCASE(
"Primitive color and parent object") {
859 uint p = ctx.addPatch();
860 ctx.setPrimitiveColor(p, RGB::red);
861 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::red);
862 ctx.overridePrimitiveTextureColor(p);
863 DOCTEST_CHECK(ctx.isPrimitiveTextureColorOverridden(p));
864 ctx.usePrimitiveTextureColor(p);
865 DOCTEST_CHECK(!ctx.isPrimitiveTextureColorOverridden(p));
867 ctx.setPrimitiveParentObjectID(p,
obj);
868 DOCTEST_CHECK(ctx.getPrimitiveParentObjectID(p) ==
obj);
871TEST_CASE(
"Object Management: Creation and Properties") {
873 SUBCASE(
"addSphereObject") {
875 uint objID = ctx.addSphereObject(10,
make_vec3(1, 2, 3), 5.f);
876 DOCTEST_CHECK(ctx.doesObjectExist(objID));
877 Sphere *sphere = ctx.getSphereObjectPointer(objID);
878 DOCTEST_CHECK(sphere !=
nullptr);
879 DOCTEST_CHECK(sphere->getCenter() ==
make_vec3(1, 2, 3));
880 DOCTEST_CHECK(sphere->getRadius() ==
make_vec3(5.f, 5.f, 5.f));
881 DOCTEST_CHECK(sphere->getSubdivisionCount() == 10);
884 SUBCASE(
"addDiskObject") {
887 DOCTEST_CHECK(ctx.doesObjectExist(objID));
888 Disk *disk = ctx.getDiskObjectPointer(objID);
889 DOCTEST_CHECK(disk !=
nullptr);
890 DOCTEST_CHECK(disk->getCenter() ==
make_vec3(1, 2, 3));
891 DOCTEST_CHECK(disk->getSize() ==
make_vec2(4, 5));
892 DOCTEST_CHECK(disk->getSubdivisionCount() ==
make_int2(8, 16));
895 SUBCASE(
"addConeObject") {
898 DOCTEST_CHECK(ctx.doesObjectExist(objID));
899 Cone *cone = ctx.getConeObjectPointer(objID);
900 DOCTEST_CHECK(cone !=
nullptr);
901 DOCTEST_CHECK(cone->getNodeCoordinate(0) ==
make_vec3(0, 0, 0));
902 DOCTEST_CHECK(cone->getNodeCoordinate(1) ==
make_vec3(0, 0, 5));
903 DOCTEST_CHECK(cone->getNodeRadius(0) == 2.f);
904 DOCTEST_CHECK(cone->getNodeRadius(1) == 1.f);
905 DOCTEST_CHECK(cone->getSubdivisionCount() == 10);
909TEST_CASE(
"Global Data Management") {
910 SUBCASE(
"Integer Data") {
912 ctx.setGlobalData(
"test_int", 123);
913 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"test_int"));
915 ctx.getGlobalData(
"test_int", val);
916 DOCTEST_CHECK(val == 123);
917 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_int") == 1);
918 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_int") == HELIOS_TYPE_INT);
919 ctx.clearGlobalData(
"test_int");
920 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"test_int"));
923 SUBCASE(
"Vector Data") {
925 std::vector<vec3> vec_data = {{1, 2, 3}, {4, 5, 6}};
926 ctx.setGlobalData(
"test_vec", vec_data);
927 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"test_vec"));
928 std::vector<vec3> read_vec;
929 ctx.getGlobalData(
"test_vec", read_vec);
930 DOCTEST_CHECK(read_vec.size() == 2);
931 DOCTEST_CHECK(read_vec[1] ==
make_vec3(4, 5, 6));
932 DOCTEST_CHECK(ctx.getGlobalDataSize(
"test_vec") == 2);
933 DOCTEST_CHECK(ctx.getGlobalDataType(
"test_vec") == HELIOS_TYPE_VEC3);
936 SUBCASE(
"List Data") {
938 ctx.setGlobalData(
"d1", 1);
939 ctx.setGlobalData(
"d2", 2.f);
940 std::vector<std::string> labels = ctx.listGlobalData();
941 DOCTEST_CHECK(labels.size() == 2);
942 DOCTEST_CHECK(std::find(labels.begin(), labels.end(),
"d1") != labels.end());
946TEST_CASE(
"Context primitive data management") {
948 uint p1 = ctx.addPatch();
949 uint p2 = ctx.addPatch();
950 ctx.setPrimitiveData(p1,
"my_data", 10);
953 ctx.copyPrimitiveData(p1, p2);
954 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2,
"my_data"));
956 ctx.getPrimitiveData(p2,
"my_data", val);
957 DOCTEST_CHECK(val == 10);
960 ctx.renamePrimitiveData(p1,
"my_data",
"new_data_name");
961 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p1,
"my_data"));
962 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1,
"new_data_name"));
965 ctx.duplicatePrimitiveData(p2,
"my_data",
"my_data_copy");
966 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2,
"my_data_copy"));
967 ctx.getPrimitiveData(p2,
"my_data_copy", val);
968 DOCTEST_CHECK(val == 10);
971 ctx.setPrimitiveData(p1,
"global_copy_test", 5.5f);
972 ctx.duplicatePrimitiveData(
"global_copy_test",
"global_copy_test_new");
973 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1,
"global_copy_test_new"));
974 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p2,
"global_copy_test_new"));
976 ctx.clearPrimitiveData(p1,
"new_data_name");
977 ctx.setPrimitiveData(p2,
"my_data_copy", 15);
978 ctx.setPrimitiveData(p2,
"my_data_copy", 20);
979 ctx.clearPrimitiveData(p2,
"my_data_copy");
980 std::vector<std::string> all_labels = ctx.listAllPrimitiveDataLabels();
981 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"my_data") != all_labels.end());
982 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"my_data_copy") == all_labels.end());
983 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(),
"new_data_name") == all_labels.end());
986TEST_CASE(
"Context primitive data calculations") {
988 std::vector<uint> uuids;
989 for (
int i = 0; i < 5; ++i) {
991 ctx.setPrimitiveData(p,
"float_val", (
float) i);
992 ctx.setPrimitiveData(p,
"double_val", (
double) i);
993 ctx.setPrimitiveData(p,
"vec2_val",
make_vec2((
float) i, (
float) i));
999 ctx.calculatePrimitiveDataMean(uuids,
"float_val", float_mean);
1000 DOCTEST_CHECK(float_mean == doctest::Approx(2.0f));
1002 ctx.calculatePrimitiveDataMean(uuids,
"double_val", double_mean);
1003 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
1005 ctx.calculatePrimitiveDataMean(uuids,
"vec2_val", vec2_mean);
1006 DOCTEST_CHECK(vec2_mean.x == doctest::Approx(2.0f));
1010 ctx.calculatePrimitiveDataAreaWeightedMean(uuids,
"float_val", awt_mean_f);
1011 DOCTEST_CHECK(awt_mean_f == doctest::Approx(2.0f));
1015 ctx.calculatePrimitiveDataSum(uuids,
"float_val", float_sum);
1016 DOCTEST_CHECK(float_sum == doctest::Approx(10.0f));
1020 ctx.calculatePrimitiveDataAreaWeightedSum(uuids,
"float_val", awt_sum_f);
1021 DOCTEST_CHECK(awt_sum_f == doctest::Approx(10.0f));
1024 ctx.scalePrimitiveData(uuids,
"float_val", 2.0f);
1025 ctx.getPrimitiveData(uuids[2],
"float_val", float_mean);
1026 DOCTEST_CHECK(float_mean == doctest::Approx(4.0f));
1027 ctx.scalePrimitiveData(
"double_val", 0.5f);
1028 ctx.getPrimitiveData(uuids[4],
"double_val", double_mean);
1029 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
1032 ctx.setPrimitiveData(uuids,
"int_val", 10);
1033 ctx.incrementPrimitiveData(uuids,
"int_val", 5);
1035 ctx.getPrimitiveData(uuids[0],
"int_val", int_val);
1036 DOCTEST_CHECK(int_val == 15);
1037 capture_cerr cerr_buffer;
1038 ctx.incrementPrimitiveData(uuids,
"float_val", 1);
1039 DOCTEST_CHECK(cerr_buffer.has_output());
1042TEST_CASE(
"Context primitive data aggregation and filtering") {
1044 std::vector<uint> uuids;
1045 for (
int i = 0; i < 3; ++i) {
1046 uint p = ctx.addPatch();
1047 ctx.setPrimitiveData(p,
"d1", (
float) i);
1048 ctx.setPrimitiveData(p,
"d2", (
float) i * 2.0f);
1049 ctx.setPrimitiveData(p,
"d3", (
float) i * 3.0f);
1050 ctx.setPrimitiveData(p,
"filter_me", i);
1055 std::vector<std::string> labels = {
"d1",
"d2",
"d3"};
1056 ctx.aggregatePrimitiveDataSum(uuids, labels,
"sum_data");
1058 ctx.getPrimitiveData(uuids[1],
"sum_data", sum_val);
1059 DOCTEST_CHECK(sum_val == doctest::Approx(1.f + 2.f + 3.f));
1062 ctx.aggregatePrimitiveDataProduct(uuids, labels,
"prod_data");
1064 ctx.getPrimitiveData(uuids[2],
"prod_data", prod_val);
1065 DOCTEST_CHECK(prod_val == doctest::Approx(2.f * 4.f * 6.f));
1068 std::vector<uint> filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
">=");
1069 DOCTEST_CHECK(filtered.size() == 2);
1070 filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
"==");
1071 DOCTEST_CHECK(filtered.size() == 1);
1072 DOCTEST_CHECK(filtered[0] == uuids[1]);
1073 capture_cerr cerr_buffer;
1074 DOCTEST_CHECK_THROWS_AS(filtered = ctx.filterPrimitivesByData(uuids,
"filter_me", 1,
"!!"), std::runtime_error);
1077TEST_CASE(
"Object data") {
1080 ctx.setObjectData(o,
"test_int", 5);
1081 ctx.setObjectData(o,
"test_float", 3.14f);
1084 DOCTEST_CHECK(ctx.getObjectDataType(
"test_int") == HELIOS_TYPE_INT);
1086 capture_cerr cerr_buffer;
1087 DOCTEST_CHECK_THROWS_AS(ctx.getObjectDataType(
"non_existent"), std::runtime_error);
1091 DOCTEST_CHECK(ctx.getObjectDataSize(o,
"test_int") == 1);
1094 ctx.clearObjectData(o,
"test_int");
1095 DOCTEST_CHECK(!ctx.doesObjectDataExist(o,
"test_int"));
1098 std::vector<std::string> data_labels = ctx.listObjectData(o);
1099 DOCTEST_CHECK(std::find(data_labels.begin(), data_labels.end(),
"test_float") != data_labels.end());
1102TEST_CASE(
"Context object data management") {
1106 ctx.setObjectData(o1,
"my_data", 10);
1109 ctx.copyObjectData(o1, o2);
1110 DOCTEST_CHECK(ctx.doesObjectDataExist(o2,
"my_data"));
1113 ctx.renameObjectData(o1,
"my_data",
"new_name");
1114 DOCTEST_CHECK(!ctx.doesObjectDataExist(o1,
"my_data"));
1115 DOCTEST_CHECK(ctx.doesObjectDataExist(o1,
"new_name"));
1118 ctx.duplicateObjectData(o2,
"my_data",
"my_data_copy");
1119 DOCTEST_CHECK(ctx.doesObjectDataExist(o2,
"my_data_copy"));
1121 std::vector<std::string> all_obj_labels = ctx.listAllObjectDataLabels();
1122 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"my_data") != all_obj_labels.end());
1123 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"my_data_copy") != all_obj_labels.end());
1124 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(),
"new_name") != all_obj_labels.end());
1127TEST_CASE(
"Global data") {
1129 ctx.setGlobalData(
"g_int", 5);
1130 ctx.setGlobalData(
"g_float", 3.14f);
1133 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int"));
1134 DOCTEST_CHECK(ctx.getGlobalDataType(
"g_int") == HELIOS_TYPE_INT);
1135 DOCTEST_CHECK(ctx.getGlobalDataSize(
"g_int") == 1);
1138 ctx.duplicateGlobalData(
"g_int",
"g_int_copy");
1139 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int_copy"));
1140 ctx.renameGlobalData(
"g_int",
"g_int_new");
1141 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"g_int"));
1142 DOCTEST_CHECK(ctx.doesGlobalDataExist(
"g_int_new"));
1143 ctx.clearGlobalData(
"g_int_new");
1144 DOCTEST_CHECK(!ctx.doesGlobalDataExist(
"g_int_new"));
1147 std::vector<std::string> g_labels = ctx.listGlobalData();
1148 DOCTEST_CHECK(g_labels.size() > 0);
1151 ctx.setGlobalData(
"inc_me", 10);
1152 ctx.incrementGlobalData(
"inc_me", 5);
1154 ctx.getGlobalData(
"inc_me", val);
1155 DOCTEST_CHECK(val == 15);
1156 capture_cerr cerr_buffer;
1157 ctx.incrementGlobalData(
"g_float", 1);
1158 DOCTEST_CHECK(cerr_buffer.has_output());
1161TEST_CASE(
"Voxel Management") {
1162 SUBCASE(
"addVoxel and voxel properties") {
1167 float rotation = 0.5f *
PI_F;
1169 uint vox1 = ctx.addVoxel(center, size);
1170 DOCTEST_CHECK(ctx.getPrimitiveType(vox1) == PRIMITIVE_TYPE_VOXEL);
1171 DOCTEST_CHECK(ctx.getVoxelCenter(vox1) == center);
1172 DOCTEST_CHECK(ctx.getVoxelSize(vox1) == size);
1174 uint vox2 = ctx.addVoxel(center, size, rotation);
1175 DOCTEST_CHECK(ctx.getVoxelCenter(vox2) == center);
1176 DOCTEST_CHECK(ctx.getVoxelSize(vox2) == size);
1178 uint vox3 = ctx.addVoxel(center, size, rotation, RGB::red);
1179 DOCTEST_CHECK(ctx.getPrimitiveColor(vox3) == RGB::red);
1181 uint vox4 = ctx.addVoxel(center, size, rotation, RGBA::red);
1182 RGBAcolor color_rgba = ctx.getPrimitiveColorRGBA(vox4);
1183 DOCTEST_CHECK(color_rgba.r == RGBA::red.r);
1184 DOCTEST_CHECK(color_rgba.a == RGBA::red.a);
1186 DOCTEST_CHECK(ctx.getPrimitiveCount() >= 4);
1188 float area = ctx.getPrimitiveArea(vox1);
1189 DOCTEST_CHECK(area == doctest::Approx(2.f * (size.x * size.y + size.y * size.z + size.x * size.z)));
1193TEST_CASE(
"Texture Management") {
1194 SUBCASE(
"texture validation and properties") {
1198 ctx.setPrimitiveTextureFile(patch,
"lib/images/solid.jpg");
1199 DOCTEST_CHECK(ctx.getPrimitiveTextureFile(patch) ==
"lib/images/solid.jpg");
1200 DOCTEST_CHECK(!ctx.primitiveTextureHasTransparencyChannel(patch));
1202 Texture tex(
"lib/images/solid.jpg");
1203 std::vector<vec2> uv = {{0, 0}, {1, 0}, {1, 1}};
1204 float solid_frac = tex.getSolidFraction(uv);
1205 DOCTEST_CHECK(solid_frac == doctest::Approx(1.f));
1209TEST_CASE(
"Triangle Management") {
1210 SUBCASE(
"setTriangleVertices") {
1215 uint tri = ctx.addTriangle(v0, v1, v2);
1220 ctx.setTriangleVertices(tri, new_v0, new_v1, new_v2);
1222 std::vector<vec3> vertices = ctx.getPrimitiveVertices(tri);
1223 DOCTEST_CHECK(vertices[0] == new_v0);
1224 DOCTEST_CHECK(vertices[1] == new_v1);
1225 DOCTEST_CHECK(vertices[2] == new_v2);
1229TEST_CASE(
"UUID and Object Management") {
1230 SUBCASE(
"getAllUUIDs and cleanDeletedUUIDs") {
1232 uint p1 = ctx.addPatch();
1233 uint p2 = ctx.addPatch();
1234 uint p3 = ctx.addPatch();
1236 std::vector<uint> all_uuids = ctx.getAllUUIDs();
1237 DOCTEST_CHECK(all_uuids.size() == 3);
1238 DOCTEST_CHECK(std::find(all_uuids.begin(), all_uuids.end(), p1) != all_uuids.end());
1240 ctx.deletePrimitive(p2);
1241 std::vector<uint> uuids_with_deleted = {p1, p2, p3};
1242 ctx.cleanDeletedUUIDs(uuids_with_deleted);
1243 DOCTEST_CHECK(uuids_with_deleted.size() == 2);
1244 DOCTEST_CHECK(std::find(uuids_with_deleted.begin(), uuids_with_deleted.end(), p2) == uuids_with_deleted.end());
1246 std::vector<std::vector<uint>> nested_uuids = {{p1, p2}, {p3, p2}};
1247 ctx.cleanDeletedUUIDs(nested_uuids);
1248 DOCTEST_CHECK(nested_uuids[0].size() == 1);
1249 DOCTEST_CHECK(nested_uuids[1].size() == 1);
1251 std::vector<std::vector<std::vector<uint>>> triple_nested = {{{p1, p2, p3}}};
1252 ctx.cleanDeletedUUIDs(triple_nested);
1253 DOCTEST_CHECK(triple_nested[0][0].size() == 2);
1256 SUBCASE(
"object management utilities") {
1260 DOCTEST_CHECK(ctx.areObjectPrimitivesComplete(
obj));
1262 std::vector<uint> obj_ids = {
obj, 999};
1263 ctx.cleanDeletedObjectIDs(obj_ids);
1264 DOCTEST_CHECK(obj_ids.size() == 1);
1265 DOCTEST_CHECK(obj_ids[0] ==
obj);
1267 std::vector<std::vector<uint>> nested_obj_ids = {{
obj, 999}, {
obj}};
1268 ctx.cleanDeletedObjectIDs(nested_obj_ids);
1269 DOCTEST_CHECK(nested_obj_ids[0].size() == 1);
1270 DOCTEST_CHECK(nested_obj_ids[1].size() == 1);
1272 std::vector<std::vector<std::vector<uint>>> triple_nested_obj = {{{
obj, 999}}};
1273 ctx.cleanDeletedObjectIDs(triple_nested_obj);
1274 DOCTEST_CHECK(triple_nested_obj[0][0].size() == 1);
1276 Box *box_ptr = ctx.getBoxObjectPointer(
obj);
1277 DOCTEST_CHECK(box_ptr !=
nullptr);
1280 ctx.setObjectOrigin(
obj, new_origin);
1283 ctx.setObjectAverageNormal(
obj,
make_vec3(0, 0, 0), new_normal);
1287TEST_CASE(
"Tile Object Advanced Features") {
1288 SUBCASE(
"tile object subdivision management") {
1292 float area_ratio = ctx.getTileObjectAreaRatio(tile);
1293 DOCTEST_CHECK(area_ratio > 0.f);
1295 ctx.setTileObjectSubdivisionCount({tile},
make_int2(4, 4));
1297 ctx.setTileObjectSubdivisionCount({tile}, 0.5f);
1301TEST_CASE(
"Pseudocolor Visualization") {
1302 SUBCASE(
"colorPrimitiveByDataPseudocolor") {
1304 std::vector<uint> patches;
1305 for (
int i = 0; i < 5; i++) {
1306 uint p = ctx.addPatch();
1307 ctx.setPrimitiveData(p,
"value",
float(i));
1308 patches.push_back(p);
1311 DOCTEST_CHECK_NOTHROW(ctx.colorPrimitiveByDataPseudocolor(patches,
"value",
"hot", 10));
1312 DOCTEST_CHECK_NOTHROW(ctx.colorPrimitiveByDataPseudocolor(patches,
"value",
"rainbow", 5, 0.f, 4.f));
1316TEST_CASE(
"Date and Time Extensions") {
1317 SUBCASE(
"getMonthString") {
1319 ctx.setDate(15, 1, 2025);
1320 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"JAN") == 0);
1321 ctx.setDate(15, 2, 2025);
1322 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"FEB") == 0);
1323 ctx.setDate(15, 12, 2025);
1324 DOCTEST_CHECK(strcmp(ctx.getMonthString(),
"DEC") == 0);
1328TEST_CASE(
"Tube Object Management") {
1329 SUBCASE(
"appendTubeSegment with texture") {
1332 std::vector<float> radii = {0.2f, 0.1f};
1333 uint tube = ctx.addTubeObject(10, nodes, radii);
1335 ctx.appendTubeSegment(tube,
make_vec3(0, 0, 2), 0.05f,
"lib/images/solid.jpg",
make_vec2(0.5f, 1.0f));
1336 DOCTEST_CHECK(ctx.getTubeObjectNodeCount(tube) == 3);
1340TEST_CASE(
"Edge Cases and Additional Coverage") {
1341 SUBCASE(
"Julian date edge cases") {
1343 ctx.setDate(1, 1, 2025);
1344 DOCTEST_CHECK(ctx.getJulianDate() == 1);
1346 ctx.setDate(31, 12, 2025);
1347 DOCTEST_CHECK(ctx.getJulianDate() == 365);
1349 ctx.setDate(100, 2025);
1350 Date d = ctx.getDate();
1351 DOCTEST_CHECK(d.day == 10);
1352 DOCTEST_CHECK(d.month == 4);
1355 SUBCASE(
"time edge cases") {
1357 ctx.setTime(0, 0, 0);
1358 Time t = ctx.getTime();
1359 DOCTEST_CHECK(t.hour == 0);
1360 DOCTEST_CHECK(t.minute == 0);
1361 DOCTEST_CHECK(t.second == 0);
1363 ctx.setTime(59, 59, 23);
1365 DOCTEST_CHECK(t.hour == 23);
1366 DOCTEST_CHECK(t.minute == 59);
1367 DOCTEST_CHECK(t.second == 59);
1370 SUBCASE(
"random number edge cases") {
1372 ctx.seedRandomGenerator(0);
1374 float r1 = ctx.randu(5.f, 5.f);
1375 DOCTEST_CHECK(r1 == doctest::Approx(5.f));
1377 int ri = ctx.randu(10, 10);
1378 DOCTEST_CHECK(ri == 10);
1380 float rn = ctx.randn(0.f, 0.f);
1381 DOCTEST_CHECK(rn == doctest::Approx(0.f));
1384 SUBCASE(
"texture edge cases") {
1386 uint patch = ctx.addPatch();
1388 ctx.overridePrimitiveTextureColor(patch);
1389 ctx.usePrimitiveTextureColor(patch);
1391 std::vector<uint> patches = {patch};
1392 ctx.overridePrimitiveTextureColor(patches);
1393 ctx.usePrimitiveTextureColor(patches);
1395 DOCTEST_CHECK(!ctx.isPrimitiveTextureColorOverridden(patch));
1398 SUBCASE(
"primitive existence checks") {
1400 uint p1 = ctx.addPatch();
1401 uint p2 = ctx.addPatch();
1403 DOCTEST_CHECK(ctx.doesPrimitiveExist(p1));
1404 DOCTEST_CHECK(ctx.doesPrimitiveExist({p1, p2}));
1406 ctx.deletePrimitive(p1);
1407 DOCTEST_CHECK(!ctx.doesPrimitiveExist(p1));
1408 DOCTEST_CHECK(!ctx.doesPrimitiveExist({p1, p2}));
1409 DOCTEST_CHECK(ctx.doesPrimitiveExist(std::vector<uint>{p2}));
1412 SUBCASE(
"object containment checks") {
1415 std::vector<uint> prims = ctx.getObjectPrimitiveUUIDs(
obj);
1417 DOCTEST_CHECK(ctx.doesObjectContainPrimitive(
obj, prims[0]));
1419 uint independent_patch = ctx.addPatch();
1420 DOCTEST_CHECK(!ctx.doesObjectContainPrimitive(
obj, independent_patch));
1423 SUBCASE(
"transformation matrix operations") {
1425 uint p = ctx.addPatch();
1427 float identity[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
1428 ctx.setPrimitiveTransformationMatrix(p, identity);
1430 std::vector<uint> patches = {p};
1431 ctx.setPrimitiveTransformationMatrix(patches, identity);
1434 ctx.setObjectTransformationMatrix(
obj, identity);
1436 std::vector<uint> objs = {
obj};
1437 ctx.setObjectTransformationMatrix(objs, identity);
1439 float retrieved[16];
1440 ctx.getObjectTransformationMatrix(
obj, retrieved);
1441 for (
int i = 0; i < 16; i++) {
1442 DOCTEST_CHECK(retrieved[i] == doctest::Approx(identity[i]));
1446 SUBCASE(
"object type and texture checks") {
1450 DOCTEST_CHECK(!ctx.objectHasTexture(
obj));
1453 DOCTEST_CHECK(ctx.objectHasTexture(textured_obj));
1456 SUBCASE(
"tube object segment operations") {
1459 std::vector<float> radii = {0.2f, 0.15f, 0.1f};
1460 uint tube = ctx.addTubeObject(10, nodes, radii);
1462 float seg_volume = ctx.getTubeObjectSegmentVolume(tube, 0);
1463 DOCTEST_CHECK(seg_volume > 0.f);
1465 seg_volume = ctx.getTubeObjectSegmentVolume(tube, 1);
1466 DOCTEST_CHECK(seg_volume > 0.f);
1469 SUBCASE(
"cone object advanced properties") {
1473 float radius0 = ctx.getConeObjectNodeRadius(cone, 0);
1474 DOCTEST_CHECK(radius0 == doctest::Approx(1.f));
1476 float radius1 = ctx.getConeObjectNodeRadius(cone, 1);
1477 DOCTEST_CHECK(radius1 == doctest::Approx(0.5f));
1479 float length = ctx.getConeObjectLength(cone);
1480 DOCTEST_CHECK(length == doctest::Approx(2.f));
1482 DOCTEST_CHECK(ctx.getConeObjectSubdivisionCount(cone) == 10);
1485 SUBCASE(
"primitive color operations") {
1487 uint p = ctx.addPatch();
1489 ctx.setPrimitiveColor(p, RGB::blue);
1490 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::blue);
1492 ctx.setPrimitiveColor(p, RGBA::green);
1493 RGBAcolor rgba = ctx.getPrimitiveColorRGBA(p);
1494 DOCTEST_CHECK(rgba.r == RGBA::green.r);
1495 DOCTEST_CHECK(rgba.a == RGBA::green.a);
1497 std::vector<uint> patches = {p};
1498 ctx.setPrimitiveColor(patches, RGB::red);
1499 DOCTEST_CHECK(ctx.getPrimitiveColor(p) == RGB::red);
1501 ctx.setPrimitiveColor(patches, RGBA::yellow);
1502 rgba = ctx.getPrimitiveColorRGBA(p);
1503 DOCTEST_CHECK(rgba.r == RGBA::yellow.r);
1506 SUBCASE(
"object color operations") {
1510 ctx.setObjectColor(
obj, RGB::cyan);
1511 ctx.setObjectColor(
obj, RGBA::magenta);
1513 std::vector<uint> objs = {
obj};
1514 ctx.setObjectColor(objs, RGB::white);
1515 ctx.setObjectColor(objs, RGBA::black);
1519TEST_CASE(
"Print and Information Functions") {
1520 SUBCASE(
"printPrimitiveInfo and printObjectInfo") {
1526 capture_cout cout_buffer;
1527 DOCTEST_CHECK_NOTHROW(ctx.printPrimitiveInfo(patch));
1528 DOCTEST_CHECK_NOTHROW(ctx.printObjectInfo(
obj));
1531 DOCTEST_CHECK(cout_buffer.has_output());
1532 std::string output = cout_buffer.get_captured_output();
1533 DOCTEST_CHECK(output.find(
"Info for UUID") != std::string::npos);
1534 DOCTEST_CHECK(output.find(
"Info for ObjID") != std::string::npos);
1538TEST_CASE(
"Object Pointer Access") {
1539 SUBCASE(
"getObjectPointer functions") {
1543 Box *box_ptr = ctx.getBoxObjectPointer(box);
1544 DOCTEST_CHECK(box_ptr !=
nullptr);
1547 Disk *disk_ptr = ctx.getDiskObjectPointer(disk);
1548 DOCTEST_CHECK(disk_ptr !=
nullptr);
1550 uint sphere = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1551 Sphere *sphere_ptr = ctx.getSphereObjectPointer(sphere);
1552 DOCTEST_CHECK(sphere_ptr !=
nullptr);
1555 std::vector<float> radii = {0.2f, 0.1f};
1556 uint tube = ctx.addTubeObject(10, nodes, radii);
1557 Tube *tube_ptr = ctx.getTubeObjectPointer(tube);
1558 DOCTEST_CHECK(tube_ptr !=
nullptr);
1561 Cone *cone_ptr = ctx.getConeObjectPointer(cone);
1562 DOCTEST_CHECK(cone_ptr !=
nullptr);
1565 uint polymesh = ctx.addPolymeshObject(prim_uuids);
1566 Polymesh *polymesh_ptr = ctx.getPolymeshObjectPointer(polymesh);
1567 DOCTEST_CHECK(polymesh_ptr !=
nullptr);
1571TEST_CASE(
"Advanced Primitive Operations") {
1572 SUBCASE(
"primitive visibility and print operations") {
1574 uint p1 = ctx.addPatch();
1575 uint p2 = ctx.addPatch();
1578 ctx.hidePrimitive(p1);
1579 DOCTEST_CHECK(ctx.isPrimitiveHidden(p1));
1580 ctx.showPrimitive(p1);
1581 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p1));
1583 std::vector<uint> patches = {p1, p2};
1584 ctx.hidePrimitive(patches);
1585 DOCTEST_CHECK(ctx.isPrimitiveHidden(p1));
1586 DOCTEST_CHECK(ctx.isPrimitiveHidden(p2));
1587 ctx.showPrimitive(patches);
1588 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p1));
1589 DOCTEST_CHECK(!ctx.isPrimitiveHidden(p2));
1592 SUBCASE(
"primitive counts by type") {
1594 uint initial_patch_count = ctx.getPatchCount();
1595 uint initial_triangle_count = ctx.getTriangleCount();
1597 uint p1 = ctx.addPatch();
1598 uint p2 = ctx.addPatch();
1601 DOCTEST_CHECK(ctx.getPatchCount() == initial_patch_count + 2);
1602 DOCTEST_CHECK(ctx.getTriangleCount() == initial_triangle_count + 1);
1605 ctx.hidePrimitive(p1);
1606 DOCTEST_CHECK(ctx.getPatchCount(
false) == initial_patch_count + 1);
1607 DOCTEST_CHECK(ctx.getPatchCount(
true) == initial_patch_count + 2);
1611TEST_CASE(
"Data Type and Size Functions") {
1612 SUBCASE(
"primitive data type operations") {
1614 uint p = ctx.addPatch();
1616 ctx.setPrimitiveData(p,
"test_int", 42);
1617 ctx.setPrimitiveData(p,
"test_float", 3.14f);
1618 ctx.setPrimitiveData(p,
"test_vec3",
make_vec3(1, 2, 3));
1620 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_int") == HELIOS_TYPE_INT);
1621 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_float") == HELIOS_TYPE_FLOAT);
1622 DOCTEST_CHECK(ctx.getPrimitiveDataType(
"test_vec3") == HELIOS_TYPE_VEC3);
1624 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_int") == 1);
1625 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_vec3") == 1);
1627 std::vector<float> vec_data = {1.0f, 2.0f, 3.0f};
1628 ctx.setPrimitiveData(p,
"test_vector", vec_data);
1629 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p,
"test_vector") == 3);
1633TEST_CASE(
"Additional Missing Coverage") {
1634 SUBCASE(
"getDirtyUUIDs function") {
1636 uint p1 = ctx.addPatch();
1637 uint p2 = ctx.addPatch();
1639 ctx.markGeometryClean();
1640 std::vector<uint> dirty_uuids = ctx.getDirtyUUIDs();
1641 DOCTEST_CHECK(dirty_uuids.empty());
1643 ctx.markPrimitiveDirty(p1);
1644 dirty_uuids = ctx.getDirtyUUIDs();
1645 DOCTEST_CHECK(dirty_uuids.size() == 1);
1646 DOCTEST_CHECK(std::find(dirty_uuids.begin(), dirty_uuids.end(), p1) != dirty_uuids.end());
1650TEST_CASE(
"Advanced Object Operations") {
1651 SUBCASE(
"object primitive count and area calculations") {
1655 DOCTEST_CHECK(ctx.getObjectPrimitiveCount(
obj) == 6);
1657 float area = ctx.getObjectArea(
obj);
1658 float expected_area = 2 * (2 * 3 + 3 * 4 + 2 * 4);
1659 DOCTEST_CHECK(area == doctest::Approx(expected_area).epsilon(0.01));
1662 SUBCASE(
"object bounding box operations") {
1666 vec3 min_corner, max_corner;
1667 ctx.getObjectBoundingBox(
obj, min_corner, max_corner);
1669 DOCTEST_CHECK(min_corner.x == doctest::Approx(0.f).epsilon(0.01));
1670 DOCTEST_CHECK(max_corner.x == doctest::Approx(2.f).epsilon(0.01));
1671 DOCTEST_CHECK(min_corner.y == doctest::Approx(0.f).epsilon(0.01));
1672 DOCTEST_CHECK(max_corner.y == doctest::Approx(4.f).epsilon(0.01));
1674 std::vector<uint> objs = {
obj};
1675 ctx.getObjectBoundingBox(objs, min_corner, max_corner);
1676 DOCTEST_CHECK(min_corner.x == doctest::Approx(0.f).epsilon(0.01));
1677 DOCTEST_CHECK(max_corner.x == doctest::Approx(2.f).epsilon(0.01));
1681TEST_CASE(
"Additional Object Features") {
1682 SUBCASE(
"getAllObjectIDs") {
1685 uint obj2 = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1687 std::vector<uint> all_ids = ctx.getAllObjectIDs();
1688 DOCTEST_CHECK(all_ids.size() >= 2);
1689 DOCTEST_CHECK(std::find(all_ids.begin(), all_ids.end(), obj1) != all_ids.end());
1690 DOCTEST_CHECK(std::find(all_ids.begin(), all_ids.end(), obj2) != all_ids.end());
1693 SUBCASE(
"object type checks") {
1696 uint sphere = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1699 DOCTEST_CHECK(ctx.getObjectType(box) == OBJECT_TYPE_BOX);
1700 DOCTEST_CHECK(ctx.getObjectType(sphere) == OBJECT_TYPE_SPHERE);
1701 DOCTEST_CHECK(ctx.getObjectType(disk) == OBJECT_TYPE_DISK);
1705TEST_CASE(
"Comprehensive Object Property Tests") {
1706 SUBCASE(
"rotation operations on objects") {
1708 std::vector<uint> objs;
1712 DOCTEST_CHECK_NOTHROW(ctx.rotateObject(objs, 0.5f *
PI_F,
"z"));
1713 DOCTEST_CHECK_NOTHROW(ctx.rotateObject(objs, 0.5f *
PI_F,
make_vec3(0, 0, 1)));
1715 DOCTEST_CHECK_NOTHROW(ctx.rotateObjectAboutOrigin(objs, 0.5f *
PI_F,
make_vec3(0, 0, 1)));
1718 SUBCASE(
"scaling operations on objects") {
1720 std::vector<uint> objs;
1723 DOCTEST_CHECK_NOTHROW(ctx.scaleObject(objs,
make_vec3(2, 2, 2)));
1724 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutCenter(objs,
make_vec3(0.5f, 0.5f, 0.5f)));
1725 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutPoint(objs,
make_vec3(2, 2, 2),
make_vec3(0, 0, 0)));
1726 DOCTEST_CHECK_NOTHROW(ctx.scaleObjectAboutOrigin(objs,
make_vec3(0.5f, 0.5f, 0.5f)));
1729 SUBCASE(
"translation operations on objects") {
1731 std::vector<uint> objs;
1734 DOCTEST_CHECK_NOTHROW(ctx.translateObject(objs,
make_vec3(1, 2, 3)));
1738TEST_CASE(
"Domain and Bounding Operations") {
1739 SUBCASE(
"domain bounding sphere") {
1746 ctx.getDomainBoundingSphere(center, radius);
1747 DOCTEST_CHECK(center.x == doctest::Approx(0.f).epsilon(0.1));
1748 DOCTEST_CHECK(radius > 2.f);
1752TEST_CASE(
"Missing Data and State Functions") {
1753 SUBCASE(
"listTimeseriesVariables") {
1758 ctx.addTimeseriesData(
"temp", 25.5f, date, time);
1759 ctx.addTimeseriesData(
"humidity", 60.0f, date, time);
1761 std::vector<std::string> vars = ctx.listTimeseriesVariables();
1762 DOCTEST_CHECK(vars.size() >= 2);
1763 DOCTEST_CHECK(std::find(vars.begin(), vars.end(),
"temp") != vars.end());
1764 DOCTEST_CHECK(std::find(vars.begin(), vars.end(),
"humidity") != vars.end());
1767 SUBCASE(
"getUniquePrimitiveParentObjectIDs") {
1770 uint obj2 = ctx.addSphereObject(10,
make_vec3(0, 0, 0), 1.f);
1772 std::vector<uint> all_prims = ctx.getAllUUIDs();
1773 std::vector<uint> obj_ids = ctx.getUniquePrimitiveParentObjectIDs(all_prims);
1774 DOCTEST_CHECK(obj_ids.size() >= 2);
1775 DOCTEST_CHECK(std::find(obj_ids.begin(), obj_ids.end(), obj1) != obj_ids.end());
1776 DOCTEST_CHECK(std::find(obj_ids.begin(), obj_ids.end(), obj2) != obj_ids.end());
1780TEST_CASE(
"Comprehensive Coverage Tests") {
1781 SUBCASE(
"additional object operations with vectors") {
1783 std::vector<uint> obj_ids;
1787 std::vector<uint> all_uuids = ctx.getObjectPrimitiveUUIDs(obj_ids);
1788 DOCTEST_CHECK(all_uuids.size() == 12);
1790 std::vector<std::vector<uint>> nested_obj_ids = {{obj_ids[0]}, {obj_ids[1]}};
1791 std::vector<uint> nested_uuids = ctx.getObjectPrimitiveUUIDs(nested_obj_ids);
1792 DOCTEST_CHECK(nested_uuids.size() == 12);
1794 ctx.hideObject(obj_ids);
1795 DOCTEST_CHECK(ctx.isObjectHidden(obj_ids[0]));
1796 DOCTEST_CHECK(ctx.isObjectHidden(obj_ids[1]));
1798 ctx.showObject(obj_ids);
1799 DOCTEST_CHECK(!ctx.isObjectHidden(obj_ids[0]));
1800 DOCTEST_CHECK(!ctx.isObjectHidden(obj_ids[1]));
1803 SUBCASE(
"object texture color overrides") {
1805 std::vector<uint> obj_ids;
1808 ctx.overrideObjectTextureColor(obj_ids);
1809 ctx.useObjectTextureColor(obj_ids);
1813TEST_CASE(
"getAllUUIDs Cache Performance") {
1814 SUBCASE(
"Cache invalidation on primitive add/delete") {
1818 std::vector<uint> empty_uuids = ctx.getAllUUIDs();
1819 DOCTEST_CHECK(empty_uuids.empty());
1822 uint p1 = ctx.addPatch();
1823 std::vector<uint> one_uuid = ctx.getAllUUIDs();
1824 DOCTEST_CHECK(one_uuid.size() == 1);
1825 DOCTEST_CHECK(one_uuid[0] == p1);
1828 std::vector<uint> same_uuid = ctx.getAllUUIDs();
1829 DOCTEST_CHECK(same_uuid.size() == 1);
1830 DOCTEST_CHECK(same_uuid[0] == p1);
1836 std::vector<uint> three_uuids = ctx.getAllUUIDs();
1837 DOCTEST_CHECK(three_uuids.size() == 3);
1838 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), p1) != three_uuids.end());
1839 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), t1) != three_uuids.end());
1840 DOCTEST_CHECK(std::find(three_uuids.begin(), three_uuids.end(), v1) != three_uuids.end());
1843 ctx.deletePrimitive(t1);
1844 std::vector<uint> two_uuids = ctx.getAllUUIDs();
1845 DOCTEST_CHECK(two_uuids.size() == 2);
1846 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), t1) == two_uuids.end());
1847 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), p1) != two_uuids.end());
1848 DOCTEST_CHECK(std::find(two_uuids.begin(), two_uuids.end(), v1) != two_uuids.end());
1851 SUBCASE(
"Cache invalidation on hide/show primitives") {
1853 uint p1 = ctx.addPatch();
1854 uint p2 = ctx.addPatch();
1855 uint p3 = ctx.addPatch();
1858 std::vector<uint> all_visible = ctx.getAllUUIDs();
1859 DOCTEST_CHECK(all_visible.size() == 3);
1862 ctx.hidePrimitive(p2);
1863 std::vector<uint> two_visible = ctx.getAllUUIDs();
1864 DOCTEST_CHECK(two_visible.size() == 2);
1865 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p2) == two_visible.end());
1866 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p1) != two_visible.end());
1867 DOCTEST_CHECK(std::find(two_visible.begin(), two_visible.end(), p3) != two_visible.end());
1870 std::vector<uint> to_hide = {p1, p3};
1871 ctx.hidePrimitive(to_hide);
1872 std::vector<uint> none_visible = ctx.getAllUUIDs();
1873 DOCTEST_CHECK(none_visible.empty());
1876 ctx.showPrimitive(p1);
1877 std::vector<uint> one_visible = ctx.getAllUUIDs();
1878 DOCTEST_CHECK(one_visible.size() == 1);
1879 DOCTEST_CHECK(one_visible[0] == p1);
1882 std::vector<uint> to_show = {p2, p3};
1883 ctx.showPrimitive(to_show);
1884 std::vector<uint> all_back = ctx.getAllUUIDs();
1885 DOCTEST_CHECK(all_back.size() == 3);
1888 SUBCASE(
"Cache invalidation on copy primitives") {
1890 uint original = ctx.addPatch();
1892 std::vector<uint> before_copy = ctx.getAllUUIDs();
1893 DOCTEST_CHECK(before_copy.size() == 1);
1895 uint copied = ctx.copyPrimitive(original);
1896 std::vector<uint> after_copy = ctx.getAllUUIDs();
1897 DOCTEST_CHECK(after_copy.size() == 2);
1898 DOCTEST_CHECK(std::find(after_copy.begin(), after_copy.end(), original) != after_copy.end());
1899 DOCTEST_CHECK(std::find(after_copy.begin(), after_copy.end(), copied) != after_copy.end());
1902 std::vector<uint> originals = {original, copied};
1903 std::vector<uint> copies = ctx.copyPrimitive(originals);
1904 std::vector<uint> after_multi_copy = ctx.getAllUUIDs();
1905 DOCTEST_CHECK(after_multi_copy.size() == 4);
1906 for (
uint copy_id: copies) {
1907 DOCTEST_CHECK(std::find(after_multi_copy.begin(), after_multi_copy.end(), copy_id) != after_multi_copy.end());
1911 SUBCASE(
"Cache consistency across mixed operations") {
1915 uint p1 = ctx.addPatch();
1918 std::vector<uint> step1 = ctx.getAllUUIDs();
1919 DOCTEST_CHECK(step1.size() == 2);
1921 ctx.hidePrimitive(p1);
1922 std::vector<uint> step2 = ctx.getAllUUIDs();
1923 DOCTEST_CHECK(step2.size() == 1);
1924 DOCTEST_CHECK(step2[0] == p2);
1927 std::vector<uint> step3 = ctx.getAllUUIDs();
1928 DOCTEST_CHECK(step3.size() == 2);
1930 ctx.showPrimitive(p1);
1931 std::vector<uint> step4 = ctx.getAllUUIDs();
1932 DOCTEST_CHECK(step4.size() == 3);
1934 ctx.deletePrimitive(p2);
1935 std::vector<uint> step5 = ctx.getAllUUIDs();
1936 DOCTEST_CHECK(step5.size() == 2);
1937 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p2) == step5.end());
1938 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p1) != step5.end());
1939 DOCTEST_CHECK(std::find(step5.begin(), step5.end(), p3) != step5.end());
1943TEST_CASE(
"Error Handling") {
1944 SUBCASE(
"Context error handling") {
1945 Context context_test;
1947 capture_cerr cerr_buffer;
1950 DOCTEST_CHECK_THROWS_AS(center = context_test.getPatchCenter(tri), std::runtime_error);
1954 std::vector<uint> vlist{vox};
1955 DOCTEST_CHECK_THROWS_AS(context_test.rotatePrimitive(vlist,
PI_F / 4.f,
"a"), std::runtime_error);
1959TEST_CASE(
"Zero Area Triangle Detection") {
1960 SUBCASE(
"addTubeObject with nearly identical vertices should not create zero-area triangles") {
1964 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)};
1966 std::vector<float> radii = {0.000500000024f, 0.000500000024f, 0.000500000024f};
1967 std::vector<RGBcolor> colors = {RGB::green, RGB::green, RGB::green};
1970 uint tube_obj = ctx.addTubeObject(7, nodes, radii, colors);
1973 DOCTEST_CHECK(ctx.doesObjectExist(tube_obj));
1976 std::vector<uint> tube_primitives = ctx.getObjectPrimitiveUUIDs(tube_obj);
1977 DOCTEST_CHECK(tube_primitives.size() > 0);
1979 for (
uint uuid: tube_primitives) {
1980 float area = ctx.getPrimitiveArea(uuid);
1981 DOCTEST_CHECK(area > 0.0f);
1982 DOCTEST_CHECK(area > 1e-12f);
1986 SUBCASE(
"addTubeObject with extremely small displacements") {
1992 std::vector<float> radii = {1e-4f, 1e-4f, 1e-4f};
1994 uint tube_obj = ctx.addTubeObject(6, nodes, radii);
1995 DOCTEST_CHECK(ctx.doesObjectExist(tube_obj));
1997 std::vector<uint> tube_primitives = ctx.getObjectPrimitiveUUIDs(tube_obj);
1998 for (
uint uuid: tube_primitives) {
1999 float area = ctx.getPrimitiveArea(uuid);
2000 DOCTEST_CHECK(area > 0.0f);
2005TEST_CASE(
"Transparent Texture Zero Area Validation") {
2006 SUBCASE(
"addSphere with transparent texture should filter zero-area triangles") {
2010 std::vector<uint> sphere_uuids = ctx.addSphere(20,
make_vec3(0, 0, 0), 1.0f,
"lib/images/diamond_texture.png");
2013 DOCTEST_CHECK(sphere_uuids.size() > 0);
2014 for (
uint uuid: sphere_uuids) {
2015 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2016 float area = ctx.getPrimitiveArea(uuid);
2017 DOCTEST_CHECK(area > 0.0f);
2021 std::vector<uint> sphere_disk_uuids = ctx.addSphere(30,
make_vec3(2, 0, 0), 1.0f,
"lib/images/disk_texture.png");
2023 DOCTEST_CHECK(sphere_disk_uuids.size() > 0);
2024 for (
uint uuid: sphere_disk_uuids) {
2025 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2026 float area = ctx.getPrimitiveArea(uuid);
2027 DOCTEST_CHECK(area > 0.0f);
2031 int zero_area_count_diamond = 0;
2032 for (
uint uuid: sphere_uuids) {
2033 float area = ctx.getPrimitiveArea(uuid);
2035 zero_area_count_diamond++;
2038 DOCTEST_CHECK(zero_area_count_diamond == 0);
2040 int zero_area_count_disk = 0;
2041 for (
uint uuid: sphere_disk_uuids) {
2042 float area = ctx.getPrimitiveArea(uuid);
2044 zero_area_count_disk++;
2047 DOCTEST_CHECK(zero_area_count_disk == 0);
2050 std::vector<uint> solid_sphere_uuids = ctx.addSphere(20,
make_vec3(4, 0, 0), 1.0f, RGB::green);
2052 int zero_area_count_solid = 0;
2053 for (
uint uuid: solid_sphere_uuids) {
2054 float area = ctx.getPrimitiveArea(uuid);
2056 zero_area_count_solid++;
2059 DOCTEST_CHECK(zero_area_count_solid == 0);
2062 SUBCASE(
"texture transparency validation preserves object integrity") {
2066 std::vector<uint> sphere_uuids = ctx.addSphere(15,
make_vec3(0, 0, 0), 1.0f,
"lib/images/diamond_texture.png");
2069 for (
uint uuid: sphere_uuids) {
2070 DOCTEST_CHECK(ctx.doesPrimitiveExist(uuid));
2071 DOCTEST_CHECK(ctx.getPrimitiveType(uuid) == PRIMITIVE_TYPE_TRIANGLE);
2073 float area = ctx.getPrimitiveArea(uuid);
2074 DOCTEST_CHECK(area > 0.0f);
2075 DOCTEST_CHECK(area > 1e-10f);
2078 float solid_fraction = ctx.getPrimitiveSolidFraction(uuid);
2079 DOCTEST_CHECK(solid_fraction > 0.0f);
2080 DOCTEST_CHECK(solid_fraction <= 1.0f);
2084 std::vector<uint> all_uuids = ctx.getAllUUIDs();
2085 int total_zero_area = 0;
2086 int total_negative_area = 0;
2088 for (
uint uuid: all_uuids) {
2089 float area = ctx.getPrimitiveArea(uuid);
2094 total_negative_area++;
2099 DOCTEST_CHECK(total_zero_area == 0);
2100 DOCTEST_CHECK(total_negative_area == 0);
2103 for (
uint uuid: sphere_uuids) {
2104 float solid_fraction = ctx.getPrimitiveSolidFraction(uuid);
2105 DOCTEST_CHECK(solid_fraction >= 0.0f);
2106 DOCTEST_CHECK(solid_fraction <= 1.0f);
2109 if (ctx.getPrimitiveType(uuid) == PRIMITIVE_TYPE_TRIANGLE) {
2110 vec3 v0 = ctx.getTriangleVertex(uuid, 0);
2111 vec3 v1 = ctx.getTriangleVertex(uuid, 1);
2112 vec3 v2 = ctx.getTriangleVertex(uuid, 2);
2114 float effective_area = ctx.getPrimitiveArea(uuid);
2117 DOCTEST_CHECK(effective_area <= geometric_area + 1e-6f);
2118 DOCTEST_CHECK(effective_area > 0.0f);
2123 DOCTEST_SUBCASE(
"Test Other Primitive Methods Zero Area Validation") {
2128 std::vector<float> tube_radii = {0.1f, 0.15f, 0.1f};
2129 std::vector<uint> tube_uuids = ctx_other.addTube(8, tube_nodes, tube_radii,
"lib/images/diamond_texture.png");
2132 int tube_positive_area = 0, tube_zero_area = 0;
2133 for (
uint uuid: tube_uuids) {
2134 float area = ctx_other.getPrimitiveArea(uuid);
2135 DOCTEST_CHECK(area >= 0.0f);
2137 tube_positive_area++;
2143 DOCTEST_CHECK(tube_positive_area > 0);
2144 DOCTEST_CHECK(tube_zero_area == 0);
2150 int disk_positive_area = 0, disk_zero_area = 0;
2151 for (
uint uuid: disk_uuids) {
2152 float area = ctx_other.getPrimitiveArea(uuid);
2153 DOCTEST_CHECK(area >= 0.0f);
2155 disk_positive_area++;
2161 DOCTEST_CHECK(disk_positive_area > 0);
2162 DOCTEST_CHECK(disk_zero_area == 0);
2165 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");
2168 int cone_positive_area = 0, cone_zero_area = 0;
2169 for (
uint uuid: cone_uuids) {
2170 float area = ctx_other.getPrimitiveArea(uuid);
2171 DOCTEST_CHECK(area >= 0.0f);
2173 cone_positive_area++;
2179 DOCTEST_CHECK(cone_positive_area > 0);
2180 DOCTEST_CHECK(cone_zero_area == 0);
2186 int tile_positive_area = 0, tile_zero_area = 0;
2187 for (
uint uuid: tile_uuids) {
2188 float area = ctx_other.getPrimitiveArea(uuid);
2189 DOCTEST_CHECK(area >= 0.0f);
2191 tile_positive_area++;
2197 DOCTEST_CHECK(tile_positive_area > 0);
2198 DOCTEST_CHECK(tile_zero_area == 0);
2202 DOCTEST_SUBCASE(
"Test Compound Object Methods Zero Area Validation") {
2203 Context ctx_compound;
2206 uint sphere_obj = ctx_compound.addSphereObject(8,
make_vec3(0, 0, 0), 0.5f,
"lib/images/diamond_texture.png");
2207 std::vector<uint> sphere_primitives = ctx_compound.getObjectPrimitiveUUIDs(sphere_obj);
2210 int sphere_positive_area = 0, sphere_zero_area = 0;
2211 for (
uint uuid: sphere_primitives) {
2212 float area = ctx_compound.getPrimitiveArea(uuid);
2213 DOCTEST_CHECK(area >= 0.0f);
2215 sphere_positive_area++;
2221 DOCTEST_CHECK(sphere_positive_area > 0);
2222 DOCTEST_CHECK(sphere_zero_area == 0);
2226 std::vector<float> tube_radii = {0.1f, 0.15f, 0.1f};
2227 uint tube_obj = ctx_compound.addTubeObject(8, tube_nodes, tube_radii,
"lib/images/diamond_texture.png");
2228 std::vector<uint> tube_primitives = ctx_compound.getObjectPrimitiveUUIDs(tube_obj);
2231 int tube_positive_area = 0, tube_zero_area = 0;
2232 for (
uint uuid: tube_primitives) {
2233 float area = ctx_compound.getPrimitiveArea(uuid);
2234 DOCTEST_CHECK(area >= 0.0f);
2236 tube_positive_area++;
2242 DOCTEST_CHECK(tube_positive_area > 0);
2243 DOCTEST_CHECK(tube_zero_area == 0);
2247 std::vector<uint> disk_primitives = ctx_compound.getObjectPrimitiveUUIDs(disk_obj);
2250 int disk_positive_area = 0, disk_zero_area = 0;
2251 for (
uint uuid: disk_primitives) {
2252 float area = ctx_compound.getPrimitiveArea(uuid);
2253 DOCTEST_CHECK(area >= 0.0f);
2255 disk_positive_area++;
2261 DOCTEST_CHECK(disk_positive_area > 0);
2262 DOCTEST_CHECK(disk_zero_area == 0);
2265 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");
2266 std::vector<uint> cone_primitives = ctx_compound.getObjectPrimitiveUUIDs(cone_obj);
2269 int cone_positive_area = 0, cone_zero_area = 0;
2270 for (
uint uuid: cone_primitives) {
2271 float area = ctx_compound.getPrimitiveArea(uuid);
2272 DOCTEST_CHECK(area >= 0.0f);
2274 cone_positive_area++;
2280 DOCTEST_CHECK(cone_positive_area > 0);
2281 DOCTEST_CHECK(cone_zero_area == 0);
2286TEST_CASE(
"File path resolution priority") {
2287 SUBCASE(
"resolveFilePath current directory priority") {
2292 std::string testFileName =
"test_file_resolution.jpg";
2293 std::filesystem::path currentDirFile = std::filesystem::current_path() / testFileName;
2296 std::filesystem::path sourceTexture =
"core/lib/models/texture.jpg";
2298 if (std::filesystem::exists(sourceTexture)) {
2300 std::filesystem::copy_file(sourceTexture, currentDirFile, std::filesystem::copy_options::overwrite_existing);
2301 DOCTEST_CHECK(std::filesystem::exists(currentDirFile));
2305 DOCTEST_CHECK(resolved == std::filesystem::canonical(currentDirFile));
2308 std::filesystem::remove(currentDirFile);
2312 SUBCASE(
"addPatch with texture from current directory") {
2316 std::filesystem::create_directories(
"test_models");
2317 std::string testTexture =
"test_models/test_texture.jpg";
2318 std::filesystem::path testTexturePath = std::filesystem::current_path() / testTexture;
2321 std::filesystem::path sourceTexture =
"core/lib/models/texture.jpg";
2323 if (std::filesystem::exists(sourceTexture)) {
2324 std::filesystem::copy_file(sourceTexture, testTexturePath, std::filesystem::copy_options::overwrite_existing);
2330 DOCTEST_CHECK_NOTHROW({ patch_id = ctx.addPatch(
make_vec3(0, 0, 0),
make_vec2(1, 1), rotation, testTexture.c_str()); });
2331 DOCTEST_CHECK(patch_id > 0);
2334 bool has_transparency = ctx.primitiveTextureHasTransparencyChannel(patch_id);
2335 DOCTEST_CHECK((has_transparency || !has_transparency));
2338 std::filesystem::remove(testTexturePath);
2339 std::filesystem::remove(
"test_models");