132 lines
5.3 KiB
C++
132 lines
5.3 KiB
C++
#include <gtest/gtest.h>
|
||
|
||
#include <cmath>
|
||
#include <vector>
|
||
|
||
#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:滚轮推进步长(按对角线比例 × 方向)──
|
||
TEST(SlicePlaneMath, WheelStepForwardPositive) {
|
||
EXPECT_GT(wheelStep({0, 10, 0, 0, 0, 0}, +1), 0.0);
|
||
}
|
||
TEST(SlicePlaneMath, WheelStepBackwardNegative) {
|
||
EXPECT_LT(wheelStep({0, 10, 0, 0, 0, 0}, -1), 0.0);
|
||
}
|
||
TEST(SlicePlaneMath, WheelStepScalesWithBounds) {
|
||
const double small = wheelStep({0, 10, 0, 0, 0, 0}, 1);
|
||
const double big = wheelStep({0, 100, 0, 0, 0, 0}, 1);
|
||
EXPECT_GT(big, small); // 体越大步长越大
|
||
}
|
||
|
||
// ── 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<Vec3> centers{{0, 0, 0}, {0, 0, 10}};
|
||
std::vector<Vec3> 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<Vec3> centers{{0, 0, 0}};
|
||
std::vector<Vec3> 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); }
|