|
9 | 9 |
|
10 | 10 | #include <gtest/gtest.h> |
11 | 11 | #include <mrpt/maps/CColouredPointsMap.h> |
| 12 | +#include <mrpt/maps/CGenericPointsMap.h> |
12 | 13 | #include <mrpt/maps/CPointsMapXYZI.h> |
13 | 14 | #include <mrpt/maps/CSimplePointsMap.h> |
14 | 15 | #include <mrpt/maps/CWeightedPointsMap.h> |
15 | 16 | #include <mrpt/poses/CPoint2D.h> |
16 | 17 |
|
17 | 18 | #include <array> |
18 | 19 | #include <sstream> |
19 | | -#include <utility> |
20 | 20 |
|
21 | 21 | using namespace mrpt; |
22 | 22 | using namespace mrpt::maps; |
@@ -280,3 +280,146 @@ TEST(CSimplePointsMapTests, loadSaveStreams) { do_tests_loadSaveStreams<CSimpleP |
280 | 280 | TEST(CWeightedPointsMapTests, loadSaveStreams) { do_tests_loadSaveStreams<CWeightedPointsMap>(); } |
281 | 281 |
|
282 | 282 | TEST(CColouredPointsMapTests, loadSaveStreams) { do_tests_loadSaveStreams<CColouredPointsMap>(); } |
| 283 | + |
| 284 | +// ---------------------------------------------------------------------- |
| 285 | +// Tests for CGenericPointsMap custom field capabilities |
| 286 | +// ---------------------------------------------------------------------- |
| 287 | + |
| 288 | +TEST(CGenericPointsMap, CustomFieldsLifecycle) |
| 289 | +{ |
| 290 | + CGenericPointsMap pts; |
| 291 | + |
| 292 | + // 1. Registration |
| 293 | + EXPECT_TRUE(pts.registerField_float("pressure")); |
| 294 | + EXPECT_TRUE(pts.registerField_double("timestamp")); |
| 295 | + EXPECT_TRUE(pts.registerField_uint16("class_id")); |
| 296 | + |
| 297 | + // Ensure duplicate registration returns true (idempotent) or handles gracefully, |
| 298 | + // but strict check: hasPointField should be true. |
| 299 | + EXPECT_TRUE(pts.hasPointField("pressure")); |
| 300 | + EXPECT_TRUE(pts.hasPointField("timestamp")); |
| 301 | + EXPECT_TRUE(pts.hasPointField("class_id")); |
| 302 | + EXPECT_FALSE(pts.hasPointField("non_existent")); |
| 303 | + |
| 304 | + // Check field name listings |
| 305 | + { |
| 306 | + const auto f_names = pts.getPointFieldNames_float(); |
| 307 | + const auto d_names = pts.getPointFieldNames_double(); |
| 308 | + const auto u_names = pts.getPointFieldNames_uint16(); |
| 309 | + |
| 310 | + // Note: "x", "y", "z" are always in float names |
| 311 | + EXPECT_NE(std::find(f_names.begin(), f_names.end(), "pressure"), f_names.end()); |
| 312 | + EXPECT_NE(std::find(d_names.begin(), d_names.end(), "timestamp"), d_names.end()); |
| 313 | + EXPECT_NE(std::find(u_names.begin(), u_names.end(), "class_id"), u_names.end()); |
| 314 | + } |
| 315 | + |
| 316 | + // 2. Insertion (Synchronized) |
| 317 | + const size_t N = 5; |
| 318 | + for (size_t i = 0; i < N; i++) |
| 319 | + { |
| 320 | + // Standard XYZ insert |
| 321 | + pts.insertPointFast(static_cast<float>(i), 0.0f, 0.0f); |
| 322 | + |
| 323 | + // Custom fields insert (Must match size of XYZ) |
| 324 | + pts.insertPointField_float("pressure", static_cast<float>(i) * 10); |
| 325 | + pts.insertPointField_double("timestamp", static_cast<double>(i) * 1000.0); |
| 326 | + pts.insertPointField_uint16("class_id", static_cast<uint16_t>(i + 1)); |
| 327 | + } |
| 328 | + |
| 329 | + EXPECT_EQ(pts.size(), N); |
| 330 | + |
| 331 | + // 3. Random Access (Getters) |
| 332 | + for (size_t i = 0; i < N; i++) |
| 333 | + { |
| 334 | + EXPECT_FLOAT_EQ(pts.getPointField_float(i, "pressure"), static_cast<float>(i * 10)); |
| 335 | + EXPECT_DOUBLE_EQ(pts.getPointField_double(i, "timestamp"), static_cast<double>(i * 1000.0)); |
| 336 | + EXPECT_EQ(pts.getPointField_uint16(i, "class_id"), static_cast<uint16_t>(i + 1)); |
| 337 | + } |
| 338 | + |
| 339 | + // 4. Random Access (Setters) |
| 340 | + pts.setPointField_float(0, "pressure", 99.9f); |
| 341 | + pts.setPointField_double(0, "timestamp", 888.88); |
| 342 | + pts.setPointField_uint16(0, "class_id", 55); |
| 343 | + |
| 344 | + EXPECT_FLOAT_EQ(pts.getPointField_float(0, "pressure"), 99.9f); |
| 345 | + EXPECT_DOUBLE_EQ(pts.getPointField_double(0, "timestamp"), 888.88); |
| 346 | + EXPECT_EQ(pts.getPointField_uint16(0, "class_id"), 55); |
| 347 | + |
| 348 | + // 5. Resize behavior |
| 349 | + // Resizing should resize auxiliary fields and initialize new elements to 0 |
| 350 | + pts.resize(N + 2); |
| 351 | + EXPECT_EQ(pts.size(), N + 2); |
| 352 | + // Check old value preserved |
| 353 | + EXPECT_EQ(pts.getPointField_uint16(0, "class_id"), 55); |
| 354 | + // Check new value is zero-initialized |
| 355 | + EXPECT_EQ(pts.getPointField_float(N, "pressure"), 0.0f); |
| 356 | + EXPECT_EQ(pts.getPointField_uint16(N + 1, "class_id"), 0); |
| 357 | + |
| 358 | + // 6. Unregistration |
| 359 | + EXPECT_TRUE(pts.unregisterField("pressure")); |
| 360 | + EXPECT_FALSE(pts.hasPointField("pressure")); |
| 361 | + // Trying to access a removed field: Returns 0 if field does not exist |
| 362 | + EXPECT_EQ(pts.getPointField_float(0, "pressure"), 0.0f); |
| 363 | +} |
| 364 | + |
| 365 | +TEST(CGenericPointsMap, DeepCopy) |
| 366 | +{ |
| 367 | + CGenericPointsMap pts1; |
| 368 | + pts1.registerField_float("intensity"); |
| 369 | + |
| 370 | + pts1.insertPointFast(1, 1, 1); |
| 371 | + pts1.insertPointField_float("intensity", 0.5f); |
| 372 | + |
| 373 | + pts1.insertPointFast(2, 2, 2); |
| 374 | + pts1.insertPointField_float("intensity", 1.0f); |
| 375 | + |
| 376 | + // Copy Constructor |
| 377 | + const CGenericPointsMap pts2 = pts1; |
| 378 | + |
| 379 | + EXPECT_EQ(pts1.size(), pts2.size()); |
| 380 | + EXPECT_TRUE(pts2.hasPointField("intensity")); |
| 381 | + EXPECT_FLOAT_EQ(pts2.getPointField_float(1, "intensity"), 1.0f); |
| 382 | + |
| 383 | + // Modify original, ensure copy is independent |
| 384 | + pts1.setPointField_float(1, "intensity", 0.0f); |
| 385 | + EXPECT_FLOAT_EQ(pts1.getPointField_float(1, "intensity"), 0.0f); |
| 386 | + EXPECT_FLOAT_EQ(pts2.getPointField_float(1, "intensity"), 1.0f); |
| 387 | +} |
| 388 | + |
| 389 | +TEST(CGenericPointsMap, DirectVectorAccess) |
| 390 | +{ |
| 391 | + CGenericPointsMap pts; |
| 392 | + pts.registerField_float("temperature"); |
| 393 | + |
| 394 | + pts.resize(10); |
| 395 | + |
| 396 | + // Access via reference |
| 397 | + auto* vec = pts.getPointsBufferRef_float_field("temperature"); |
| 398 | + ASSERT_TRUE(vec != nullptr); |
| 399 | + ASSERT_EQ(vec->size(), 10u); |
| 400 | + |
| 401 | + // Modify vector directly |
| 402 | + (*vec)[5] = 37.5f; |
| 403 | + |
| 404 | + // Read back via API |
| 405 | + EXPECT_FLOAT_EQ(pts.getPointField_float(5, "temperature"), 37.5f); |
| 406 | +} |
| 407 | + |
| 408 | +// Verify that standard maps (CSimplePointsMap) behave correctly (reject custom fields) |
| 409 | +TEST(CSimplePointsMapTests, RejectCustomFields) |
| 410 | +{ |
| 411 | + CSimplePointsMap pts; |
| 412 | + |
| 413 | + // Should return false as CSimplePointsMap doesn't support dynamic fields |
| 414 | + EXPECT_FALSE(pts.registerField_float("my_custom_float")); |
| 415 | + EXPECT_FALSE(pts.registerField_double("my_custom_double")); |
| 416 | + EXPECT_FALSE(pts.registerField_uint16("my_custom_uint")); |
| 417 | + |
| 418 | + // Should return false/empty |
| 419 | + EXPECT_FALSE(pts.hasPointField("my_custom_float")); |
| 420 | + |
| 421 | + // Standard fields should still work |
| 422 | + EXPECT_TRUE(pts.hasPointField("x")); |
| 423 | + EXPECT_TRUE(pts.hasPointField("y")); |
| 424 | + EXPECT_TRUE(pts.hasPointField("z")); |
| 425 | +} |
0 commit comments