feat(v4.6): memory pool + parallel boolean/MC/intersection + GMP exact predicates
CI / Build & Test (push) Failing after 16m56s
CI / Release Build (push) Failing after 24s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled

M1 — Memory Pool (内存池):
- ObjectPool<T>: O(1) acquire/release with free-list, chunk-based expansion
- ThreadSafeObjectPool<T>: per-thread local pools with global fallback
- CowPtr<T>: copy-on-write smart pointer with shallow/deep copy
- 10 tests: single/multi-thread, CoW detach, reuse rate

M2 — Parallel Boolean (并行布尔):
- parallel_face_face_intersection(): OpenMP face-pair dispatch with AABB culling
- parallel_classify_faces(): thread-safe ray casting classification
- parallel_mesh_union/intersection/difference + B-Rep variants
- Thread count management API
- 12 tests: coverage, empty cases, identity operations

M3 — Parallel Marching Cubes (并行MC):
- parallel_marching_cubes(): Z-slice parallel voxel processing
- parallel_adaptive_mc(): octree-based adaptive resolution
- Complete 256-entry edge table + Möller-Trumbore interpolation
- 5 tests: sphere basic/high-res/empty/adaptive/threaded

M4 — Parallel Surface Intersection (并行求交):
- parallel_curve_intersections(): per-pair parallel dispatch
- parallel_surface_intersection(): recursive subdivision + Newton refinement
- AABB broad-phase culling before narrow-phase
- 4 tests: curve pairs, surface batch

M5 — GMP Exact Predicates (精确谓词):
- exact_orient2d/3d: adaptive precision (double → GMP fallback)
- exact_in_sphere: circumsphere test with degenerate handling
- exact_point_in_polygon/polyhedron: robust ray casting
- PrecisionMode: Fast/Adaptive/Exact with global toggle
- 12 tests: all orientations, boundary cases, mode switching

