1.3.64
 
Loading...
Searching...
No Matches
Test_data.h
1#pragma once
2// =================================================================================
3// Suite 5: Context Data
4//
5// Tests for managing primitive, object, and global data within the Context class.
6// =================================================================================
7TEST_CASE("Global Data") {
8 Context ctx;
9
10 SUBCASE("Set, Get, Exists, List, Clear") {
11 ctx.setGlobalData("test_int", 123);
12 ctx.setGlobalData("test_float", 456.789f);
13 ctx.setGlobalData("test_string", "hello");
14 std::vector<double> double_vec = {1.1, 2.2, 3.3};
15 ctx.setGlobalData("test_double_vec", double_vec);
16
17 DOCTEST_CHECK(ctx.doesGlobalDataExist("test_int"));
18 DOCTEST_CHECK(ctx.doesGlobalDataExist("test_float"));
19 DOCTEST_CHECK(ctx.doesGlobalDataExist("test_string"));
20 DOCTEST_CHECK(ctx.doesGlobalDataExist("test_double_vec"));
21 DOCTEST_CHECK(!ctx.doesGlobalDataExist("non_existent"));
22
23 int i;
24 ctx.getGlobalData("test_int", i);
25 DOCTEST_CHECK(i == 123);
26
27 float f;
28 ctx.getGlobalData("test_float", f);
29 DOCTEST_CHECK(f == doctest::Approx(456.789f));
30
31 std::string s;
32 ctx.getGlobalData("test_string", s);
33 DOCTEST_CHECK(s == "hello");
34
35 std::vector<double> dv;
36 ctx.getGlobalData("test_double_vec", dv);
37 DOCTEST_CHECK(dv.size() == 3);
38 DOCTEST_CHECK(dv[1] == doctest::Approx(2.2));
39
40 std::vector<std::string> labels = ctx.listGlobalData();
41 DOCTEST_CHECK(labels.size() == 4);
42
43 ctx.clearGlobalData("test_int");
44 DOCTEST_CHECK(!ctx.doesGlobalDataExist("test_int"));
45 }
46
47 SUBCASE("Management") {
48 ctx.setGlobalData("g_int", 5);
49 DOCTEST_CHECK(ctx.getGlobalDataType("g_int") == HELIOS_TYPE_INT);
50 DOCTEST_CHECK(ctx.getGlobalDataSize("g_int") == 1);
51
52 ctx.duplicateGlobalData("g_int", "g_int_copy");
53 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int_copy"));
54
55 ctx.renameGlobalData("g_int", "g_int_new");
56 DOCTEST_CHECK(!ctx.doesGlobalDataExist("g_int"));
57 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int_new"));
58 }
59
60 SUBCASE("Increment") {
61 ctx.setGlobalData("inc_me_int", 10);
62 ctx.incrementGlobalData("inc_me_int", 5);
63 int val_i;
64 ctx.getGlobalData("inc_me_int", val_i);
65 DOCTEST_CHECK(val_i == 15);
66
67 ctx.setGlobalData("inc_me_uint", std::vector<uint>{1});
68 ctx.incrementGlobalData("inc_me_uint", (uint) 1);
69 std::vector<uint> val_u;
70 ctx.getGlobalData("inc_me_uint", val_u);
71 DOCTEST_CHECK(val_u[0] == 2);
72
73 ctx.setGlobalData("inc_me_float", std::vector<float>{1.f});
74 ctx.incrementGlobalData("inc_me_float", 1.f);
75 std::vector<float> val_f;
76 ctx.getGlobalData("inc_me_float", val_f);
77 DOCTEST_CHECK(val_f[0] == doctest::Approx(2.f));
78
79 ctx.setGlobalData("inc_me_double", std::vector<double>{1.0});
80 ctx.incrementGlobalData("inc_me_double", 1.0);
81 std::vector<double> val_d;
82 ctx.getGlobalData("inc_me_double", val_d);
83 DOCTEST_CHECK(val_d[0] == doctest::Approx(2.0));
84 }
85
86 SUBCASE("All Vector Types") {
87 ctx.setGlobalData("g_uint_vec", std::vector<uint>{1});
88 ctx.setGlobalData("g_vec2_vec", std::vector<vec2>{make_vec2(1, 1)});
89 ctx.setGlobalData("g_vec3_vec", std::vector<vec3>{make_vec3(1, 1, 1)});
90 ctx.setGlobalData("g_vec4_vec", std::vector<vec4>{make_vec4(1, 1, 1, 1)});
91 ctx.setGlobalData("g_int2_vec", std::vector<int2>{make_int2(1, 1)});
92 ctx.setGlobalData("g_int3_vec", std::vector<int3>{make_int3(1, 1, 1)});
93 ctx.setGlobalData("g_int4_vec", std::vector<int4>{make_int4(1, 1, 1, 1)});
94 ctx.setGlobalData("g_string_vec", std::vector<std::string>{"hello"});
95
96 ctx.duplicateGlobalData("g_uint_vec", "g_uint_vec_copy");
97 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_uint_vec_copy"));
98 ctx.duplicateGlobalData("g_vec2_vec", "g_vec2_vec_copy");
99 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec2_vec_copy"));
100 ctx.duplicateGlobalData("g_vec3_vec", "g_vec3_vec_copy");
101 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec3_vec_copy"));
102 ctx.duplicateGlobalData("g_vec4_vec", "g_vec4_vec_copy");
103 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec4_vec_copy"));
104 ctx.duplicateGlobalData("g_int2_vec", "g_int2_vec_copy");
105 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int2_vec_copy"));
106 ctx.duplicateGlobalData("g_int3_vec", "g_int3_vec_copy");
107 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int3_vec_copy"));
108 ctx.duplicateGlobalData("g_int4_vec", "g_int4_vec_copy");
109 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int4_vec_copy"));
110 ctx.duplicateGlobalData("g_string_vec", "g_string_vec_copy");
111 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_string_vec_copy"));
112 }
113
114 SUBCASE("Global data error handling and all types") {
115 Context ctx;
116 capture_cerr cerr_buffer;
117 DOCTEST_CHECK_THROWS(ctx.getGlobalDataType("non_existent"));
118 DOCTEST_CHECK_THROWS(ctx.getGlobalDataSize("non_existent"));
119 DOCTEST_CHECK_THROWS(ctx.renameGlobalData("non_existent", "new"));
120 DOCTEST_CHECK_THROWS(ctx.duplicateGlobalData("non_existent", "new"));
121 DOCTEST_CHECK_THROWS(ctx.incrementGlobalData("non_existent", 1));
122
123 ctx.setGlobalData("g_uint", std::vector<uint>{1});
124 ctx.setGlobalData("g_float", std::vector<float>{1.f});
125 ctx.setGlobalData("g_double", std::vector<double>{1.0});
126 ctx.setGlobalData("g_vec2", std::vector<vec2>{make_vec2(1, 1)});
127 ctx.setGlobalData("g_vec3", std::vector<vec3>{make_vec3(1, 1, 1)});
128 ctx.setGlobalData("g_vec4", std::vector<vec4>{make_vec4(1, 1, 1, 1)});
129 ctx.setGlobalData("g_int2", std::vector<int2>{make_int2(1, 1)});
130 ctx.setGlobalData("g_int3", std::vector<int3>{make_int3(1, 1, 1)});
131 ctx.setGlobalData("g_int4", std::vector<int4>{make_int4(1, 1, 1, 1)});
132 ctx.setGlobalData("g_string", std::vector<std::string>{"hello"});
133
134 ctx.duplicateGlobalData("g_uint", "g_uint_copy");
135 ctx.duplicateGlobalData("g_float", "g_float_copy");
136 ctx.duplicateGlobalData("g_double", "g_double_copy");
137 ctx.duplicateGlobalData("g_vec2", "g_vec2_copy");
138 ctx.duplicateGlobalData("g_vec3", "g_vec3_copy");
139 ctx.duplicateGlobalData("g_vec4", "g_vec4_copy");
140 ctx.duplicateGlobalData("g_int2", "g_int2_copy");
141 ctx.duplicateGlobalData("g_int3", "g_int3_copy");
142 ctx.duplicateGlobalData("g_int4", "g_int4_copy");
143 ctx.duplicateGlobalData("g_string", "g_string_copy");
144
145 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_uint_copy"));
146 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_float_copy"));
147 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_double_copy"));
148 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec2_copy"));
149 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec3_copy"));
150 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_vec4_copy"));
151 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int2_copy"));
152 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int3_copy"));
153 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_int4_copy"));
154 DOCTEST_CHECK(ctx.doesGlobalDataExist("g_string_copy"));
155
156 ctx.incrementGlobalData("g_uint", (uint) 1);
157 ctx.incrementGlobalData("g_float", 1.f);
158 ctx.incrementGlobalData("g_double", 1.0);
159
160 std::vector<uint> g_uint;
161 ctx.getGlobalData("g_uint", g_uint);
162 DOCTEST_CHECK(g_uint[0] == 2);
163 }
164}
165
166TEST_CASE("Object Data") {
167 Context ctx;
168 uint o1 = ctx.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
169 uint o2 = ctx.addTileObject(nullorigin, make_vec2(1, 1), nullrotation, make_int2(2, 2));
170
171 SUBCASE("Set, Get, Exists, List, Clear") {
172 ctx.setObjectData(o1, "test_uint", (uint) 42);
173 std::vector<vec3> vec_data = {make_vec3(1, 2, 3), make_vec3(4, 5, 6)};
174 ctx.setObjectData(o1, "test_vec3_vec", vec_data);
175
176 DOCTEST_CHECK(ctx.doesObjectDataExist(o1, "test_uint"));
177 DOCTEST_CHECK(ctx.doesObjectDataExist(o1, "test_vec3_vec"));
178 DOCTEST_CHECK(!ctx.doesObjectDataExist(o1, "non_existent"));
179
180 uint ui;
181 ctx.getObjectData(o1, "test_uint", ui);
182 DOCTEST_CHECK(ui == 42);
183
184 std::vector<vec3> v_r;
185 ctx.getObjectData(o1, "test_vec3_vec", v_r);
186 DOCTEST_CHECK(v_r.size() == 2);
187 DOCTEST_CHECK(v_r[1].y == doctest::Approx(5.0));
188
189 DOCTEST_CHECK(ctx.getObjectDataType("test_uint") == HELIOS_TYPE_UINT);
190 DOCTEST_CHECK(ctx.getObjectDataSize(o1, "test_vec3_vec") == 2);
191
192 std::vector<std::string> labels = ctx.listObjectData(o1);
193 DOCTEST_CHECK(labels.size() == 2);
194
195 ctx.clearObjectData(o1, "test_uint");
196 DOCTEST_CHECK(!ctx.doesObjectDataExist(o1, "test_uint"));
197 }
198
199 SUBCASE("Management") {
200 ctx.setObjectData(o1, "my_data", 10);
201
202 ctx.copyObjectData(o1, o2);
203 DOCTEST_CHECK(ctx.doesObjectDataExist(o2, "my_data"));
204
205 ctx.renameObjectData(o1, "my_data", "new_name");
206 DOCTEST_CHECK(!ctx.doesObjectDataExist(o1, "my_data"));
207 DOCTEST_CHECK(ctx.doesObjectDataExist(o1, "new_name"));
208
209 ctx.duplicateObjectData(o2, "my_data", "my_data_copy");
210 DOCTEST_CHECK(ctx.doesObjectDataExist(o2, "my_data_copy"));
211
212 std::vector<std::string> all_obj_labels = ctx.listAllObjectDataLabels();
213 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(), "my_data") != all_obj_labels.end());
214 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(), "new_name") != all_obj_labels.end());
215 DOCTEST_CHECK(std::find(all_obj_labels.begin(), all_obj_labels.end(), "my_data_copy") != all_obj_labels.end());
216 }
217
218 SUBCASE("Object data error handling") {
219 Context ctx;
220 capture_cerr cerr_buffer;
221 uint bad_oid = 999;
222 DOCTEST_CHECK_THROWS(ctx.getObjectDataType("test"));
223 DOCTEST_CHECK_THROWS(ctx.getObjectDataSize(bad_oid, "test"));
224 DOCTEST_CHECK_THROWS(ctx.doesObjectDataExist(bad_oid, "test"));
225 DOCTEST_CHECK_THROWS(ctx.clearObjectData(bad_oid, "test"));
226 DOCTEST_CHECK_THROWS(ctx.copyObjectData(0, bad_oid));
227 DOCTEST_CHECK_THROWS(ctx.copyObjectData(bad_oid, 0));
228 DOCTEST_CHECK_THROWS(ctx.renameObjectData(bad_oid, "old", "new"));
229 DOCTEST_CHECK_THROWS(ctx.duplicateObjectData(bad_oid, "old", "new"));
230 }
231
232 SUBCASE("setObjectDataFromPrimitiveDataMean") {
233 Context ctx;
234
235 // Test with float data
236 uint tile_obj = ctx.addTileObject(nullorigin, make_vec2(2, 2), nullrotation, make_int2(2, 2));
237 std::vector<uint> prims = ctx.getObjectPrimitiveUUIDs(tile_obj);
238 DOCTEST_CHECK(prims.size() == 4);
239
240 ctx.setPrimitiveData(prims[0], "temperature", 10.0f);
241 ctx.setPrimitiveData(prims[1], "temperature", 20.0f);
242 ctx.setPrimitiveData(prims[2], "temperature", 30.0f);
243 ctx.setPrimitiveData(prims[3], "temperature", 40.0f);
244
245 ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "temperature");
246
247 DOCTEST_CHECK(ctx.doesObjectDataExist(tile_obj, "temperature"));
248 float temp_mean;
249 ctx.getObjectData(tile_obj, "temperature", temp_mean);
250 DOCTEST_CHECK(temp_mean == doctest::Approx(25.0f));
251
252 // Test with double data
253 uint box_obj = ctx.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
254 std::vector<uint> box_prims = ctx.getObjectPrimitiveUUIDs(box_obj);
255
256 ctx.setPrimitiveData(box_prims[0], "value", 1.5);
257 ctx.setPrimitiveData(box_prims[1], "value", 2.5);
258 ctx.setPrimitiveData(box_prims[2], "value", 3.5);
259 ctx.setPrimitiveData(box_prims[3], "value", 4.5);
260 ctx.setPrimitiveData(box_prims[4], "value", 5.5);
261 ctx.setPrimitiveData(box_prims[5], "value", 6.5);
262
263 ctx.setObjectDataFromPrimitiveDataMean(box_obj, "value");
264
265 double value_mean;
266 ctx.getObjectData(box_obj, "value", value_mean);
267 DOCTEST_CHECK(value_mean == doctest::Approx(4.0));
268
269 // Test with vec2 data
270 uint cone_obj = ctx.addConeObject(10, make_vec3(0, 0, 0), make_vec3(0, 0, 1), 1.0f, 0.5f);
271 std::vector<uint> cone_prims = ctx.getObjectPrimitiveUUIDs(cone_obj);
272
273 for (size_t i = 0; i < cone_prims.size(); ++i) {
274 ctx.setPrimitiveData(cone_prims[i], "vec2_data", make_vec2(float(i), float(i) * 2.0f));
275 }
276
277 ctx.setObjectDataFromPrimitiveDataMean(cone_obj, "vec2_data");
278
279 vec2 vec2_mean;
280 ctx.getObjectData(cone_obj, "vec2_data", vec2_mean);
281 float expected_x = float(cone_prims.size() - 1) / 2.0f;
282 DOCTEST_CHECK(vec2_mean.x == doctest::Approx(expected_x));
283 DOCTEST_CHECK(vec2_mean.y == doctest::Approx(expected_x * 2.0f));
284
285 // Test with vec3 data
286 ctx.setPrimitiveData(prims[0], "color", make_vec3(1.0f, 0.0f, 0.0f));
287 ctx.setPrimitiveData(prims[1], "color", make_vec3(0.0f, 1.0f, 0.0f));
288 ctx.setPrimitiveData(prims[2], "color", make_vec3(0.0f, 0.0f, 1.0f));
289 ctx.setPrimitiveData(prims[3], "color", make_vec3(1.0f, 1.0f, 1.0f));
290
291 ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "color");
292
293 vec3 color_mean;
294 ctx.getObjectData(tile_obj, "color", color_mean);
295 DOCTEST_CHECK(color_mean.x == doctest::Approx(0.5f));
296 DOCTEST_CHECK(color_mean.y == doctest::Approx(0.5f));
297 DOCTEST_CHECK(color_mean.z == doctest::Approx(0.5f));
298
299 // Test with vec4 data
300 ctx.setPrimitiveData(prims[0], "rgba", make_vec4(1.0f, 0.0f, 0.0f, 1.0f));
301 ctx.setPrimitiveData(prims[1], "rgba", make_vec4(0.0f, 1.0f, 0.0f, 0.8f));
302
303 ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "rgba");
304
305 vec4 rgba_mean;
306 ctx.getObjectData(tile_obj, "rgba", rgba_mean);
307 DOCTEST_CHECK(rgba_mean.x == doctest::Approx(0.5f));
308 DOCTEST_CHECK(rgba_mean.y == doctest::Approx(0.5f));
309 DOCTEST_CHECK(rgba_mean.z == doctest::Approx(0.0f));
310 DOCTEST_CHECK(rgba_mean.w == doctest::Approx(0.9f));
311
312 // Test with partial data (not all primitives have the data)
313 ctx.setPrimitiveData(prims[0], "partial", 100.0f);
314 ctx.setPrimitiveData(prims[2], "partial", 200.0f);
315
316 ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "partial");
317
318 float partial_mean;
319 ctx.getObjectData(tile_obj, "partial", partial_mean);
320 DOCTEST_CHECK(partial_mean == doctest::Approx(150.0f));
321
322 // Error condition: invalid object ID
323 DOCTEST_CHECK_THROWS(ctx.setObjectDataFromPrimitiveDataMean(999, "temperature"));
324
325 // Error condition: no primitives have the data
326 DOCTEST_CHECK_THROWS(ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "nonexistent_data"));
327
328 // Error condition: unsupported data type (int)
329 ctx.setPrimitiveData(prims[0], "int_data", 42);
330 DOCTEST_CHECK_THROWS(ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "int_data"));
331
332 // Error condition: unsupported data type (string)
333 ctx.setPrimitiveData(prims[0], "string_data", "hello");
334 DOCTEST_CHECK_THROWS(ctx.setObjectDataFromPrimitiveDataMean(tile_obj, "string_data"));
335 }
336}
337
338TEST_CASE("Primitive Data") {
339 Context ctx;
340 uint p1 = ctx.addPatch();
341 uint p2 = ctx.addPatch();
342 std::vector<uint> uuids;
343 for (int i = 0; i < 5; ++i) {
344 uint p = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1));
345 uuids.push_back(p);
346 }
347
348 SUBCASE("Set and Get All Data Types") {
349 // Test all supported scalar types
350 ctx.setPrimitiveData(p1, "test_int", 1);
351 ctx.setPrimitiveData(p1, "test_uint", 1u);
352 ctx.setPrimitiveData(p1, "test_float", 2.f);
353 ctx.setPrimitiveData(p1, "test_double", 2.0);
354 ctx.setPrimitiveData(p1, "test_vec2", make_vec2(1, 2));
355 ctx.setPrimitiveData(p1, "test_vec3", make_vec3(1, 2, 3));
356 ctx.setPrimitiveData(p1, "test_vec4", make_vec4(1, 2, 3, 4));
357 ctx.setPrimitiveData(p1, "test_int2", make_int2(1, 2));
358 ctx.setPrimitiveData(p1, "test_int3", make_int3(1, 2, 3));
359 ctx.setPrimitiveData(p1, "test_int4", make_int4(1, 2, 3, 4));
360 ctx.setPrimitiveData(p1, "test_string", "world");
361
362 int r_i;
363 ctx.getPrimitiveData(p1, "test_int", r_i);
364 DOCTEST_CHECK(r_i == 1);
365 uint r_ui;
366 ctx.getPrimitiveData(p1, "test_uint", r_ui);
367 DOCTEST_CHECK(r_ui == 1);
368 float r_f;
369 ctx.getPrimitiveData(p1, "test_float", r_f);
370 DOCTEST_CHECK(r_f == doctest::Approx(2.0));
371 double r_d;
372 ctx.getPrimitiveData(p1, "test_double", r_d);
373 DOCTEST_CHECK(r_d == doctest::Approx(2.0));
374 vec2 r_v2;
375 ctx.getPrimitiveData(p1, "test_vec2", r_v2);
376 DOCTEST_CHECK(r_v2 == make_vec2(1, 2));
377 vec3 r_v3;
378 ctx.getPrimitiveData(p1, "test_vec3", r_v3);
379 DOCTEST_CHECK(r_v3 == make_vec3(1, 2, 3));
380 vec4 r_v4;
381 ctx.getPrimitiveData(p1, "test_vec4", r_v4);
382 DOCTEST_CHECK(r_v4 == make_vec4(1, 2, 3, 4));
383 int2 r_i2;
384 ctx.getPrimitiveData(p1, "test_int2", r_i2);
385 DOCTEST_CHECK(r_i2 == make_int2(1, 2));
386 int3 r_i3;
387 ctx.getPrimitiveData(p1, "test_int3", r_i3);
388 DOCTEST_CHECK(r_i3 == make_int3(1, 2, 3));
389 int4 r_i4;
390 ctx.getPrimitiveData(p1, "test_int4", r_i4);
391 DOCTEST_CHECK(r_i4 == make_int4(1, 2, 3, 4));
392 std::string r_s;
393 ctx.getPrimitiveData(p1, "test_string", r_s);
394 DOCTEST_CHECK(r_s == "world");
395
396 // Test all supported vector types
397 std::vector<int> v_i = {1, 2, 3};
398 ctx.setPrimitiveData(p1, "test_v_int", v_i);
399 std::vector<int> r_v_i;
400 ctx.getPrimitiveData(p1, "test_v_int", r_v_i);
401 DOCTEST_CHECK(r_v_i == v_i);
402 std::vector<uint> v_ui = {4, 5, 6};
403 ctx.setPrimitiveData(p1, "test_v_uint", v_ui);
404 std::vector<uint> r_v_ui;
405 ctx.getPrimitiveData(p1, "test_v_uint", r_v_ui);
406 DOCTEST_CHECK(r_v_ui == v_ui);
407 std::vector<float> v_f = {1.1, 2.2};
408 ctx.setPrimitiveData(p1, "test_v_float", v_f);
409 std::vector<float> r_v_f;
410 ctx.getPrimitiveData(p1, "test_v_float", r_v_f);
411 DOCTEST_CHECK(r_v_f == v_f);
412 std::vector<double> v_d = {1.1, 2.2};
413 ctx.setPrimitiveData(p1, "test_v_double", v_d);
414 std::vector<double> r_v_d;
415 ctx.getPrimitiveData(p1, "test_v_double", r_v_d);
416 DOCTEST_CHECK(r_v_d == v_d);
417 std::vector<vec2> v_v2 = {make_vec2(1, 2), make_vec2(3, 4)};
418 ctx.setPrimitiveData(p1, "test_v_vec2", v_v2);
419 std::vector<vec2> r_v_v2;
420 ctx.getPrimitiveData(p1, "test_v_vec2", r_v_v2);
421 DOCTEST_CHECK(r_v_v2 == v_v2);
422 std::vector<vec3> v_v3 = {make_vec3(1, 2, 3), make_vec3(4, 5, 6)};
423 ctx.setPrimitiveData(p1, "test_v_vec3", v_v3);
424 std::vector<vec3> r_v_v3;
425 ctx.getPrimitiveData(p1, "test_v_vec3", r_v_v3);
426 DOCTEST_CHECK(r_v_v3 == v_v3);
427 std::vector<vec4> v_v4 = {make_vec4(1, 2, 3, 4), make_vec4(5, 6, 7, 8)};
428 ctx.setPrimitiveData(p1, "test_v_vec4", v_v4);
429 std::vector<vec4> r_v_v4;
430 ctx.getPrimitiveData(p1, "test_v_vec4", r_v_v4);
431 DOCTEST_CHECK(r_v_v4 == v_v4);
432 std::vector<int2> v_i2 = {make_int2(1, 2), make_int2(3, 4)};
433 ctx.setPrimitiveData(p1, "test_v_int2", v_i2);
434 std::vector<int2> r_v_i2;
435 ctx.getPrimitiveData(p1, "test_v_int2", r_v_i2);
436 DOCTEST_CHECK(r_v_i2 == v_i2);
437 std::vector<int3> v_i3 = {make_int3(1, 2, 3), make_int3(4, 5, 6)};
438 ctx.setPrimitiveData(p1, "test_v_int3", v_i3);
439 std::vector<int3> r_v_i3;
440 ctx.getPrimitiveData(p1, "test_v_int3", r_v_i3);
441 DOCTEST_CHECK(r_v_i3 == v_i3);
442 std::vector<int4> v_i4 = {make_int4(1, 2, 3, 4), make_int4(5, 6, 7, 8)};
443 ctx.setPrimitiveData(p1, "test_v_int4", v_i4);
444 std::vector<int4> r_v_i4;
445 ctx.getPrimitiveData(p1, "test_v_int4", r_v_i4);
446 DOCTEST_CHECK(r_v_i4 == v_i4);
447 std::vector<std::string> v_s = {"hello", "world"};
448 ctx.setPrimitiveData(p1, "test_v_string", v_s);
449 std::vector<std::string> r_v_s;
450 ctx.getPrimitiveData(p1, "test_v_string", r_v_s);
451 DOCTEST_CHECK(r_v_s == v_s);
452 }
453
454 SUBCASE("Management") {
455 ctx.setPrimitiveData(p1, "my_data", 10);
456 ctx.copyPrimitiveData(p1, p2);
457 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2, "my_data"));
458 int val;
459 ctx.getPrimitiveData(p2, "my_data", val);
460 DOCTEST_CHECK(val == 10);
461
462 ctx.renamePrimitiveData(p1, "my_data", "new_data_name");
463 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p1, "my_data"));
464 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1, "new_data_name"));
465
466 ctx.duplicatePrimitiveData(p2, "my_data", "my_data_copy");
467 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p2, "my_data_copy"));
468 ctx.getPrimitiveData(p2, "my_data_copy", val);
469 DOCTEST_CHECK(val == 10);
470
471 ctx.setPrimitiveData(p1, "global_copy_test", 5.5f);
472 ctx.duplicatePrimitiveData("global_copy_test", "global_copy_test_new");
473 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p1, "global_copy_test_new"));
474 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p2, "global_copy_test_new"));
475
476 std::vector<std::string> all_labels = ctx.listAllPrimitiveDataLabels();
477 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(), "my_data") != all_labels.end());
478 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(), "my_data_copy") != all_labels.end());
479 DOCTEST_CHECK(std::find(all_labels.begin(), all_labels.end(), "new_data_name") != all_labels.end());
480
481 ctx.clearPrimitiveData(p1, "new_data_name");
482 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p1, "new_data_name"));
483 ctx.clearPrimitiveData(uuids, "global_copy_test");
484 }
485
486 SUBCASE("Calculations") {
487 for (int i = 0; i < 5; ++i) {
488 ctx.setPrimitiveData(uuids[i], "float_val", (float) i);
489 ctx.setPrimitiveData(uuids[i], "double_val", (double) i);
490 ctx.setPrimitiveData(uuids[i], "vec2_val", make_vec2((float) i, (float) i));
491 }
492
493 float float_mean;
494 ctx.calculatePrimitiveDataMean(uuids, "float_val", float_mean);
495 DOCTEST_CHECK(float_mean == doctest::Approx(2.0f));
496 double double_mean;
497 ctx.calculatePrimitiveDataMean(uuids, "double_val", double_mean);
498 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
499 vec2 vec2_mean;
500 ctx.calculatePrimitiveDataMean(uuids, "vec2_val", vec2_mean);
501 DOCTEST_CHECK(vec2_mean.x == doctest::Approx(2.0f));
502
503 float awt_mean_f;
504 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "float_val", awt_mean_f);
505 DOCTEST_CHECK(awt_mean_f == doctest::Approx(2.0f));
506
507 float float_sum;
508 ctx.calculatePrimitiveDataSum(uuids, "float_val", float_sum);
509 DOCTEST_CHECK(float_sum == doctest::Approx(10.0f));
510
511 float awt_sum_f;
512 ctx.calculatePrimitiveDataAreaWeightedSum(uuids, "float_val", awt_sum_f);
513 DOCTEST_CHECK(awt_sum_f == doctest::Approx(10.0f));
514
515 ctx.scalePrimitiveData(uuids, "float_val", 2.0f);
516 ctx.getPrimitiveData(uuids[2], "float_val", float_mean);
517 DOCTEST_CHECK(float_mean == doctest::Approx(4.0f));
518 capture_cerr cerr_buffer;
519 ctx.scalePrimitiveData("double_val", 0.5f);
520 ctx.getPrimitiveData(uuids[4], "double_val", double_mean);
521 DOCTEST_CHECK(double_mean == doctest::Approx(2.0));
522 DOCTEST_CHECK(cerr_buffer.has_output());
523
524 ctx.setPrimitiveData(uuids, "int_val", 10);
525 ctx.incrementPrimitiveData(uuids, "int_val", 5);
526 int int_val;
527 ctx.getPrimitiveData(uuids[0], "int_val", int_val);
528 DOCTEST_CHECK(int_val == 15);
529 }
530
531 SUBCASE("Context::calculatePrimitiveDataMean empty list") {
532 Context ctx;
533 std::vector<uint> uuids;
534 float float_mean;
535 capture_cerr cerr_buffer;
536 ctx.calculatePrimitiveDataMean(uuids, "non_existent", float_mean);
537 DOCTEST_CHECK(float_mean == 0.f);
538 DOCTEST_CHECK(cerr_buffer.has_output());
539 }
540
541 SUBCASE("Context::calculatePrimitiveDataMean vec3/vec4") {
542 Context ctx;
543 uint p1 = ctx.addPatch();
544 ctx.setPrimitiveData(p1, "v3", make_vec3(1, 2, 3));
545 ctx.setPrimitiveData(p1, "v4", make_vec4(1, 2, 3, 4));
546 std::vector<uint> uuids = {p1};
547 vec3 v3_mean;
548 vec4 v4_mean;
549 ctx.calculatePrimitiveDataMean(uuids, "v3", v3_mean);
550 ctx.calculatePrimitiveDataMean(uuids, "v4", v4_mean);
551 DOCTEST_CHECK(v3_mean == make_vec3(1, 2, 3));
552 DOCTEST_CHECK(v4_mean == make_vec4(1, 2, 3, 4));
553 }
554
555 SUBCASE("Context::calculatePrimitiveDataAreaWeightedMean all types") {
556 Context ctx;
557 uint p = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(2, 2)); // Area = 4
558 ctx.setPrimitiveData(p, "double", 2.5);
559 ctx.setPrimitiveData(p, "vec2", make_vec2(1, 2));
560 ctx.setPrimitiveData(p, "vec3", make_vec3(1, 2, 3));
561 ctx.setPrimitiveData(p, "vec4", make_vec4(1, 2, 3, 4));
562 std::vector<uint> uuids = {p};
563
564 double d_mean;
565 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "double", d_mean);
566 DOCTEST_CHECK(d_mean == doctest::Approx(2.5));
567
568 vec2 v2_mean;
569 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec2", v2_mean);
570 DOCTEST_CHECK(v2_mean == make_vec2(1, 2));
571
572 vec3 v3_mean;
573 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec3", v3_mean);
574 DOCTEST_CHECK(v3_mean == make_vec3(1, 2, 3));
575
576 vec4 v4_mean;
577 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec4", v4_mean);
578 DOCTEST_CHECK(v4_mean == make_vec4(1, 2, 3, 4));
579 }
580
581 SUBCASE("Context::calculatePrimitiveDataSum all types") {
582 Context ctx;
583 uint p = ctx.addPatch();
584 ctx.setPrimitiveData(p, "double", 2.5);
585 ctx.setPrimitiveData(p, "vec2", make_vec2(1, 2));
586 ctx.setPrimitiveData(p, "vec3", make_vec3(1, 2, 3));
587 ctx.setPrimitiveData(p, "vec4", make_vec4(1, 2, 3, 4));
588 std::vector<uint> uuids = {p};
589
590 double d_sum;
591 ctx.calculatePrimitiveDataSum(uuids, "double", d_sum);
592 DOCTEST_CHECK(d_sum == doctest::Approx(2.5));
593
594 vec2 v2_sum;
595 ctx.calculatePrimitiveDataSum(uuids, "vec2", v2_sum);
596 DOCTEST_CHECK(v2_sum == make_vec2(1, 2));
597
598 vec3 v3_sum;
599 ctx.calculatePrimitiveDataSum(uuids, "vec3", v3_sum);
600 DOCTEST_CHECK(v3_sum == make_vec3(1, 2, 3));
601
602 vec4 v4_sum;
603 ctx.calculatePrimitiveDataSum(uuids, "vec4", v4_sum);
604 DOCTEST_CHECK(v4_sum == make_vec4(1, 2, 3, 4));
605 }
606
607 SUBCASE("Context::incrementPrimitiveData all types") {
608 Context ctx;
609 uint p = ctx.addPatch();
610 ctx.setPrimitiveData(p, "uint", (uint) 1);
611 ctx.setPrimitiveData(p, "float", 1.f);
612 ctx.setPrimitiveData(p, "double", 1.0);
613 std::vector<uint> uuids = {p};
614
615 ctx.incrementPrimitiveData(uuids, "uint", (uint) 2);
616 uint u_val;
617 ctx.getPrimitiveData(p, "uint", u_val);
618 DOCTEST_CHECK(u_val == 3);
619
620 ctx.incrementPrimitiveData(uuids, "float", 2.f);
621 float f_val;
622 ctx.getPrimitiveData(p, "float", f_val);
623 DOCTEST_CHECK(f_val == doctest::Approx(3.f));
624
625 ctx.incrementPrimitiveData(uuids, "double", 2.0);
626 double d_val;
627 ctx.getPrimitiveData(p, "double", d_val);
628 DOCTEST_CHECK(d_val == doctest::Approx(3.0));
629 }
630
631 SUBCASE("Aggregation and Filtering") {
632 std::vector<uint> agg_uuids;
633 for (int i = 0; i < 3; ++i) {
634 uint p = ctx.addPatch();
635 ctx.setPrimitiveData(p, "d1", (float) i);
636 ctx.setPrimitiveData(p, "d2", (float) i * 2.0f);
637 ctx.setPrimitiveData(p, "d3", (float) i * 3.0f);
638 ctx.setPrimitiveData(p, "filter_me", i);
639 agg_uuids.push_back(p);
640 }
641
642 std::vector<std::string> labels = {"d1", "d2", "d3"};
643 ctx.aggregatePrimitiveDataSum(agg_uuids, labels, "sum_data");
644 float sum_val;
645 ctx.getPrimitiveData(agg_uuids[1], "sum_data", sum_val);
646 DOCTEST_CHECK(sum_val == doctest::Approx(1.f + 2.f + 3.f));
647
648 ctx.aggregatePrimitiveDataProduct(agg_uuids, labels, "prod_data");
649 float prod_val;
650 ctx.getPrimitiveData(agg_uuids[2], "prod_data", prod_val);
651 DOCTEST_CHECK(prod_val == doctest::Approx(2.f * 4.f * 6.f));
652
653 std::vector<uint> filtered = ctx.filterPrimitivesByData(agg_uuids, "filter_me", 1, ">=");
654 DOCTEST_CHECK(filtered.size() == 2);
655 filtered = ctx.filterPrimitivesByData(agg_uuids, "filter_me", 1, "==");
656 DOCTEST_CHECK(filtered.size() == 1);
657 DOCTEST_CHECK(filtered[0] == agg_uuids[1]);
658 }
659
660 SUBCASE("Context::filterPrimitivesByData all types") {
661 Context ctx;
662 uint p = ctx.addPatch();
663 ctx.setPrimitiveData(p, "double", 1.0);
664 ctx.setPrimitiveData(p, "uint", (uint) 10);
665 std::vector<uint> uuids = {p};
666
667 auto filtered_d = ctx.filterPrimitivesByData(uuids, "double", 0.5, ">");
668 DOCTEST_CHECK(filtered_d.size() == 1);
669
670 auto filtered_u = ctx.filterPrimitivesByData(uuids, "uint", (uint) 5, ">");
671 DOCTEST_CHECK(filtered_u.size() == 1);
672 }
673
674 SUBCASE("Error Handling") {
675 capture_cerr cerr_buffer;
676 uint bad_uuid = 999;
677 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataType("test"));
678 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataSize(bad_uuid, "test"));
679 DOCTEST_CHECK_THROWS(ctx.doesPrimitiveDataExist(bad_uuid, "test"));
680 DOCTEST_CHECK_THROWS(ctx.clearPrimitiveData(bad_uuid, "test"));
681 DOCTEST_CHECK_THROWS(ctx.copyPrimitiveData(p1, bad_uuid));
682 DOCTEST_CHECK_THROWS(ctx.copyPrimitiveData(bad_uuid, p1));
683 DOCTEST_CHECK_THROWS(ctx.renamePrimitiveData(bad_uuid, "old", "new"));
684 DOCTEST_CHECK_THROWS(ctx.duplicatePrimitiveData(bad_uuid, "old", "new"));
685
686 std::vector<uint> empty_uuids;
687 float float_mean;
688 ctx.calculatePrimitiveDataMean(empty_uuids, "non_existent", float_mean);
689 DOCTEST_CHECK(float_mean == 0.f);
690 DOCTEST_CHECK(cerr_buffer.has_output());
691
692 ctx.setPrimitiveData(p1, "float_val_err", 1.f);
693 ctx.incrementPrimitiveData(std::vector<uint>{p1}, "float_val_err", 1); // Wrong type
694 DOCTEST_CHECK(cerr_buffer.has_output());
695
696 std::vector<uint> filtered_UUIDs;
697 DOCTEST_CHECK_THROWS_AS(filtered_UUIDs = ctx.filterPrimitivesByData(uuids, "filter_me", 1, "!!"), std::runtime_error);
698 }
699
700 SUBCASE("Primitive::getPrimitiveDataSize all types") {
701 Context ctx;
702 uint p = ctx.addPatch();
703 ctx.setPrimitiveData(p, "uint", (uint) 1);
704 ctx.setPrimitiveData(p, "double", 1.0);
705 ctx.setPrimitiveData(p, "vec2", make_vec2(1, 1));
706 ctx.setPrimitiveData(p, "vec3", make_vec3(1, 1, 1));
707 ctx.setPrimitiveData(p, "vec4", make_vec4(1, 1, 1, 1));
708 ctx.setPrimitiveData(p, "int2", make_int2(1, 1));
709 ctx.setPrimitiveData(p, "int3", make_int3(1, 1, 1));
710 ctx.setPrimitiveData(p, "int4", make_int4(1, 1, 1, 1));
711 ctx.setPrimitiveData(p, "string", "hello");
712
713 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "uint") == 1);
714 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "double") == 1);
715 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec2") == 1);
716 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec3") == 1);
717 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec4") == 1);
718 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "int2") == 1);
719 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "int3") == 1);
720 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "int4") == 1);
721 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "string") == 1);
722 }
723
724 SUBCASE("Primitive::clearPrimitiveData all types") {
725 Context ctx;
726 uint p = ctx.addPatch();
727 ctx.setPrimitiveData(p, "uint", (uint) 1);
728 ctx.setPrimitiveData(p, "double", 1.0);
729 ctx.setPrimitiveData(p, "vec2", make_vec2(1, 1));
730 ctx.setPrimitiveData(p, "vec3", make_vec3(1, 1, 1));
731 ctx.setPrimitiveData(p, "vec4", make_vec4(1, 1, 1, 1));
732 ctx.setPrimitiveData(p, "int2", make_int2(1, 1));
733 ctx.setPrimitiveData(p, "int3", make_int3(1, 1, 1));
734 ctx.setPrimitiveData(p, "int4", make_int4(1, 1, 1, 1));
735 ctx.setPrimitiveData(p, "string", "hello");
736
737 ctx.clearPrimitiveData(p, "uint");
738 ctx.clearPrimitiveData(p, "double");
739 ctx.clearPrimitiveData(p, "vec2");
740 ctx.clearPrimitiveData(p, "vec3");
741 ctx.clearPrimitiveData(p, "vec4");
742 ctx.clearPrimitiveData(p, "int2");
743 ctx.clearPrimitiveData(p, "int3");
744 ctx.clearPrimitiveData(p, "int4");
745 ctx.clearPrimitiveData(p, "string");
746
747 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "uint"));
748 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "double"));
749 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "vec2"));
750 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "vec3"));
751 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "vec4"));
752 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "int2"));
753 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "int3"));
754 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "int4"));
755 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p, "string"));
756 }
757
758 SUBCASE("Context data functions with invalid UUID") {
759 Context ctx;
760 capture_cerr cerr_buffer;
761 uint bad_uuid = 999;
762 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataType("test"));
763 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataSize(bad_uuid, "test"));
764 DOCTEST_CHECK_THROWS(ctx.doesPrimitiveDataExist(bad_uuid, "test"));
765 DOCTEST_CHECK_THROWS(ctx.clearPrimitiveData(bad_uuid, "test"));
766 DOCTEST_CHECK_THROWS(ctx.copyPrimitiveData(0, bad_uuid));
767 DOCTEST_CHECK_THROWS(ctx.copyPrimitiveData(bad_uuid, 0));
768 DOCTEST_CHECK_THROWS(ctx.renamePrimitiveData(bad_uuid, "old", "new"));
769 DOCTEST_CHECK_THROWS(ctx.duplicatePrimitiveData(bad_uuid, "old", "new"));
770 }
771
772 SUBCASE("Context::clearPrimitiveData for multiple UUIDs") {
773 Context ctx;
774 uint p1 = ctx.addPatch();
775 uint p2 = ctx.addPatch();
776 ctx.setPrimitiveData(p1, "data", 1);
777 ctx.setPrimitiveData(p2, "data", 2);
778 std::vector<uint> uuids = {p1, p2};
779 ctx.clearPrimitiveData(uuids, "data");
780 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p1, "data"));
781 DOCTEST_CHECK(!ctx.doesPrimitiveDataExist(p2, "data"));
782 }
783
784 SUBCASE("Context::duplicatePrimitiveData all types") {
785 Context ctx;
786 uint p = ctx.addPatch();
787 ctx.setPrimitiveData(p, "uint", (uint) 1);
788 ctx.setPrimitiveData(p, "double", 1.0);
789 ctx.setPrimitiveData(p, "vec2", make_vec2(1, 1));
790 ctx.setPrimitiveData(p, "vec3", make_vec3(1, 1, 1));
791 ctx.setPrimitiveData(p, "vec4", make_vec4(1, 1, 1, 1));
792 ctx.setPrimitiveData(p, "int2", make_int2(1, 1));
793 ctx.setPrimitiveData(p, "int3", make_int3(1, 1, 1));
794 ctx.setPrimitiveData(p, "int4", make_int4(1, 1, 1, 1));
795 ctx.setPrimitiveData(p, "string", "hello");
796
797 ctx.duplicatePrimitiveData(p, "uint", "uint_copy");
798 ctx.duplicatePrimitiveData(p, "double", "double_copy");
799 ctx.duplicatePrimitiveData(p, "vec2", "vec2_copy");
800 ctx.duplicatePrimitiveData(p, "vec3", "vec3_copy");
801 ctx.duplicatePrimitiveData(p, "vec4", "vec4_copy");
802 ctx.duplicatePrimitiveData(p, "int2", "int2_copy");
803 ctx.duplicatePrimitiveData(p, "int3", "int3_copy");
804 ctx.duplicatePrimitiveData(p, "int4", "int4_copy");
805 ctx.duplicatePrimitiveData(p, "string", "string_copy");
806
807 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "uint_copy"));
808 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "double_copy"));
809 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "vec2_copy"));
810 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "vec3_copy"));
811 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "vec4_copy"));
812 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "int2_copy"));
813 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "int3_copy"));
814 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "int4_copy"));
815 DOCTEST_CHECK(ctx.doesPrimitiveDataExist(p, "string_copy"));
816 }
817
818
819 SUBCASE("Generic Value-Level Caching System") {
820 Context ctx_test;
821
822 // Test error handling - should throw when caching not enabled
823 std::vector<std::string> test_values;
824 bool threw_error = false;
825 try {
826 ctx_test.getUniquePrimitiveDataValues("uncached_label", test_values);
827 } catch (const std::runtime_error &e) {
828 threw_error = true;
829 DOCTEST_CHECK(std::string(e.what()).find("Value-level caching is not enabled") != std::string::npos);
830 }
831 DOCTEST_CHECK(threw_error);
832
833 threw_error = false;
834 try {
835 ctx_test.getUniqueObjectDataValues("uncached_label", test_values);
836 } catch (const std::runtime_error &e) {
837 threw_error = true;
838 DOCTEST_CHECK(std::string(e.what()).find("Value-level caching is not enabled") != std::string::npos);
839 }
840 DOCTEST_CHECK(threw_error);
841
842 // Test cache configuration
843 DOCTEST_CHECK(!ctx_test.isPrimitiveDataValueCachingEnabled("test_string"));
844 DOCTEST_CHECK(!ctx_test.isObjectDataValueCachingEnabled("test_string"));
845
846 ctx_test.enablePrimitiveDataValueCaching("test_string");
847 ctx_test.enablePrimitiveDataValueCaching("test_int");
848 ctx_test.enablePrimitiveDataValueCaching("test_uint");
849 ctx_test.enableObjectDataValueCaching("obj_string");
850 ctx_test.enableObjectDataValueCaching("obj_int");
851 ctx_test.enableObjectDataValueCaching("obj_uint");
852
853 DOCTEST_CHECK(ctx_test.isPrimitiveDataValueCachingEnabled("test_string"));
854 DOCTEST_CHECK(ctx_test.isPrimitiveDataValueCachingEnabled("test_int"));
855 DOCTEST_CHECK(ctx_test.isPrimitiveDataValueCachingEnabled("test_uint"));
856 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("obj_string"));
857 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("obj_int"));
858 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("obj_uint"));
859
860 // Create test primitives and objects
861 uint patch1 = ctx_test.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1));
862 uint patch2 = ctx_test.addPatch(make_vec3(1, 0, 0), make_vec2(1, 1));
863 uint patch3 = ctx_test.addPatch(make_vec3(2, 0, 0), make_vec2(1, 1));
864 uint obj1 = ctx_test.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
865 uint obj2 = ctx_test.addTileObject(make_vec3(1, 0, 0), make_vec2(1, 1), nullrotation, make_int2(1, 1));
866
867 // Test primitive string value caching
868 ctx_test.setPrimitiveData(patch1, "test_string", std::string("apple"));
869 ctx_test.setPrimitiveData(patch2, "test_string", std::string("banana"));
870 ctx_test.setPrimitiveData(patch3, "test_string", std::string("apple"));
871
872 std::vector<std::string> string_values;
873 ctx_test.getUniquePrimitiveDataValues("test_string", string_values);
874 DOCTEST_CHECK(string_values.size() == 2);
875 std::sort(string_values.begin(), string_values.end());
876 DOCTEST_CHECK(string_values[0] == "apple");
877 DOCTEST_CHECK(string_values[1] == "banana");
878
879 // Test primitive int value caching
880 ctx_test.setPrimitiveData(patch1, "test_int", 10);
881 ctx_test.setPrimitiveData(patch2, "test_int", 20);
882 ctx_test.setPrimitiveData(patch3, "test_int", 10);
883
884 std::vector<int> int_values;
885 ctx_test.getUniquePrimitiveDataValues("test_int", int_values);
886 DOCTEST_CHECK(int_values.size() == 2);
887 std::sort(int_values.begin(), int_values.end());
888 DOCTEST_CHECK(int_values[0] == 10);
889 DOCTEST_CHECK(int_values[1] == 20);
890
891 // Test primitive uint value caching
892 ctx_test.setPrimitiveData(patch1, "test_uint", 100u);
893 ctx_test.setPrimitiveData(patch2, "test_uint", 200u);
894 ctx_test.setPrimitiveData(patch3, "test_uint", 100u);
895
896 std::vector<uint> uint_values;
897 ctx_test.getUniquePrimitiveDataValues("test_uint", uint_values);
898 DOCTEST_CHECK(uint_values.size() == 2);
899 std::sort(uint_values.begin(), uint_values.end());
900 DOCTEST_CHECK(uint_values[0] == 100u);
901 DOCTEST_CHECK(uint_values[1] == 200u);
902
903 // Test object string value caching
904 ctx_test.setObjectData(obj1, "obj_string", std::string("circle"));
905 ctx_test.setObjectData(obj2, "obj_string", std::string("square"));
906
907 std::vector<std::string> obj_string_values;
908 ctx_test.getUniqueObjectDataValues("obj_string", obj_string_values);
909 DOCTEST_CHECK(obj_string_values.size() == 2);
910 std::sort(obj_string_values.begin(), obj_string_values.end());
911 DOCTEST_CHECK(obj_string_values[0] == "circle");
912 DOCTEST_CHECK(obj_string_values[1] == "square");
913
914 // Test object int value caching
915 ctx_test.setObjectData(obj1, "obj_int", 5);
916 ctx_test.setObjectData(obj2, "obj_int", 15);
917
918 std::vector<int> obj_int_values;
919 ctx_test.getUniqueObjectDataValues("obj_int", obj_int_values);
920 DOCTEST_CHECK(obj_int_values.size() == 2);
921 std::sort(obj_int_values.begin(), obj_int_values.end());
922 DOCTEST_CHECK(obj_int_values[0] == 5);
923 DOCTEST_CHECK(obj_int_values[1] == 15);
924
925 // Test object uint value caching
926 ctx_test.setObjectData(obj1, "obj_uint", 50u);
927 ctx_test.setObjectData(obj2, "obj_uint", 150u);
928
929 std::vector<uint> obj_uint_values;
930 ctx_test.getUniqueObjectDataValues("obj_uint", obj_uint_values);
931 DOCTEST_CHECK(obj_uint_values.size() == 2);
932 std::sort(obj_uint_values.begin(), obj_uint_values.end());
933 DOCTEST_CHECK(obj_uint_values[0] == 50u);
934 DOCTEST_CHECK(obj_uint_values[1] == 150u);
935
936 // Test value update (cache maintenance)
937 ctx_test.setPrimitiveData(patch1, "test_string", std::string("cherry"));
938 ctx_test.getUniquePrimitiveDataValues("test_string", string_values);
939 DOCTEST_CHECK(string_values.size() == 3);
940 std::sort(string_values.begin(), string_values.end());
941 DOCTEST_CHECK(string_values[0] == "apple");
942 DOCTEST_CHECK(string_values[1] == "banana");
943 DOCTEST_CHECK(string_values[2] == "cherry");
944
945 // Test clearing data (cache maintenance)
946 ctx_test.clearPrimitiveData(patch2, "test_string");
947 ctx_test.getUniquePrimitiveDataValues("test_string", string_values);
948 DOCTEST_CHECK(string_values.size() == 2);
949 std::sort(string_values.begin(), string_values.end());
950 DOCTEST_CHECK(string_values[0] == "apple");
951 DOCTEST_CHECK(string_values[1] == "cherry");
952
953 ctx_test.clearObjectData(obj1, "obj_string");
954 ctx_test.getUniqueObjectDataValues("obj_string", obj_string_values);
955 DOCTEST_CHECK(obj_string_values.size() == 1);
956 DOCTEST_CHECK(obj_string_values[0] == "square");
957
958 // Test disabling cache - should throw error after disabling
959 ctx_test.disablePrimitiveDataValueCaching("test_string");
960 DOCTEST_CHECK(!ctx_test.isPrimitiveDataValueCachingEnabled("test_string"));
961
962 threw_error = false;
963 try {
964 ctx_test.getUniquePrimitiveDataValues("test_string", string_values);
965 } catch (const std::runtime_error &e) {
966 threw_error = true;
967 }
968 DOCTEST_CHECK(threw_error);
969
970 ctx_test.disableObjectDataValueCaching("obj_string");
971 DOCTEST_CHECK(!ctx_test.isObjectDataValueCachingEnabled("obj_string"));
972
973 threw_error = false;
974 try {
975 ctx_test.getUniqueObjectDataValues("obj_string", obj_string_values);
976 } catch (const std::runtime_error &e) {
977 threw_error = true;
978 }
979 DOCTEST_CHECK(threw_error);
980
981 // Test empty results when no data exists (but caching is enabled)
982 ctx_test.enablePrimitiveDataValueCaching("empty_label");
983 std::vector<std::string> empty_values;
984 ctx_test.getUniquePrimitiveDataValues("empty_label", empty_values);
985 DOCTEST_CHECK(empty_values.empty());
986 }
987}
988
989TEST_CASE("Vector setObjectData Value Caching") {
990 Context ctx_test;
991
992 // Enable caching for test labels
993 ctx_test.enableObjectDataValueCaching("vec_string");
994 ctx_test.enableObjectDataValueCaching("vec_int");
995 ctx_test.enableObjectDataValueCaching("vec_uint");
996
997 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("vec_string"));
998 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("vec_int"));
999 DOCTEST_CHECK(ctx_test.isObjectDataValueCachingEnabled("vec_uint"));
1000
1001 // Create test objects
1002 uint obj1 = ctx_test.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1003 uint obj2 = ctx_test.addTileObject(make_vec3(1, 0, 0), make_vec2(1, 1), nullrotation, make_int2(1, 1));
1004 uint obj3 = ctx_test.addSphereObject(1, make_vec3(2, 0, 0), 1.0f);
1005 uint obj4 = ctx_test.addConeObject(1, make_vec3(3, 0, 0), make_vec3(3, 0, 2), 1.0f, 2.0f);
1006
1007 SUBCASE("Vector<uint> string caching") {
1008 // Test vector version with string data
1009 std::vector<uint> obj_vec = {obj1, obj2};
1010 ctx_test.setObjectData(obj_vec, "vec_string", std::string("apple"));
1011
1012 // Single object gets different value
1013 ctx_test.setObjectData(obj3, "vec_string", std::string("banana"));
1014
1015 std::vector<std::string> string_values;
1016 ctx_test.getUniqueObjectDataValues("vec_string", string_values);
1017 DOCTEST_CHECK(string_values.size() == 2);
1018 std::sort(string_values.begin(), string_values.end());
1019 DOCTEST_CHECK(string_values[0] == "apple");
1020 DOCTEST_CHECK(string_values[1] == "banana");
1021 }
1022
1023 SUBCASE("Vector<uint> int caching") {
1024 // Test vector version with int data
1025 std::vector<uint> obj_vec = {obj1, obj2, obj3};
1026 ctx_test.setObjectData(obj_vec, "vec_int", 100);
1027
1028 // Single object gets different value
1029 ctx_test.setObjectData(obj4, "vec_int", 200);
1030
1031 std::vector<int> int_values;
1032 ctx_test.getUniqueObjectDataValues("vec_int", int_values);
1033 DOCTEST_CHECK(int_values.size() == 2);
1034 std::sort(int_values.begin(), int_values.end());
1035 DOCTEST_CHECK(int_values[0] == 100);
1036 DOCTEST_CHECK(int_values[1] == 200);
1037 }
1038
1039 SUBCASE("Vector<uint> uint caching") {
1040 // Test vector version with uint data
1041 std::vector<uint> obj_vec = {obj1, obj2};
1042 ctx_test.setObjectData(obj_vec, "vec_uint", 50u);
1043
1044 // Another vector with same value (should not increase unique count)
1045 std::vector<uint> obj_vec2 = {obj3};
1046 ctx_test.setObjectData(obj_vec2, "vec_uint", 50u);
1047
1048 // Single object gets different value
1049 ctx_test.setObjectData(obj4, "vec_uint", 150u);
1050
1051 std::vector<uint> uint_values;
1052 ctx_test.getUniqueObjectDataValues("vec_uint", uint_values);
1053 DOCTEST_CHECK(uint_values.size() == 2);
1054 std::sort(uint_values.begin(), uint_values.end());
1055 DOCTEST_CHECK(uint_values[0] == 50u);
1056 DOCTEST_CHECK(uint_values[1] == 150u);
1057 }
1058
1059 SUBCASE("Vector<vector<uint>> caching") {
1060 // Test nested vector version
1061 std::vector<std::vector<uint>> nested_vec = {{obj1, obj2}, {obj3}};
1062 ctx_test.setObjectData(nested_vec, "vec_string", std::string("cherry"));
1063
1064 // Single object gets different value
1065 ctx_test.setObjectData(obj4, "vec_string", std::string("date"));
1066
1067 std::vector<std::string> string_values;
1068 ctx_test.getUniqueObjectDataValues("vec_string", string_values);
1069 DOCTEST_CHECK(string_values.size() == 2);
1070 std::sort(string_values.begin(), string_values.end());
1071 DOCTEST_CHECK(string_values[0] == "cherry");
1072 DOCTEST_CHECK(string_values[1] == "date");
1073 }
1074
1075 SUBCASE("Vector<vector<vector<uint>>> caching") {
1076 // Test triple nested vector version
1077 std::vector<std::vector<std::vector<uint>>> triple_vec = {{{obj1}, {obj2}}, {{obj3}}};
1078 ctx_test.setObjectData(triple_vec, "vec_int", 300);
1079
1080 // Single object gets different value
1081 ctx_test.setObjectData(obj4, "vec_int", 400);
1082
1083 std::vector<int> int_values;
1084 ctx_test.getUniqueObjectDataValues("vec_int", int_values);
1085 DOCTEST_CHECK(int_values.size() == 2);
1086 std::sort(int_values.begin(), int_values.end());
1087 DOCTEST_CHECK(int_values[0] == 300);
1088 DOCTEST_CHECK(int_values[1] == 400);
1089 }
1090
1091 SUBCASE("Vector cache update behavior") {
1092 // Test that updating existing data via vector properly maintains cache
1093 std::vector<uint> obj_vec = {obj1, obj2};
1094
1095 // Set initial values
1096 ctx_test.setObjectData(obj_vec, "vec_string", std::string("old_value"));
1097 ctx_test.setObjectData(obj3, "vec_string", std::string("other_value"));
1098
1099 // Verify initial state
1100 std::vector<std::string> string_values;
1101 ctx_test.getUniqueObjectDataValues("vec_string", string_values);
1102 DOCTEST_CHECK(string_values.size() == 2);
1103
1104 // Update values via vector
1105 ctx_test.setObjectData(obj_vec, "vec_string", std::string("new_value"));
1106
1107 // Verify cache is properly updated
1108 ctx_test.getUniqueObjectDataValues("vec_string", string_values);
1109 DOCTEST_CHECK(string_values.size() == 2);
1110 std::sort(string_values.begin(), string_values.end());
1111 DOCTEST_CHECK(string_values[0] == "new_value");
1112 DOCTEST_CHECK(string_values[1] == "other_value");
1113 }
1114
1115 SUBCASE("Mixed single and vector operations") {
1116 // Test mixing single objID and vector objID operations
1117 ctx_test.setObjectData(obj1, "vec_int", 10);
1118
1119 std::vector<uint> obj_vec = {obj2, obj3};
1120 ctx_test.setObjectData(obj_vec, "vec_int", 10); // Same value
1121
1122 ctx_test.setObjectData(obj4, "vec_int", 20); // Different value
1123
1124 std::vector<int> int_values;
1125 ctx_test.getUniqueObjectDataValues("vec_int", int_values);
1126 DOCTEST_CHECK(int_values.size() == 2);
1127 std::sort(int_values.begin(), int_values.end());
1128 DOCTEST_CHECK(int_values[0] == 10);
1129 DOCTEST_CHECK(int_values[1] == 20);
1130 }
1131}
1132
1133TEST_CASE("Object Data Filtering") {
1134 Context ctx;
1135 uint o1 = ctx.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1136 uint o2 = ctx.addTileObject(make_vec3(1, 0, 0), make_vec2(2, 2), nullrotation, make_int2(1, 1));
1137 uint o3 = ctx.addConeObject(5, make_vec3(2, 0, 0), make_vec3(2, 0, 2), 1.0f, 2.0f);
1138
1139 SUBCASE("filterObjectsByData for all types") {
1140 // Set up test data
1141 ctx.setObjectData(o1, "float_val", 1.5f);
1142 ctx.setObjectData(o2, "float_val", 2.5f);
1143 ctx.setObjectData(o3, "float_val", 0.5f);
1144
1145 ctx.setObjectData(o1, "double_val", 1.5);
1146 ctx.setObjectData(o2, "double_val", 2.5);
1147 ctx.setObjectData(o3, "double_val", 0.5);
1148
1149 ctx.setObjectData(o1, "int_val", 1);
1150 ctx.setObjectData(o2, "int_val", 2);
1151 ctx.setObjectData(o3, "int_val", 0);
1152
1153 ctx.setObjectData(o1, "uint_val", (uint) 1);
1154 ctx.setObjectData(o2, "uint_val", (uint) 2);
1155 ctx.setObjectData(o3, "uint_val", (uint) 0);
1156
1157 ctx.setObjectData(o1, "string_val", std::string("apple"));
1158 ctx.setObjectData(o2, "string_val", std::string("banana"));
1159 ctx.setObjectData(o3, "string_val", std::string("cherry"));
1160
1161 std::vector<uint> all_objects = {o1, o2, o3};
1162
1163 // Test float filtering - use std::string for parameter names to avoid ambiguity
1164 auto filtered_f = ctx.filterObjectsByData(all_objects, std::string("float_val"), 1.0f, std::string(">"));
1165 DOCTEST_CHECK(filtered_f.size() == 2);
1166 DOCTEST_CHECK(std::find(filtered_f.begin(), filtered_f.end(), o1) != filtered_f.end());
1167 DOCTEST_CHECK(std::find(filtered_f.begin(), filtered_f.end(), o2) != filtered_f.end());
1168
1169 filtered_f = ctx.filterObjectsByData(all_objects, std::string("float_val"), 2.5f, std::string("=="));
1170 DOCTEST_CHECK(filtered_f.size() == 1);
1171 DOCTEST_CHECK(filtered_f[0] == o2);
1172
1173 // Test double filtering
1174 auto filtered_d = ctx.filterObjectsByData(all_objects, std::string("double_val"), 1.0, std::string(">="));
1175 DOCTEST_CHECK(filtered_d.size() == 2);
1176
1177 // Test int filtering
1178 auto filtered_i = ctx.filterObjectsByData(all_objects, std::string("int_val"), 1, std::string("<"));
1179 DOCTEST_CHECK(filtered_i.size() == 1);
1180 DOCTEST_CHECK(filtered_i[0] == o3);
1181
1182 // Test uint filtering
1183 auto filtered_u = ctx.filterObjectsByData(all_objects, std::string("uint_val"), (uint) 0, std::string(">"));
1184 DOCTEST_CHECK(filtered_u.size() == 2);
1185
1186 // Note: String filtering may not be supported by all filterObjectsByData overloads
1187 // Test with string data but focus on supported data types
1188 }
1189
1190 SUBCASE("filterObjectsByData error handling") {
1191 std::vector<uint> objects = {o1};
1192 capture_cerr cerr_buffer;
1193
1194 // Test invalid comparison operator
1195 std::vector<uint> filtered_f;
1196 DOCTEST_CHECK_THROWS_AS(filtered_f = ctx.filterObjectsByData(objects, std::string("non_existent"), 1.0f, std::string("!!")), std::runtime_error);
1197
1198 // Test non-existent data
1199 auto empty_result = ctx.filterObjectsByData(objects, std::string("non_existent"), 1.0f, std::string(">"));
1200 DOCTEST_CHECK(empty_result.empty());
1201 }
1202}
1203
1204TEST_CASE("Surface Area Calculations") {
1205 Context ctx;
1206
1207 SUBCASE("sumPrimitiveSurfaceArea") {
1208 uint p1 = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(2, 3)); // Area = 6
1209 uint p2 = ctx.addPatch(make_vec3(1, 0, 0), make_vec2(1, 4)); // Area = 4
1210 uint t1 = ctx.addTriangle(make_vec3(0, 0, 0), make_vec3(2, 0, 0), make_vec3(0, 2, 0)); // Area = 2
1211
1212 std::vector<uint> patches = {p1, p2};
1213 std::vector<uint> triangles = {t1};
1214 std::vector<uint> all_prims = {p1, p2, t1};
1215
1216 float patch_area = ctx.sumPrimitiveSurfaceArea(patches);
1217 DOCTEST_CHECK(patch_area == doctest::Approx(10.0f));
1218
1219 float triangle_area = ctx.sumPrimitiveSurfaceArea(triangles);
1220 DOCTEST_CHECK(triangle_area == doctest::Approx(2.0f));
1221
1222 float total_area = ctx.sumPrimitiveSurfaceArea(all_prims);
1223 DOCTEST_CHECK(total_area == doctest::Approx(12.0f));
1224
1225 // Test empty list
1226 std::vector<uint> empty_list;
1227 float zero_area = ctx.sumPrimitiveSurfaceArea(empty_list);
1228 DOCTEST_CHECK(zero_area == doctest::Approx(0.0f));
1229 }
1230}
1231
1232TEST_CASE("Advanced Area-Weighted Calculations") {
1233 Context ctx;
1234
1235 SUBCASE("calculatePrimitiveDataAreaWeightedMean comprehensive") {
1236 // Create primitives with different areas
1237 uint p1 = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1)); // Area = 1
1238 uint p2 = ctx.addPatch(make_vec3(1, 0, 0), make_vec2(2, 2)); // Area = 4
1239 uint p3 = ctx.addPatch(make_vec3(2, 0, 0), make_vec2(3, 1)); // Area = 3
1240
1241 // Set data for different types
1242 ctx.setPrimitiveData(p1, "double_val", 10.0);
1243 ctx.setPrimitiveData(p2, "double_val", 20.0);
1244 ctx.setPrimitiveData(p3, "double_val", 30.0);
1245
1246 ctx.setPrimitiveData(p1, "vec2_val", make_vec2(1, 2));
1247 ctx.setPrimitiveData(p2, "vec2_val", make_vec2(3, 4));
1248 ctx.setPrimitiveData(p3, "vec2_val", make_vec2(5, 6));
1249
1250 ctx.setPrimitiveData(p1, "vec3_val", make_vec3(1, 2, 3));
1251 ctx.setPrimitiveData(p2, "vec3_val", make_vec3(4, 5, 6));
1252 ctx.setPrimitiveData(p3, "vec3_val", make_vec3(7, 8, 9));
1253
1254 ctx.setPrimitiveData(p1, "vec4_val", make_vec4(1, 2, 3, 4));
1255 ctx.setPrimitiveData(p2, "vec4_val", make_vec4(5, 6, 7, 8));
1256 ctx.setPrimitiveData(p3, "vec4_val", make_vec4(9, 10, 11, 12));
1257
1258 std::vector<uint> uuids = {p1, p2, p3};
1259
1260 // Test double area-weighted mean
1261 // Expected: (10*1 + 20*4 + 30*3) / (1+4+3) = 180/8 = 22.5
1262 double d_mean;
1263 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "double_val", d_mean);
1264 DOCTEST_CHECK(d_mean == doctest::Approx(22.5));
1265
1266 // Test vec2 area-weighted mean
1267 // Expected x: (1*1 + 3*4 + 5*3) / 8 = 28/8 = 3.5
1268 // Expected y: (2*1 + 4*4 + 6*3) / 8 = 36/8 = 4.5
1269 vec2 v2_mean;
1270 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec2_val", v2_mean);
1271 DOCTEST_CHECK(v2_mean.x == doctest::Approx(3.5f));
1272 DOCTEST_CHECK(v2_mean.y == doctest::Approx(4.5f));
1273
1274 // Test vec3 area-weighted mean
1275 vec3 v3_mean;
1276 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec3_val", v3_mean);
1277 DOCTEST_CHECK(v3_mean.x == doctest::Approx(4.75f)); // (1*1 + 4*4 + 7*3) / 8 = 38/8 = 4.75
1278 DOCTEST_CHECK(v3_mean.y == doctest::Approx(5.75f)); // (2*1 + 5*4 + 8*3) / 8 = 46/8 = 5.75
1279 DOCTEST_CHECK(v3_mean.z == doctest::Approx(6.75f)); // (3*1 + 6*4 + 9*3) / 8 = 54/8 = 6.75
1280
1281 // Test vec4 area-weighted mean
1282 vec4 v4_mean;
1283 ctx.calculatePrimitiveDataAreaWeightedMean(uuids, "vec4_val", v4_mean);
1284 DOCTEST_CHECK(v4_mean.x == doctest::Approx(6.0f)); // (1*1 + 5*4 + 9*3) / 8 = 48/8 = 6.0
1285 DOCTEST_CHECK(v4_mean.y == doctest::Approx(7.0f)); // (2*1 + 6*4 + 10*3) / 8 = 56/8 = 7.0
1286 DOCTEST_CHECK(v4_mean.z == doctest::Approx(8.0f)); // (3*1 + 7*4 + 11*3) / 8 = 64/8 = 8.0
1287 DOCTEST_CHECK(v4_mean.w == doctest::Approx(9.0f)); // (4*1 + 8*4 + 12*3) / 8 = 72/8 = 9.0
1288 }
1289
1290 SUBCASE("calculatePrimitiveDataAreaWeightedSum comprehensive") {
1291 uint p1 = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(2, 1)); // Area = 2
1292 uint p2 = ctx.addPatch(make_vec3(1, 0, 0), make_vec2(1, 3)); // Area = 3
1293
1294 ctx.setPrimitiveData(p1, "double_val", 5.0);
1295 ctx.setPrimitiveData(p2, "double_val", 10.0);
1296
1297 ctx.setPrimitiveData(p1, "vec2_val", make_vec2(1, 2));
1298 ctx.setPrimitiveData(p2, "vec2_val", make_vec2(3, 4));
1299
1300 ctx.setPrimitiveData(p1, "vec3_val", make_vec3(1, 2, 3));
1301 ctx.setPrimitiveData(p2, "vec3_val", make_vec3(4, 5, 6));
1302
1303 ctx.setPrimitiveData(p1, "vec4_val", make_vec4(1, 2, 3, 4));
1304 ctx.setPrimitiveData(p2, "vec4_val", make_vec4(5, 6, 7, 8));
1305
1306 std::vector<uint> uuids = {p1, p2};
1307
1308 // Test double area-weighted sum: 5*2 + 10*3 = 40
1309 double d_sum;
1310 ctx.calculatePrimitiveDataAreaWeightedSum(uuids, "double_val", d_sum);
1311 DOCTEST_CHECK(d_sum == doctest::Approx(40.0));
1312
1313 // Test vec2 area-weighted sum
1314 vec2 v2_sum;
1315 ctx.calculatePrimitiveDataAreaWeightedSum(uuids, "vec2_val", v2_sum);
1316 DOCTEST_CHECK(v2_sum.x == doctest::Approx(11.0f)); // 1*2 + 3*3 = 11
1317 DOCTEST_CHECK(v2_sum.y == doctest::Approx(16.0f)); // 2*2 + 4*3 = 16
1318
1319 // Test vec3 area-weighted sum
1320 vec3 v3_sum;
1321 ctx.calculatePrimitiveDataAreaWeightedSum(uuids, "vec3_val", v3_sum);
1322 DOCTEST_CHECK(v3_sum.x == doctest::Approx(14.0f)); // 1*2 + 4*3 = 14
1323 DOCTEST_CHECK(v3_sum.y == doctest::Approx(19.0f)); // 2*2 + 5*3 = 19
1324 DOCTEST_CHECK(v3_sum.z == doctest::Approx(24.0f)); // 3*2 + 6*3 = 24
1325
1326 // Test vec4 area-weighted sum
1327 vec4 v4_sum;
1328 ctx.calculatePrimitiveDataAreaWeightedSum(uuids, "vec4_val", v4_sum);
1329 DOCTEST_CHECK(v4_sum.x == doctest::Approx(17.0f)); // 1*2 + 5*3 = 17
1330 DOCTEST_CHECK(v4_sum.y == doctest::Approx(22.0f)); // 2*2 + 6*3 = 22
1331 DOCTEST_CHECK(v4_sum.z == doctest::Approx(27.0f)); // 3*2 + 7*3 = 27
1332 DOCTEST_CHECK(v4_sum.w == doctest::Approx(32.0f)); // 4*2 + 8*3 = 32
1333 }
1334}
1335
1336TEST_CASE("Global Data Scaling") {
1337 Context ctx;
1338
1339 SUBCASE("scalePrimitiveData with label") {
1340 uint p1 = ctx.addPatch();
1341 uint p2 = ctx.addPatch();
1342 uint p3 = ctx.addPatch();
1343
1344 // Set data on primitives
1345 ctx.setPrimitiveData(p1, "scale_me", 10.0f);
1346 ctx.setPrimitiveData(p2, "scale_me", 20.0f);
1347 ctx.setPrimitiveData(p3, "other_data", 5.0f);
1348
1349 // Scale all primitives with "scale_me" label
1350 capture_cerr cerr_buffer;
1351 ctx.scalePrimitiveData("scale_me", 2.0f);
1352
1353 // Check scaled values
1354 float val1, val2, val3;
1355 ctx.getPrimitiveData(p1, "scale_me", val1);
1356 ctx.getPrimitiveData(p2, "scale_me", val2);
1357 ctx.getPrimitiveData(p3, "other_data", val3);
1358
1359 DOCTEST_CHECK(val1 == doctest::Approx(20.0f));
1360 DOCTEST_CHECK(val2 == doctest::Approx(40.0f));
1361 DOCTEST_CHECK(val3 == doctest::Approx(5.0f)); // Should be unchanged
1362
1363 // Test with non-existent label
1364 ctx.scalePrimitiveData("non_existent", 3.0f);
1365 DOCTEST_CHECK(cerr_buffer.has_output());
1366 }
1367}
1368
1369TEST_CASE("Aggregate Function Edge Cases") {
1370 Context ctx;
1371
1372 SUBCASE("aggregatePrimitiveDataProduct edge cases") {
1373 uint p1 = ctx.addPatch();
1374 uint p2 = ctx.addPatch();
1375 uint p3 = ctx.addPatch();
1376
1377 // Test with zeros
1378 ctx.setPrimitiveData(p1, "zero_test", 0.0f);
1379 ctx.setPrimitiveData(p1, "normal", 5.0f);
1380 ctx.setPrimitiveData(p2, "zero_test", 10.0f);
1381 ctx.setPrimitiveData(p2, "normal", 2.0f);
1382
1383 std::vector<uint> uuids = {p1, p2};
1384 std::vector<std::string> labels = {"zero_test", "normal"};
1385
1386 ctx.aggregatePrimitiveDataProduct(uuids, labels, "product_result");
1387
1388 float result;
1389 ctx.getPrimitiveData(p1, "product_result", result);
1390 DOCTEST_CHECK(result == doctest::Approx(0.0f)); // 0 * 5 = 0
1391
1392 ctx.getPrimitiveData(p2, "product_result", result);
1393 DOCTEST_CHECK(result == doctest::Approx(20.0f)); // 10 * 2 = 20
1394
1395 // Test with negative values
1396 ctx.setPrimitiveData(p1, "neg1", -2.0f);
1397 ctx.setPrimitiveData(p1, "neg2", -3.0f);
1398 ctx.setPrimitiveData(p2, "neg1", 4.0f);
1399 ctx.setPrimitiveData(p2, "neg2", -1.0f);
1400
1401 std::vector<std::string> neg_labels = {"neg1", "neg2"};
1402 ctx.aggregatePrimitiveDataProduct(uuids, neg_labels, "neg_product");
1403
1404 ctx.getPrimitiveData(p1, "neg_product", result);
1405 DOCTEST_CHECK(result == doctest::Approx(6.0f)); // (-2) * (-3) = 6
1406
1407 ctx.getPrimitiveData(p2, "neg_product", result);
1408 DOCTEST_CHECK(result == doctest::Approx(-4.0f)); // 4 * (-1) = -4
1409 }
1410}
1411
1412TEST_CASE("Data Type Coverage Extensions") {
1413 Context ctx;
1414
1415 SUBCASE("Primitive data with int2, int3, int4 vectors") {
1416 uint p = ctx.addPatch();
1417
1418 // Test vector of int2, int3, int4
1419 std::vector<int2> v_i2 = {make_int2(1, 2), make_int2(3, 4)};
1420 std::vector<int3> v_i3 = {make_int3(1, 2, 3), make_int3(4, 5, 6)};
1421 std::vector<int4> v_i4 = {make_int4(1, 2, 3, 4), make_int4(5, 6, 7, 8)};
1422
1423 ctx.setPrimitiveData(p, "vec_int2", v_i2);
1424 ctx.setPrimitiveData(p, "vec_int3", v_i3);
1425 ctx.setPrimitiveData(p, "vec_int4", v_i4);
1426
1427 DOCTEST_CHECK(ctx.getPrimitiveDataType("vec_int2") == HELIOS_TYPE_INT2);
1428 DOCTEST_CHECK(ctx.getPrimitiveDataType("vec_int3") == HELIOS_TYPE_INT3);
1429 DOCTEST_CHECK(ctx.getPrimitiveDataType("vec_int4") == HELIOS_TYPE_INT4);
1430
1431 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec_int2") == 2);
1432 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec_int3") == 2);
1433 DOCTEST_CHECK(ctx.getPrimitiveDataSize(p, "vec_int4") == 2);
1434
1435 // Test retrieval
1436 std::vector<int2> r_i2;
1437 std::vector<int3> r_i3;
1438 std::vector<int4> r_i4;
1439
1440 ctx.getPrimitiveData(p, "vec_int2", r_i2);
1441 ctx.getPrimitiveData(p, "vec_int3", r_i3);
1442 ctx.getPrimitiveData(p, "vec_int4", r_i4);
1443
1444 DOCTEST_CHECK(r_i2 == v_i2);
1445 DOCTEST_CHECK(r_i3 == v_i3);
1446 DOCTEST_CHECK(r_i4 == v_i4);
1447 }
1448}
1449
1450TEST_CASE("Error Handling Edge Cases") {
1451 Context ctx;
1452
1453 SUBCASE("Data operations with mixed primitive types") {
1454 uint patch = ctx.addPatch();
1455 uint triangle = ctx.addTriangle(make_vec3(0, 0, 0), make_vec3(1, 0, 0), make_vec3(0, 1, 0));
1456 uint voxel = ctx.addVoxel(make_vec3(0, 0, 0), make_vec3(1, 1, 1));
1457
1458 // Set data on different primitive types
1459 ctx.setPrimitiveData(patch, "mixed_data", 1.0f);
1460 ctx.setPrimitiveData(triangle, "mixed_data", 2.0f);
1461 ctx.setPrimitiveData(voxel, "mixed_data", 3.0f);
1462
1463 std::vector<uint> mixed_prims = {patch, triangle, voxel};
1464
1465 // Test calculations work with mixed primitive types
1466 float mean_val;
1467 ctx.calculatePrimitiveDataMean(mixed_prims, "mixed_data", mean_val);
1468 DOCTEST_CHECK(mean_val == doctest::Approx(2.0f));
1469
1470 float sum_val;
1471 ctx.calculatePrimitiveDataSum(mixed_prims, "mixed_data", sum_val);
1472 DOCTEST_CHECK(sum_val == doctest::Approx(6.0f));
1473 }
1474
1475 SUBCASE("Large dataset stress test") {
1476 std::vector<uint> large_dataset;
1477
1478 // Create many primitives with data
1479 for (int i = 0; i < 1000; ++i) {
1480 uint p = ctx.addPatch();
1481 ctx.setPrimitiveData(p, "stress_data", (float) i);
1482 large_dataset.push_back(p);
1483 }
1484
1485 // Test operations on large dataset
1486 float mean_val, sum_val;
1487 ctx.calculatePrimitiveDataMean(large_dataset, "stress_data", mean_val);
1488 ctx.calculatePrimitiveDataSum(large_dataset, "stress_data", sum_val);
1489
1490 DOCTEST_CHECK(mean_val == doctest::Approx(499.5f)); // Mean of 0-999
1491 DOCTEST_CHECK(sum_val == doctest::Approx(499500.0f)); // Sum of 0-999
1492
1493 // Test filtering on large dataset
1494 auto filtered = ctx.filterPrimitivesByData(large_dataset, "stress_data", 500.0f, ">");
1495 DOCTEST_CHECK(filtered.size() == 499); // Values 501-999
1496 }
1497}
1498
1499TEST_CASE("General Error Handling") {
1500 Context ctx;
1501 uint tri = ctx.addTriangle(make_vec3(0, 0, 0), make_vec3(1, 0, 0), make_vec3(0, 1, 0), RGB::green);
1502 capture_cerr cerr_buffer;
1503 vec3 center;
1504#ifdef HELIOS_DEBUG
1505 DOCTEST_CHECK_THROWS_AS(center = ctx.getPatchCenter(tri), std::runtime_error);
1506#endif
1507
1508 uint vox = ctx.addVoxel(make_vec3(0, 0, 0), make_vec3(1, 1, 1));
1509 std::vector<uint> vlist{vox};
1510 DOCTEST_CHECK_THROWS_AS(ctx.rotatePrimitive(vlist, PI_F / 4.f, "a"), std::runtime_error);
1511}
1512
1513TEST_CASE("Data Type Consistency and Caching") {
1514 Context ctx;
1515
1516 SUBCASE("Primitive data type caching") {
1517 // Initially, registry should be empty
1518 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataType("test_label"));
1519
1520 // Create some primitives and set data
1521 uint patch1 = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1));
1522 uint patch2 = ctx.addPatch(make_vec3(1, 0, 0), make_vec2(1, 1));
1523
1524 // Set data with consistent type
1525 ctx.setPrimitiveData(patch1, "temperature", 25.5f);
1526 ctx.setPrimitiveData(patch2, "temperature", 30.2f);
1527
1528 // Check cached type lookup
1529 DOCTEST_CHECK(ctx.getPrimitiveDataType("temperature") == HELIOS_TYPE_FLOAT);
1530
1531 // Set data with different label
1532 ctx.setPrimitiveData(patch1, "leaf_id", 123);
1533 DOCTEST_CHECK(ctx.getPrimitiveDataType("leaf_id") == HELIOS_TYPE_INT);
1534
1535 // Non-existent label should throw an exception
1536 DOCTEST_CHECK_THROWS(ctx.getPrimitiveDataType("nonexistent"));
1537 }
1538
1539 SUBCASE("Object data type caching") {
1540 // Initially, registry should be empty
1541 DOCTEST_CHECK_THROWS(ctx.getObjectDataType("test_obj_label"));
1542
1543 // Create objects and set data
1544 uint box1 = ctx.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1545 uint box2 = ctx.addBoxObject(make_vec3(2, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1546
1547 // Set data with consistent type
1548 ctx.setObjectData(box1, "volume", 1.0);
1549 ctx.setObjectData(box2, "volume", 2.5);
1550
1551 // Check cached type lookup
1552 DOCTEST_CHECK(ctx.getObjectDataType("volume") == HELIOS_TYPE_DOUBLE);
1553
1554 // Set data with different label
1555 ctx.setObjectData(box1, "material_id", static_cast<uint>(42));
1556 DOCTEST_CHECK(ctx.getObjectDataType("material_id") == HELIOS_TYPE_UINT);
1557
1558 // Non-existent label should throw an exception
1559 DOCTEST_CHECK_THROWS(ctx.getObjectDataType("nonexistent"));
1560 }
1561
1562 SUBCASE("Type consistency enforcement - compatible numeric casting") {
1563 uint patch = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1));
1564
1565 // Set initial data as float
1566 ctx.setPrimitiveData(patch, "value", 10.5f);
1567 DOCTEST_CHECK(ctx.getPrimitiveDataType("value") == HELIOS_TYPE_FLOAT);
1568
1569 // Create another primitive and try to set compatible numeric type
1570 uint patch2 = ctx.addPatch(make_vec3(1, 0, 0), make_vec2(1, 1));
1571
1572 // This should generate a warning but work for compatible numeric types
1573 capture_cerr cerr_buffer;
1574 ctx.setPrimitiveData(patch2, "value", 20); // int to float should work with warning
1575
1576 // Check that both primitives have the data and it's stored correctly
1577 float val1, val2;
1578 ctx.getPrimitiveData(patch, "value", val1);
1579 ctx.getPrimitiveData(patch2, "value", val2);
1580 DOCTEST_CHECK(val1 == doctest::Approx(10.5f));
1581 DOCTEST_CHECK(val2 == doctest::Approx(20.0f));
1582
1583 // Should have generated a warning about type casting (captured by cerr_buffer)
1584 DOCTEST_CHECK(cerr_buffer.get_captured_output().find("WARNING") != std::string::npos);
1585 DOCTEST_CHECK(cerr_buffer.get_captured_output().find("Type casting") != std::string::npos);
1586 }
1587
1588 SUBCASE("Type consistency enforcement - incompatible types") {
1589 uint box = ctx.addBoxObject(make_vec3(0, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1590
1591 // Set initial data as string
1592 ctx.setObjectData(box, "material", std::string("wood"));
1593 DOCTEST_CHECK(ctx.getObjectDataType("material") == HELIOS_TYPE_STRING);
1594
1595 // Create another object and try to set incompatible type
1596 uint box2 = ctx.addBoxObject(make_vec3(2, 0, 0), make_vec3(1, 1, 1), make_int3(1, 1, 1));
1597
1598 // This should throw an error because string and float are incompatible
1599 DOCTEST_CHECK_THROWS_AS(ctx.setObjectData(box2, "material", 3.14f), std::runtime_error);
1600 }
1601
1602 SUBCASE("Multiple data labels per primitive/object") {
1603 uint tri = ctx.addTriangle(make_vec3(0, 0, 0), make_vec3(1, 0, 0), make_vec3(0, 1, 0));
1604
1605 // Set multiple different data types
1606 ctx.setPrimitiveData(tri, "temperature", 25.0f);
1607 ctx.setPrimitiveData(tri, "id", 100);
1608 ctx.setPrimitiveData(tri, "name", std::string("leaf1"));
1609 ctx.setPrimitiveData(tri, "position", make_vec3(0.5f, 0.5f, 0.0f));
1610
1611 // Check all types are correctly cached
1612 DOCTEST_CHECK(ctx.getPrimitiveDataType("temperature") == HELIOS_TYPE_FLOAT);
1613 DOCTEST_CHECK(ctx.getPrimitiveDataType("id") == HELIOS_TYPE_INT);
1614 DOCTEST_CHECK(ctx.getPrimitiveDataType("name") == HELIOS_TYPE_STRING);
1615 DOCTEST_CHECK(ctx.getPrimitiveDataType("position") == HELIOS_TYPE_VEC3);
1616
1617 // Add another primitive with the same labels - should maintain consistency
1618 uint tri2 = ctx.addTriangle(make_vec3(1, 0, 0), make_vec3(2, 0, 0), make_vec3(1, 1, 0));
1619 ctx.setPrimitiveData(tri2, "temperature", 30.0f);
1620 ctx.setPrimitiveData(tri2, "id", 101);
1621 ctx.setPrimitiveData(tri2, "name", std::string("leaf2"));
1622 ctx.setPrimitiveData(tri2, "position", make_vec3(1.5f, 0.5f, 0.0f));
1623
1624 // Verify data can be retrieved correctly
1625 float temp;
1626 int id;
1627 std::string name;
1628 vec3 pos;
1629
1630 ctx.getPrimitiveData(tri2, "temperature", temp);
1631 ctx.getPrimitiveData(tri2, "id", id);
1632 ctx.getPrimitiveData(tri2, "name", name);
1633 ctx.getPrimitiveData(tri2, "position", pos);
1634
1635 DOCTEST_CHECK(temp == doctest::Approx(30.0f));
1636 DOCTEST_CHECK(id == 101);
1637 DOCTEST_CHECK(name == "leaf2");
1638 DOCTEST_CHECK(pos.x == doctest::Approx(1.5f));
1639 }
1640
1641 SUBCASE("Cross-contamination prevention") {
1642 // Primitive and object data registries should be separate
1643 uint patch = ctx.addPatch(make_vec3(0, 0, 0), make_vec2(1, 1));
1644 uint sphere = ctx.addSphereObject(10, make_vec3(0, 0, 0), 1.0f);
1645
1646 // Set same label with different types on primitive vs object
1647 ctx.setPrimitiveData(patch, "value", 42); // int
1648 ctx.setObjectData(sphere, "value", 3.14f); // float
1649
1650 // Registries should track them separately
1651 DOCTEST_CHECK(ctx.getPrimitiveDataType("value") == HELIOS_TYPE_INT);
1652 DOCTEST_CHECK(ctx.getObjectDataType("value") == HELIOS_TYPE_FLOAT);
1653 }
1654
1655 SUBCASE("Vector data type consistency") {
1656 uint vox1 = ctx.addVoxel(make_vec3(0, 0, 0), make_vec3(1, 1, 1));
1657 uint vox2 = ctx.addVoxel(make_vec3(1, 0, 0), make_vec3(1, 1, 1));
1658
1659 // Set vector data
1660 std::vector<float> temps1 = {20.0f, 25.0f, 30.0f};
1661 std::vector<float> temps2 = {22.0f, 27.0f, 32.0f};
1662
1663 ctx.setPrimitiveData(vox1, "temperature_profile", temps1);
1664 ctx.setPrimitiveData(vox2, "temperature_profile", temps2);
1665
1666 // Should register as HELIOS_TYPE_FLOAT (vector types use base type)
1667 DOCTEST_CHECK(ctx.getPrimitiveDataType("temperature_profile") == HELIOS_TYPE_FLOAT);
1668 }
1669}