#include #include #include #include "interact/SlicePlaneMath.hpp" using namespace geopro::render::interact; namespace { void expectVec(const Vec3& a, double x, double y, double z, double eps = 1e-9) { EXPECT_NEAR(a[0], x, eps); EXPECT_NEAR(a[1], y, eps); EXPECT_NEAR(a[2], z, eps); } } // namespace // ── axisNormal:轴向法向(spec F22–F24)+ 任意 45°(F25)── TEST(SlicePlaneMath, AxisNormalUpDownIsZ) { expectVec(axisNormal(SliceAxis::UpDown), 0, 0, 1); } TEST(SlicePlaneMath, AxisNormalFrontBackIsY) { expectVec(axisNormal(SliceAxis::FrontBack), 0, 1, 0); } TEST(SlicePlaneMath, AxisNormalLeftRightIsX) { expectVec(axisNormal(SliceAxis::LeftRight), 1, 0, 0); } TEST(SlicePlaneMath, AxisNormalObliqueIs45) { const auto n = axisNormal(SliceAxis::Oblique); const double s = std::sqrt(0.5); expectVec(n, s, 0, s); EXPECT_NEAR(norm(n), 1.0, 1e-9); // 单位向量 } // ── boundsCenter ── TEST(SlicePlaneMath, BoundsCenter) { expectVec(boundsCenter({0, 10, -4, 4, 0, 6}), 5, 0, 3); } // ── advanceOrigin:沿法向平移(滚轮推进,D46)── TEST(SlicePlaneMath, AdvanceAlongZ) { expectVec(advanceOrigin({1, 2, 3}, {0, 0, 1}, 5.0), 1, 2, 8); } TEST(SlicePlaneMath, AdvanceBackward) { expectVec(advanceOrigin({1, 2, 3}, {0, 0, 1}, -2.0), 1, 2, 1); } TEST(SlicePlaneMath, AdvanceNormalizesDirection) { // 非单位法向:先归一化再推进,步长为世界距离。 expectVec(advanceOrigin({0, 0, 0}, {0, 0, 5}, 3.0), 0, 0, 3); } TEST(SlicePlaneMath, AdvanceObliqueMovesAlong45) { const auto o = advanceOrigin({0, 0, 0}, {1, 0, 1}, std::sqrt(2.0)); expectVec(o, 1, 0, 1); // 沿 45° 推进 √2 → (1,0,1) } // ── clampToBounds:推进出体外被夹回(滚轮限位)── TEST(SlicePlaneMath, ClampInsideUnchanged) { expectVec(clampToBounds({5, 0, 3}, {0, 10, -4, 4, 0, 6}), 5, 0, 3); } TEST(SlicePlaneMath, ClampOutsideHigh) { expectVec(clampToBounds({5, 0, 99}, {0, 10, -4, 4, 0, 6}), 5, 0, 6); } TEST(SlicePlaneMath, ClampOutsideLow) { expectVec(clampToBounds({-5, 0, -1}, {0, 10, -4, 4, 0, 6}), 0, 0, 0); } // ── faceOnCamera:双击正视(E54)── // 法向 +Y:相机退到 focal+Y*dist,视线 = -Y,viewUp = +Z(切面内向上)。 TEST(SlicePlaneMath, FaceOnFrontBackNormal) { const auto cam = faceOnCamera({0, 0, 0}, {0, 1, 0}, 10.0); expectVec(cam.position, 0, 10, 0); // viewUp 与法向正交且偏 +Z。 EXPECT_NEAR(dot(cam.viewUp, Vec3{0, 1, 0}), 0.0, 1e-9); EXPECT_GT(cam.viewUp[2], 0.5); } // 法向 +X:position=focal+X*dist,viewUp 偏 +Z。 TEST(SlicePlaneMath, FaceOnLeftRightNormal) { const auto cam = faceOnCamera({1, 2, 3}, {1, 0, 0}, 5.0); expectVec(cam.position, 6, 2, 3); EXPECT_NEAR(dot(cam.viewUp, Vec3{1, 0, 0}), 0.0, 1e-9); EXPECT_GT(cam.viewUp[2], 0.5); } // 法向竖直 +Z(上下切片):viewUp 不能再取 +Z(与法向共线),兜底取 +Y。 TEST(SlicePlaneMath, FaceOnVerticalNormalFallsBackToY) { const auto cam = faceOnCamera({0, 0, 0}, {0, 0, 1}, 8.0); expectVec(cam.position, 0, 0, 8); // viewUp 与法向(+Z)正交(z≈0),且非零。 EXPECT_NEAR(cam.viewUp[2], 0.0, 1e-9); EXPECT_NEAR(norm(cam.viewUp), 1.0, 1e-9); } // 法向竖直 -Z 同样兜底。 TEST(SlicePlaneMath, FaceOnVerticalDownNormalFallsBack) { const auto cam = faceOnCamera({0, 0, 0}, {0, 0, -1}, 4.0); expectVec(cam.position, 0, 0, -4); EXPECT_NEAR(cam.viewUp[2], 0.0, 1e-9); EXPECT_NEAR(norm(cam.viewUp), 1.0, 1e-9); } // 非单位法向:position 用归一化法向 → 距焦点恰为 dist。 TEST(SlicePlaneMath, FaceOnNormalizesNormal) { const auto cam = faceOnCamera({0, 0, 0}, {0, 3, 0}, 6.0); expectVec(cam.position, 0, 6, 0); } // ── wheelStep:步长 = 沿法向体素间距 × voxels × 方向(spacing=三轴间距,X法向取X间距)── TEST(SlicePlaneMath, WheelStepForwardPositive) { EXPECT_GT(wheelStep({0.1, 0.1, 0.05}, {1, 0, 0}, 2, +1), 0.0); } TEST(SlicePlaneMath, WheelStepBackwardNegative) { EXPECT_LT(wheelStep({0.1, 0.1, 0.05}, {1, 0, 0}, 2, -1), 0.0); } TEST(SlicePlaneMath, WheelStepScalesWithVoxels) { const double fine = wheelStep({0.1, 0.1, 0.05}, {1, 0, 0}, 1, 1); const double coarse = wheelStep({0.1, 0.1, 0.05}, {1, 0, 0}, 10, 1); EXPECT_GT(coarse, fine); // voxels 越大步长越大(Shift 粗调) } // 只取法向那条轴的【间距】(非总长):长轴间距大也不影响 Z 法向步长;1 体素 = Z 间距 0.05。 TEST(SlicePlaneMath, WheelStepUsesNormalAxisSpacing) { const double zStep = wheelStep({100.0, 0.1, 0.05}, {0, 0, 1}, 1, 1); // Z 法向 → 取 Z 间距 0.05 EXPECT_NEAR(zStep, 0.05, 1e-9); // 与 X 间距 100 无关 } // ── nearestPlane:找点所在切片(按到平面距离最小)── TEST(SlicePlaneMath, NearestPlaneEmptyIsMinusOne) { EXPECT_EQ(nearestPlane({}, {}, {0, 0, 0}), -1); } TEST(SlicePlaneMath, NearestPlanePicksClosest) { // 两张水平切片 z=0 与 z=10(法向 +Z);点 z=8 → 更近 z=10(索引 1)。 std::vector centers{{0, 0, 0}, {0, 0, 10}}; std::vector normals{{0, 0, 1}, {0, 0, 1}}; EXPECT_EQ(nearestPlane(centers, normals, {5, 5, 8}), 1); EXPECT_EQ(nearestPlane(centers, normals, {5, 5, 2}), 0); } TEST(SlicePlaneMath, NearestPlaneIgnoresInPlaneOffset) { // 单张 z=0 水平面:点无论 x/y 多远,只要 z=0 距离为 0 → 命中。 std::vector centers{{0, 0, 0}}; std::vector normals{{0, 0, 1}}; EXPECT_EQ(nearestPlane(centers, normals, {999, -999, 0}), 0); } // ── 向量工具 ── TEST(SlicePlaneMath, NormalizeZeroFallsBack) { expectVec(normalize({0, 0, 0}), 0, 0, 1); } TEST(SlicePlaneMath, CrossBasic) { expectVec(cross({1, 0, 0}, {0, 1, 0}), 0, 0, 1); }