Loading files
3d097a032This file is pinned to the selected release. Download the original to open it in your design tool.
// Copyright (c) 2025 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.
#include <gtest/gtest.h>
#include <BVH_Box.hxx>
#include <Precision.hxx>
TEST(BVH_BoxTest, DefaultConstructor)
{
BVH_Box<double, 3> aBox;
EXPECT_FALSE(aBox.IsValid());
}
TEST(BVH_BoxTest, ConstructorWithCorners)
{
BVH_Vec3d aMin(0.0, 0.0, 0.0);
BVH_Vec3d aMax(1.0, 2.0, 3.0);
BVH_Box<double, 3> aBox(aMin, aMax);
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 3.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Add)
{
BVH_Box<double, 3> aBox;
aBox.Add(BVH_Vec3d(1.0, 2.0, 3.0));
EXPECT_TRUE(aBox.IsValid());
aBox.Add(BVH_Vec3d(-1.0, -2.0, -3.0));
EXPECT_NEAR(aBox.CornerMin().x(), -1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), -2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), -3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 3.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Combine)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(3.0, 3.0, 3.0));
aBox1.Combine(aBox2);
EXPECT_NEAR(aBox1.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMin().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMin().z(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().y(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().z(), 3.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Size)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(2.0, 3.0, 4.0));
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 2.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 3.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 4.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Center)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(2.0, 4.0, 6.0));
BVH_Vec3d aCenter = aBox.Center();
EXPECT_NEAR(aCenter.x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aCenter.y(), 2.0, Precision::Confusion());
EXPECT_NEAR(aCenter.z(), 3.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Area)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 2.0, 3.0));
// Surface area = 2 * (1*2 + 2*3 + 1*3) = 2 * (2 + 6 + 3) = 22
double anArea = aBox.Area();
EXPECT_NEAR(anArea, 22.0, Precision::Confusion());
}
TEST(BVH_BoxTest, IsOut)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(3.0, 3.0, 3.0));
BVH_Box<double, 3> aBox3(BVH_Vec3d(0.5, 0.5, 0.5), BVH_Vec3d(1.5, 1.5, 1.5));
EXPECT_TRUE(aBox1.IsOut(aBox2));
EXPECT_FALSE(aBox1.IsOut(aBox3));
}
TEST(BVH_BoxTest, Clear)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
EXPECT_TRUE(aBox.IsValid());
aBox.Clear();
EXPECT_FALSE(aBox.IsValid());
}
TEST(BVH_BoxTest, Box2D)
{
BVH_Box<double, 2> aBox;
aBox.Add(BVH_Vec2d(0.0, 0.0));
aBox.Add(BVH_Vec2d(1.0, 1.0));
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 1.0, Precision::Confusion());
// Area in 2D is width * height (actual area)
double anArea = aBox.Area();
EXPECT_NEAR(anArea, 1.0, Precision::Confusion()); // 1 * 1 = 1
}
TEST(BVH_BoxTest, Box4D)
{
BVH_Box<double, 4> aBox;
aBox.Add(BVH_Vec4d(0.0, 0.0, 0.0, 0.0));
aBox.Add(BVH_Vec4d(1.0, 2.0, 3.0, 4.0));
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().w(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().w(), 4.0, Precision::Confusion());
}
TEST(BVH_BoxTest, FloatPrecision)
{
BVH_Box<float, 3> aBox;
aBox.Add(BVH_Vec3f(0.0f, 0.0f, 0.0f));
aBox.Add(BVH_Vec3f(1.0f, 2.0f, 3.0f));
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().x(), 1.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().y(), 2.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().z(), 3.0f, 1e-5f);
}
TEST(BVH_BoxTest, SinglePointBox)
{
BVH_Box<double, 3> aBox;
aBox.Add(BVH_Vec3d(5.0, 5.0, 5.0));
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 5.0, Precision::Confusion());
// Size should be zero
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 0.0, Precision::Confusion());
// Area should be zero
EXPECT_NEAR(aBox.Area(), 0.0, Precision::Confusion());
}
TEST(BVH_BoxTest, NegativeCoordinates)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(-10.0, -20.0, -30.0), BVH_Vec3d(-5.0, -10.0, -15.0));
EXPECT_TRUE(aBox.IsValid());
BVH_Vec3d aCenter = aBox.Center();
EXPECT_NEAR(aCenter.x(), -7.5, Precision::Confusion());
EXPECT_NEAR(aCenter.y(), -15.0, Precision::Confusion());
EXPECT_NEAR(aCenter.z(), -22.5, Precision::Confusion());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 15.0, Precision::Confusion());
}
TEST(BVH_BoxTest, LargeValues)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(1e10, 1e10, 1e10),
BVH_Vec3d(1e10 + 1.0, 1e10 + 2.0, 1e10 + 3.0));
EXPECT_TRUE(aBox.IsValid());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 1.0, 1e-5);
EXPECT_NEAR(aSize.y(), 2.0, 1e-5);
EXPECT_NEAR(aSize.z(), 3.0, 1e-5);
}
TEST(BVH_BoxTest, CombineWithInvalid)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2; // Invalid box
aBox1.Combine(aBox2);
// Box1 should remain unchanged when combining with invalid box
EXPECT_TRUE(aBox1.IsValid());
EXPECT_NEAR(aBox1.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().x(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, AddToInvalid)
{
BVH_Box<double, 3> aBox1; // Invalid box
BVH_Box<double, 3> aBox2(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
aBox1.Combine(aBox2);
// Box1 should now be valid and equal to aBox2
EXPECT_TRUE(aBox1.IsValid());
EXPECT_NEAR(aBox1.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().x(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, IsOutTouchingBoxes)
{
// Boxes that touch at a face
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(1.0, 0.0, 0.0), BVH_Vec3d(2.0, 1.0, 1.0));
// Touching boxes should NOT be "out"
EXPECT_FALSE(aBox1.IsOut(aBox2));
}
TEST(BVH_BoxTest, IsOutTouchingAtEdge)
{
// Boxes that touch at an edge
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(1.0, 1.0, 0.0), BVH_Vec3d(2.0, 2.0, 1.0));
// Touching at edge should NOT be "out"
EXPECT_FALSE(aBox1.IsOut(aBox2));
}
TEST(BVH_BoxTest, IsOutTouchingAtCorner)
{
// Boxes that touch at a corner
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(1.0, 1.0, 1.0), BVH_Vec3d(2.0, 2.0, 2.0));
// Touching at corner should NOT be "out"
EXPECT_FALSE(aBox1.IsOut(aBox2));
}
TEST(BVH_BoxTest, AreaUnitCube)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
// Surface area of unit cube = 6 faces * 1 = 6
// Formula: 2 * (xy + yz + xz) = 2 * (1 + 1 + 1) = 6
EXPECT_NEAR(aBox.Area(), 6.0, Precision::Confusion());
}
TEST(BVH_BoxTest, CenterAtOrigin)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(-1.0, -1.0, -1.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Vec3d aCenter = aBox.Center();
EXPECT_NEAR(aCenter.x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aCenter.y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aCenter.z(), 0.0, Precision::Confusion());
}
TEST(BVH_BoxTest, MultipleAdds)
{
BVH_Box<double, 3> aBox;
// Add points in random order
aBox.Add(BVH_Vec3d(5.0, 3.0, 1.0));
aBox.Add(BVH_Vec3d(-2.0, 7.0, 4.0));
aBox.Add(BVH_Vec3d(1.0, -1.0, 8.0));
aBox.Add(BVH_Vec3d(0.0, 2.0, -3.0));
EXPECT_NEAR(aBox.CornerMin().x(), -2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), -1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), -3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 7.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 8.0, Precision::Confusion());
}
TEST(BVH_BoxTest, FlatBox2D)
{
// Box with zero extent in Z (flat)
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 5.0), BVH_Vec3d(3.0, 4.0, 5.0));
EXPECT_TRUE(aBox.IsValid());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 4.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 0.0, Precision::Confusion());
// Area = 2 * (3*4 + 4*0 + 3*0) = 24
EXPECT_NEAR(aBox.Area(), 24.0, Precision::Confusion());
}
TEST(BVH_BoxTest, FlatBox1D)
{
// Box with zero extent in Y and Z (line)
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 5.0, 5.0), BVH_Vec3d(10.0, 5.0, 5.0));
EXPECT_TRUE(aBox.IsValid());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 10.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 0.0, Precision::Confusion());
// For degenerate box (line), Area returns length of longest dimension
// This is an implementation detail - the surface area formula gives 0,
// but Area() returns the length (10) for degenerate cases
double anArea = aBox.Area();
EXPECT_GE(anArea, 0.0);
}
TEST(BVH_BoxTest, CombineMultiple)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(2.0, 0.0, 0.0), BVH_Vec3d(3.0, 1.0, 1.0));
BVH_Box<double, 3> aBox3(BVH_Vec3d(0.0, 2.0, 0.0), BVH_Vec3d(1.0, 3.0, 1.0));
aBox1.Combine(aBox2);
aBox1.Combine(aBox3);
EXPECT_NEAR(aBox1.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMin().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMin().z(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().y(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().z(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, IsOutPartialOverlap)
{
// Boxes that partially overlap
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(2.0, 2.0, 2.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(1.0, 1.0, 1.0), BVH_Vec3d(3.0, 3.0, 3.0));
EXPECT_FALSE(aBox1.IsOut(aBox2));
EXPECT_FALSE(aBox2.IsOut(aBox1));
}
TEST(BVH_BoxTest, IsOutContained)
{
// One box completely inside another
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(3.0, 3.0, 3.0));
EXPECT_FALSE(aBox1.IsOut(aBox2));
EXPECT_FALSE(aBox2.IsOut(aBox1));
}
TEST(BVH_BoxTest, IsOutFloat)
{
BVH_Box<float, 3> aBox1(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
BVH_Box<float, 3> aBox2(BVH_Vec3f(2.0f, 2.0f, 2.0f), BVH_Vec3f(3.0f, 3.0f, 3.0f));
BVH_Box<float, 3> aBox3(BVH_Vec3f(0.5f, 0.5f, 0.5f), BVH_Vec3f(1.5f, 1.5f, 1.5f));
EXPECT_TRUE(aBox1.IsOut(aBox2));
EXPECT_FALSE(aBox1.IsOut(aBox3));
}
TEST(BVH_BoxTest, Box2DIsOut)
{
BVH_Box<double, 2> aBox1(BVH_Vec2d(0.0, 0.0), BVH_Vec2d(1.0, 1.0));
BVH_Box<double, 2> aBox2(BVH_Vec2d(2.0, 2.0), BVH_Vec2d(3.0, 3.0));
BVH_Box<double, 2> aBox3(BVH_Vec2d(0.5, 0.5), BVH_Vec2d(1.5, 1.5));
EXPECT_TRUE(aBox1.IsOut(aBox2));
EXPECT_FALSE(aBox1.IsOut(aBox3));
}
TEST(BVH_BoxTest, Box2DCenter)
{
BVH_Box<double, 2> aBox(BVH_Vec2d(0.0, 0.0), BVH_Vec2d(4.0, 6.0));
BVH_Vec2d aCenter = aBox.Center();
EXPECT_NEAR(aCenter.x(), 2.0, Precision::Confusion());
EXPECT_NEAR(aCenter.y(), 3.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Box2DSize)
{
BVH_Box<double, 2> aBox(BVH_Vec2d(1.0, 2.0), BVH_Vec2d(4.0, 7.0));
BVH_Vec2d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 5.0, Precision::Confusion());
}
TEST(BVH_BoxTest, VerySmallBox)
{
// Test with very small values
double aSmall = 1e-10;
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(aSmall, aSmall, aSmall));
EXPECT_TRUE(aBox.IsValid());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), aSmall, 1e-15);
EXPECT_NEAR(aSize.y(), aSmall, 1e-15);
EXPECT_NEAR(aSize.z(), aSmall, 1e-15);
}
TEST(BVH_BoxTest, SymmetricBox)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(-5.0, -5.0, -5.0), BVH_Vec3d(5.0, 5.0, 5.0));
BVH_Vec3d aCenter = aBox.Center();
EXPECT_NEAR(aCenter.x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aCenter.y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aCenter.z(), 0.0, Precision::Confusion());
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 10.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 10.0, Precision::Confusion());
}
TEST(BVH_BoxTest, NonCubicBox)
{
// Box with different dimensions on each axis
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 10.0, 100.0));
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 100.0, Precision::Confusion());
// Area = 2 * (1*10 + 10*100 + 1*100) = 2 * (10 + 1000 + 100) = 2220
EXPECT_NEAR(aBox.Area(), 2220.0, Precision::Confusion());
}
TEST(BVH_BoxTest, ClearAndReuse)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
EXPECT_TRUE(aBox.IsValid());
aBox.Clear();
EXPECT_FALSE(aBox.IsValid());
// Reuse the box
aBox.Add(BVH_Vec3d(5.0, 5.0, 5.0));
aBox.Add(BVH_Vec3d(10.0, 10.0, 10.0));
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 10.0, Precision::Confusion());
}
TEST(BVH_BoxTest, IsOutSameBox)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
// Box should not be "out" of itself
EXPECT_FALSE(aBox.IsOut(aBox));
}
TEST(BVH_BoxTest, CombineSameBox)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
aBox1.Combine(aBox2);
// Should remain unchanged
EXPECT_NEAR(aBox1.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox1.CornerMax().x(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, IsOutNearMiss)
{
// Boxes that are very close but not touching
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(1.0001, 0.0, 0.0), BVH_Vec3d(2.0, 1.0, 1.0));
EXPECT_TRUE(aBox1.IsOut(aBox2));
}
TEST(BVH_BoxTest, AddDuplicatePoint)
{
BVH_Box<double, 3> aBox;
aBox.Add(BVH_Vec3d(1.0, 2.0, 3.0));
aBox.Add(BVH_Vec3d(1.0, 2.0, 3.0));
aBox.Add(BVH_Vec3d(1.0, 2.0, 3.0));
// Should still be a single point box
EXPECT_NEAR(aBox.CornerMin().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aBox.Size().x(), 0.0, Precision::Confusion());
}
// =======================================================================================
// Tests for Single-Point Constructor
// =======================================================================================
TEST(BVH_BoxTest, SinglePointConstructor)
{
BVH_Vec3d aPoint(5.0, 10.0, 15.0);
BVH_Box<double, 3> aBox(aPoint);
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), 15.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 15.0, Precision::Confusion());
// Size should be zero
BVH_Vec3d aSize = aBox.Size();
EXPECT_NEAR(aSize.x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aSize.y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aSize.z(), 0.0, Precision::Confusion());
}
TEST(BVH_BoxTest, SinglePointConstructor2D)
{
BVH_Vec2d aPoint(3.0, 7.0);
BVH_Box<double, 2> aBox(aPoint);
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.CornerMin().x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 7.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 7.0, Precision::Confusion());
}
TEST(BVH_BoxTest, SinglePointConstructorOrigin)
{
BVH_Vec3d aOrigin(0.0, 0.0, 0.0);
BVH_Box<double, 3> aBox(aOrigin);
EXPECT_TRUE(aBox.IsValid());
EXPECT_NEAR(aBox.Center().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center().y(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center().z(), 0.0, Precision::Confusion());
}
// =======================================================================================
// Tests for Center(axis) method
// =======================================================================================
TEST(BVH_BoxTest, CenterByAxis)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 20.0, 30.0));
EXPECT_NEAR(aBox.Center(0), 5.0, Precision::Confusion()); // X axis
EXPECT_NEAR(aBox.Center(1), 10.0, Precision::Confusion()); // Y axis
EXPECT_NEAR(aBox.Center(2), 15.0, Precision::Confusion()); // Z axis
}
TEST(BVH_BoxTest, CenterByAxis2D)
{
BVH_Box<double, 2> aBox(BVH_Vec2d(2.0, 4.0), BVH_Vec2d(8.0, 12.0));
EXPECT_NEAR(aBox.Center(0), 5.0, Precision::Confusion()); // X axis
EXPECT_NEAR(aBox.Center(1), 8.0, Precision::Confusion()); // Y axis
}
TEST(BVH_BoxTest, CenterByAxisNegative)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(-10.0, -20.0, -30.0), BVH_Vec3d(10.0, 20.0, 30.0));
EXPECT_NEAR(aBox.Center(0), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center(1), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center(2), 0.0, Precision::Confusion());
}
TEST(BVH_BoxTest, CenterByAxis4D)
{
BVH_Box<double, 4> aBox(BVH_Vec4d(0.0, 0.0, 0.0, 0.0), BVH_Vec4d(4.0, 6.0, 8.0, 10.0));
EXPECT_NEAR(aBox.Center(0), 2.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center(1), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox.Center(2), 4.0, Precision::Confusion());
}
// =======================================================================================
// Tests for IsOut(point) method
// =======================================================================================
TEST(BVH_BoxTest, IsOutPoint_Inside)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Point inside
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(5.0, 5.0, 5.0)));
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(1.0, 1.0, 1.0)));
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(9.0, 9.0, 9.0)));
}
TEST(BVH_BoxTest, IsOutPoint_Outside)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Points outside
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(11.0, 5.0, 5.0))); // Outside X
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(5.0, 11.0, 5.0))); // Outside Y
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(5.0, 5.0, 11.0))); // Outside Z
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(-1.0, 5.0, 5.0))); // Outside negative X
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(15.0, 15.0, 15.0))); // Far outside
}
TEST(BVH_BoxTest, IsOutPoint_OnBoundary)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Points on boundary (should be considered inside)
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(0.0, 5.0, 5.0))); // On min X face
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(10.0, 5.0, 5.0))); // On max X face
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(5.0, 0.0, 5.0))); // On min Y face
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(5.0, 10.0, 5.0))); // On max Y face
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(0.0, 0.0, 0.0))); // At corner
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(10.0, 10.0, 10.0))); // At opposite corner
}
TEST(BVH_BoxTest, IsOutPoint_InvalidBox)
{
BVH_Box<double, 3> aBox; // Invalid box
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(0.0, 0.0, 0.0)));
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(5.0, 5.0, 5.0)));
}
TEST(BVH_BoxTest, IsOutPoint_2D)
{
BVH_Box<double, 2> aBox(BVH_Vec2d(0.0, 0.0), BVH_Vec2d(5.0, 5.0));
EXPECT_FALSE(aBox.IsOut(BVH_Vec2d(2.5, 2.5))); // Inside
EXPECT_TRUE(aBox.IsOut(BVH_Vec2d(6.0, 2.5))); // Outside X
EXPECT_TRUE(aBox.IsOut(BVH_Vec2d(2.5, 6.0))); // Outside Y
EXPECT_FALSE(aBox.IsOut(BVH_Vec2d(0.0, 0.0))); // On corner
}
TEST(BVH_BoxTest, IsOutPoint_NegativeCoords)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(-5.0, -5.0, -5.0), BVH_Vec3d(5.0, 5.0, 5.0));
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(0.0, 0.0, 0.0))); // At center
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(-3.0, -3.0, -3.0))); // Inside negative
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(-6.0, 0.0, 0.0))); // Outside negative X
}
// =======================================================================================
// Tests for Contains() methods
// =======================================================================================
TEST(BVH_BoxTest, Contains_FullyContained)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(8.0, 8.0, 8.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_TRUE(isContained);
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_NotContained)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(8.0, 8.0, 8.0), BVH_Vec3d(12.0, 12.0, 12.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_FALSE(isContained);
EXPECT_TRUE(hasOverlap); // They overlap but box2 is not fully contained
}
TEST(BVH_BoxTest, Contains_NoOverlap)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(5.0, 5.0, 5.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(10.0, 10.0, 10.0), BVH_Vec3d(15.0, 15.0, 15.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_FALSE(isContained);
EXPECT_FALSE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_SameBox)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_TRUE(isContained);
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_TouchingBoundary)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(5.0, 5.0, 5.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_TRUE(isContained);
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_InvalidBox)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2; // Invalid
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_FALSE(isContained);
EXPECT_FALSE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_InvalidContainer)
{
BVH_Box<double, 3> aBox1; // Invalid
BVH_Box<double, 3> aBox2(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_FALSE(isContained);
EXPECT_FALSE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_PartialOverlapOneAxis)
{
BVH_Box<double, 3> aBox1(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
BVH_Box<double, 3> aBox2(BVH_Vec3d(5.0, 5.0, 5.0), BVH_Vec3d(15.0, 8.0, 8.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_FALSE(isContained); // Not fully contained (extends in X)
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, Contains_2D)
{
BVH_Box<double, 2> aBox1(BVH_Vec2d(0.0, 0.0), BVH_Vec2d(10.0, 10.0));
BVH_Box<double, 2> aBox2(BVH_Vec2d(2.0, 2.0), BVH_Vec2d(8.0, 8.0));
bool hasOverlap = false;
bool isContained = aBox1.Contains(aBox2, hasOverlap);
EXPECT_TRUE(isContained);
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, ContainsByCorners_FullyContained)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
bool hasOverlap = false;
bool isContained = aBox.Contains(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(8.0, 8.0, 8.0), hasOverlap);
EXPECT_TRUE(isContained);
EXPECT_TRUE(hasOverlap);
}
TEST(BVH_BoxTest, ContainsByCorners_NotContained)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
bool hasOverlap = false;
bool isContained =
aBox.Contains(BVH_Vec3d(8.0, 8.0, 8.0), BVH_Vec3d(12.0, 12.0, 12.0), hasOverlap);
EXPECT_FALSE(isContained);
EXPECT_TRUE(hasOverlap);
}
// =======================================================================================
// Tests for Transform() and Transformed() methods
// =======================================================================================
TEST(BVH_BoxTest, Transform_Identity)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
NCollection_Mat4<double> aIdentity;
aIdentity.InitIdentity();
aBox.Transform(aIdentity);
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Transform_Translation)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
NCollection_Mat4<double> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<double>(5.0, 10.0, 15.0));
aBox.Transform(aTransform);
EXPECT_NEAR(aBox.CornerMin().x(), 5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), 10.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), 15.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 6.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 11.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 16.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Transform_Scale)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
NCollection_Mat4<double> aTransform;
aTransform.InitIdentity();
aTransform.SetValue(0, 0, 2.0); // Scale X by 2
aTransform.SetValue(1, 1, 3.0); // Scale Y by 3
aTransform.SetValue(2, 2, 4.0); // Scale Z by 4
aBox.Transform(aTransform);
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 3.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 4.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Transformed_Identity)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
NCollection_Mat4<double> aIdentity;
aIdentity.InitIdentity();
BVH_Box<double, 3> aTransformed = aBox.Transformed(aIdentity);
// Original should be unchanged
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
// Transformed should be the same
EXPECT_NEAR(aTransformed.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMax().x(), 1.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Transformed_Translation)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
NCollection_Mat4<double> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<double>(10.0, 20.0, 30.0));
BVH_Box<double, 3> aTransformed = aBox.Transformed(aTransform);
// Original should be unchanged
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 1.0, Precision::Confusion());
// Transformed should be translated
EXPECT_NEAR(aTransformed.CornerMin().x(), 10.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMin().y(), 20.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMin().z(), 30.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMax().x(), 11.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMax().y(), 21.0, Precision::Confusion());
EXPECT_NEAR(aTransformed.CornerMax().z(), 31.0, Precision::Confusion());
}
TEST(BVH_BoxTest, Transform_InvalidBox)
{
BVH_Box<double, 3> aBox; // Invalid box
NCollection_Mat4<double> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<double>(10.0, 20.0, 30.0));
aBox.Transform(aTransform);
// Should remain invalid
EXPECT_FALSE(aBox.IsValid());
}
TEST(BVH_BoxTest, Transformed_InvalidBox)
{
BVH_Box<double, 3> aBox; // Invalid box
NCollection_Mat4<double> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<double>(10.0, 20.0, 30.0));
BVH_Box<double, 3> aTransformed = aBox.Transformed(aTransform);
// Should remain invalid
EXPECT_FALSE(aTransformed.IsValid());
}
// =======================================================================================
// Tests for Corner Modification
// =======================================================================================
TEST(BVH_BoxTest, ModifyCornerMin)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Modify via non-const reference
aBox.CornerMin() = BVH_Vec3d(-5.0, -5.0, -5.0);
EXPECT_NEAR(aBox.CornerMin().x(), -5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), -5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), -5.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().x(), 10.0, Precision::Confusion());
}
TEST(BVH_BoxTest, ModifyCornerMax)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Modify via non-const reference
aBox.CornerMax() = BVH_Vec3d(20.0, 30.0, 40.0);
EXPECT_NEAR(aBox.CornerMax().x(), 20.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().y(), 30.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 40.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().x(), 0.0, Precision::Confusion());
}
TEST(BVH_BoxTest, ModifyCornerComponents)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Modify individual components
aBox.CornerMin().x() = -1.0;
aBox.CornerMin().y() = -2.0;
aBox.CornerMax().z() = 15.0;
EXPECT_NEAR(aBox.CornerMin().x(), -1.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().y(), -2.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMin().z(), 0.0, Precision::Confusion());
EXPECT_NEAR(aBox.CornerMax().z(), 15.0, Precision::Confusion());
}
// =======================================================================================
// Additional Edge Cases
// =======================================================================================
TEST(BVH_BoxTest, Area_DegenerateBox2D)
{
// 2D box with zero area should return perimeter
BVH_Box<double, 2> aBox(BVH_Vec2d(0.0, 0.0), BVH_Vec2d(5.0, 0.0));
double anArea = aBox.Area();
// For degenerate case, returns sum of dimensions
EXPECT_GE(anArea, 0.0);
EXPECT_NEAR(anArea, 5.0, Precision::Confusion()); // Width + height = 5 + 0
}
TEST(BVH_BoxTest, IsOut_TwoCorners_Overlapping)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Test with overlapping region
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(5.0, 5.0, 5.0), BVH_Vec3d(15.0, 15.0, 15.0)));
}
TEST(BVH_BoxTest, IsOut_TwoCorners_Disjoint)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Test with disjoint region
EXPECT_TRUE(aBox.IsOut(BVH_Vec3d(20.0, 20.0, 20.0), BVH_Vec3d(30.0, 30.0, 30.0)));
}
TEST(BVH_BoxTest, IsOut_TwoCorners_Contained)
{
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(10.0, 10.0, 10.0));
// Test with fully contained region
EXPECT_FALSE(aBox.IsOut(BVH_Vec3d(2.0, 2.0, 2.0), BVH_Vec3d(8.0, 8.0, 8.0)));
}
TEST(BVH_BoxTest, Constexpr_Construction)
{
// Test that constexpr constructors work at compile time
constexpr BVH_Vec3d aMin(0.0, 0.0, 0.0);
constexpr BVH_Vec3d aMax(1.0, 1.0, 1.0);
constexpr BVH_Box<double, 3> aBox(aMin, aMax);
static_assert(aBox.IsValid(), "Constexpr box should be valid");
}
TEST(BVH_BoxTest, Constexpr_SinglePoint)
{
constexpr BVH_Vec3d aPoint(5.0, 5.0, 5.0);
constexpr BVH_Box<double, 3> aBox(aPoint);
static_assert(aBox.IsValid(), "Constexpr single-point box should be valid");
}
TEST(BVH_BoxTest, Constexpr_DefaultInvalid)
{
constexpr BVH_Box<double, 3> aBox;
static_assert(!aBox.IsValid(), "Default constexpr box should be invalid");
}
// =======================================================================================
// Tests for Transform/Transformed with float precision
// =======================================================================================
TEST(BVH_BoxTest, Transform_Float_Identity)
{
BVH_Box<float, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
NCollection_Mat4<float> aIdentity;
aIdentity.InitIdentity();
aBox.Transform(aIdentity);
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().x(), 1.0f, 1e-5f);
}
TEST(BVH_BoxTest, Transform_Float_Translation)
{
BVH_Box<float, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
NCollection_Mat4<float> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<float>(5.0f, 10.0f, 15.0f));
aBox.Transform(aTransform);
EXPECT_NEAR(aBox.CornerMin().x(), 5.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMin().y(), 10.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMin().z(), 15.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().x(), 6.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().y(), 11.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().z(), 16.0f, 1e-5f);
}
TEST(BVH_BoxTest, Transform_Float_Scale)
{
BVH_Box<float, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
NCollection_Mat4<float> aTransform;
aTransform.InitIdentity();
aTransform.SetValue(0, 0, 2.0f); // Scale X by 2
aTransform.SetValue(1, 1, 3.0f); // Scale Y by 3
aTransform.SetValue(2, 2, 4.0f); // Scale Z by 4
aBox.Transform(aTransform);
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().x(), 2.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().y(), 3.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().z(), 4.0f, 1e-5f);
}
TEST(BVH_BoxTest, Transformed_Float_Translation)
{
BVH_Box<float, 3> aBox(BVH_Vec3f(0.0f, 0.0f, 0.0f), BVH_Vec3f(1.0f, 1.0f, 1.0f));
NCollection_Mat4<float> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<float>(10.0f, 20.0f, 30.0f));
BVH_Box<float, 3> aTransformed = aBox.Transformed(aTransform);
// Original should be unchanged
EXPECT_NEAR(aBox.CornerMin().x(), 0.0f, 1e-5f);
EXPECT_NEAR(aBox.CornerMax().x(), 1.0f, 1e-5f);
// Transformed should be translated
EXPECT_NEAR(aTransformed.CornerMin().x(), 10.0f, 1e-5f);
EXPECT_NEAR(aTransformed.CornerMin().y(), 20.0f, 1e-5f);
EXPECT_NEAR(aTransformed.CornerMin().z(), 30.0f, 1e-5f);
EXPECT_NEAR(aTransformed.CornerMax().x(), 11.0f, 1e-5f);
EXPECT_NEAR(aTransformed.CornerMax().y(), 21.0f, 1e-5f);
EXPECT_NEAR(aTransformed.CornerMax().z(), 31.0f, 1e-5f);
}
TEST(BVH_BoxTest, Transform_Float_InvalidBox)
{
BVH_Box<float, 3> aBox; // Invalid box
NCollection_Mat4<float> aTransform;
aTransform.InitIdentity();
aTransform.SetColumn(3, NCollection_Vec3<float>(10.0f, 20.0f, 30.0f));
aBox.Transform(aTransform);
// Should remain invalid
EXPECT_FALSE(aBox.IsValid());
}