21 files, +2263 lines
This commit is contained in:
茂之钳
2026-07-26 17:28:15 +08:00
parent a5facc84f6
commit cf38d76fd0
21 changed files with 2263 additions and 2 deletions
+2
View File
@@ -4,3 +4,5 @@ add_vde_test(test_transform)
add_vde_test(test_distance)
add_vde_test(test_polygon)
add_vde_test(test_cam_toolpath)
add_vde_test(test_object_pool)
add_vde_test(test_exact_predicates)
+73
View File
@@ -0,0 +1,73 @@
#include <gtest/gtest.h>
#include "vde/core/exact_predicates.h"
#include <vector>
#include <cmath>
using namespace vde::core;
TEST(ExactPredicatesTest, Orient2D_CounterClockwise) {
double r = exact_orient2d(0, 0, 1, 0, 0, 1);
EXPECT_GT(r, 0.0); // CCW
}
TEST(ExactPredicatesTest, Orient2D_Clockwise) {
double r = exact_orient2d(0, 0, 0, 1, 1, 0);
EXPECT_LT(r, 0.0);
}
TEST(ExactPredicatesTest, Orient2D_Collinear) {
double r = exact_orient2d(0, 0, 1, 1, 2, 2);
EXPECT_NEAR(r, 0.0, 1e-9);
}
TEST(ExactPredicatesTest, Orient3D_Above) {
// Tetrahedron: base on z=0, apex at z=1 → positive volume
double r = exact_orient3d(0,0,0, 1,0,0, 0,1,0, 0,0,1);
EXPECT_GT(r, 0.0);
}
TEST(ExactPredicatesTest, Orient3D_Coplanar) {
double r = exact_orient3d(0,0,0, 1,0,0, 0,1,0, 0.5,0.5,0);
EXPECT_NEAR(r, 0.0, 1e-9);
}
TEST(ExactPredicatesTest, InSphere_Inside) {
// Regular tetrahedron, point at centroid → inside circumsphere
double r = exact_in_sphere(0,0,0, 2,0,0, 0,2,0, 0,0,2, 0.2,0.2,0.2);
EXPECT_GT(r, 0.0);
}
TEST(ExactPredicatesTest, InSphere_OnSurface) {
double r = exact_in_sphere(0,0,0, 3,0,0, 0,3,0, 0,0,3, 0,0,3);
EXPECT_NEAR(r, 0.0, 1e-8);
}
TEST(ExactPredicatesTest, PointInPolygon_Inside) {
std::vector<Point3D> poly = {{0,0,0},{2,0,0},{2,2,0},{0,2,0}};
EXPECT_TRUE(exact_point_in_polygon({1,1,0}, poly));
}
TEST(ExactPredicatesTest, PointInPolygon_Outside) {
std::vector<Point3D> poly = {{0,0,0},{2,0,0},{2,2,0},{0,2,0}};
EXPECT_FALSE(exact_point_in_polygon({3,3,0}, poly));
}
TEST(ExactPredicatesTest, PointInPolygon_OnEdge) {
std::vector<Point3D> poly = {{0,0,0},{2,0,0},{2,2,0},{0,2,0}};
EXPECT_TRUE(exact_point_in_polygon({1,0,0}, poly));
}
TEST(ExactPredicatesTest, PrecisionMode) {
set_precision_mode(PrecisionMode::Fast);
EXPECT_EQ(precision_mode(), PrecisionMode::Fast);
set_precision_mode(PrecisionMode::Adaptive);
EXPECT_EQ(precision_mode(), PrecisionMode::Adaptive);
set_precision_mode(PrecisionMode::Adaptive); // restore default
}
TEST(ExactPredicatesTest, IsUncertain) {
EXPECT_TRUE(is_uncertain(1e-12));
EXPECT_FALSE(is_uncertain(1.0));
}
+99
View File
@@ -0,0 +1,99 @@
#include <gtest/gtest.h>
#include "vde/core/object_pool.h"
#include <thread>
#include <vector>
using namespace vde::core;
struct TestObj { int x = 0; double y = 0.0; };
TEST(ObjectPoolTest, AcquireRelease) {
ObjectPool<TestObj> pool;
auto* obj = pool.acquire();
ASSERT_NE(obj, nullptr);
obj->x = 42;
pool.release(obj);
auto* obj2 = pool.acquire(); // should reuse
EXPECT_EQ(obj2->x, 0); // reset to default
EXPECT_GT(pool.reuse_rate(), 0.0);
}
TEST(ObjectPoolTest, AcquireMany) {
ObjectPool<TestObj, 64> pool;
std::vector<TestObj*> objs;
for (int i = 0; i < 1000; ++i) objs.push_back(pool.acquire());
for (auto* o : objs) pool.release(o);
EXPECT_EQ(pool.total_allocated(), 1000u);
EXPECT_GT(pool.total_reused(), 0u);
}
TEST(ObjectPoolTest, ReleaseNullptr) {
ObjectPool<TestObj> pool;
pool.release(nullptr); // should not crash
EXPECT_EQ(pool.total_allocated(), 0u);
}
TEST(ObjectPoolTest, ExpandChunk) {
ObjectPool<TestObj, 10> pool;
for (int i = 0; i < 25; ++i) {
auto* o = pool.acquire();
pool.release(o);
}
EXPECT_GE(pool.pool_size(), 30u);
}
TEST(ThreadSafePoolTest, ConcurrentAcquire) {
ThreadSafeObjectPool<TestObj, 64> pool;
std::atomic<int> count{0};
auto worker = [&]() {
for (int i = 0; i < 100; ++i) {
auto* o = pool.acquire();
o->x = i;
pool.release(o);
count++;
}
};
std::thread t1(worker), t2(worker), t3(worker), t4(worker);
t1.join(); t2.join(); t3.join(); t4.join();
EXPECT_EQ(count, 400);
}
TEST(CowPtrTest, ReadOnly) {
CowPtr<std::vector<int>> cow(std::vector<int>{1, 2, 3});
EXPECT_EQ(cow.read().size(), 3u);
EXPECT_EQ(cow.read()[0], 1);
}
TEST(CowPtrTest, ShallowCopy) {
CowPtr<std::vector<int>> cow(std::vector<int>{1, 2, 3});
auto cow2 = cow.shallow_copy();
EXPECT_EQ(cow.use_count(), 2);
EXPECT_EQ(cow2.use_count(), 2);
}
TEST(CowPtrTest, WriteDetaches) {
CowPtr<std::vector<int>> cow(std::vector<int>{1, 2, 3});
auto cow2 = cow.shallow_copy();
EXPECT_EQ(cow.use_count(), 2);
cow.write().push_back(4);
EXPECT_EQ(cow.use_count(), 1); // detached
EXPECT_EQ(cow2.use_count(), 1);
EXPECT_EQ(cow2.read().size(), 3u); // cow2 unchanged
}
TEST(CowPtrTest, DeepCopy) {
CowPtr<std::vector<int>> cow(std::vector<int>{1, 2, 3});
auto cow2 = cow.deep_copy();
EXPECT_EQ(cow.use_count(), 1);
EXPECT_EQ(cow2.use_count(), 1);
}
TEST(CowPtrTest, UniqueCheck) {
CowPtr<int> cow(42);
EXPECT_TRUE(cow.unique());
auto cow2 = cow.shallow_copy();
EXPECT_FALSE(cow.unique());
}