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_BinaryTree.hxx>
#include <BVH_Box.hxx>
#include <Precision.hxx>
TEST(BVH_TreeTest, DefaultConstructor)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
EXPECT_EQ(aTree.Length(), 0);
EXPECT_EQ(aTree.Depth(), 0);
}
TEST(BVH_TreeTest, AddLeafNode)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
BVH_Vec3d aMin(0.0, 0.0, 0.0);
BVH_Vec3d aMax(1.0, 1.0, 1.0);
int aNodeIndex = aTree.AddLeafNode(aMin, aMax, 0, 10);
EXPECT_EQ(aNodeIndex, 0);
EXPECT_EQ(aTree.Length(), 1);
EXPECT_TRUE(aTree.IsOuter(0));
EXPECT_EQ(aTree.BegPrimitive(0), 0);
EXPECT_EQ(aTree.EndPrimitive(0), 10);
EXPECT_EQ(aTree.NbPrimitives(0), 11);
}
TEST(BVH_TreeTest, AddInnerNode)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
// Add two leaf nodes first
BVH_Vec3d aMin1(0.0, 0.0, 0.0);
BVH_Vec3d aMax1(1.0, 1.0, 1.0);
int aLeaf1 = aTree.AddLeafNode(aMin1, aMax1, 0, 5);
BVH_Vec3d aMin2(2.0, 0.0, 0.0);
BVH_Vec3d aMax2(3.0, 1.0, 1.0);
int aLeaf2 = aTree.AddLeafNode(aMin2, aMax2, 6, 10);
// Add inner node
BVH_Vec3d aMinRoot(0.0, 0.0, 0.0);
BVH_Vec3d aMaxRoot(3.0, 1.0, 1.0);
int aRoot = aTree.AddInnerNode(aMinRoot, aMaxRoot, aLeaf1, aLeaf2);
EXPECT_EQ(aTree.Length(), 3);
EXPECT_FALSE(aTree.IsOuter(aRoot));
EXPECT_EQ(aTree.template Child<0>(aRoot), aLeaf1);
EXPECT_EQ(aTree.template Child<1>(aRoot), aLeaf2);
}
TEST(BVH_TreeTest, MinMaxPoints)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
BVH_Vec3d aMin(1.0, 2.0, 3.0);
BVH_Vec3d aMax(4.0, 5.0, 6.0);
aTree.AddLeafNode(aMin, aMax, 0, 0);
EXPECT_NEAR(aTree.MinPoint(0).x(), 1.0, Precision::Confusion());
EXPECT_NEAR(aTree.MinPoint(0).y(), 2.0, Precision::Confusion());
EXPECT_NEAR(aTree.MinPoint(0).z(), 3.0, Precision::Confusion());
EXPECT_NEAR(aTree.MaxPoint(0).x(), 4.0, Precision::Confusion());
EXPECT_NEAR(aTree.MaxPoint(0).y(), 5.0, Precision::Confusion());
EXPECT_NEAR(aTree.MaxPoint(0).z(), 6.0, Precision::Confusion());
}
TEST(BVH_TreeTest, Clear)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
aTree.AddLeafNode(BVH_Vec3d(1.0, 0.0, 0.0), BVH_Vec3d(2.0, 1.0, 1.0), 1, 1);
EXPECT_EQ(aTree.Length(), 2);
aTree.Clear();
EXPECT_EQ(aTree.Length(), 0);
EXPECT_EQ(aTree.Depth(), 0);
}
TEST(BVH_TreeTest, Reserve)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
// Reserve should not throw
aTree.Reserve(100);
// Can still add nodes after reserve
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
EXPECT_EQ(aTree.Length(), 1);
}
TEST(BVH_TreeTest, SetOuterInner)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
// Add leaf and change to inner
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
EXPECT_TRUE(aTree.IsOuter(0));
aTree.SetInner(0);
EXPECT_FALSE(aTree.IsOuter(0));
aTree.SetOuter(0);
EXPECT_TRUE(aTree.IsOuter(0));
}
TEST(BVH_TreeTest, Level)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
// Default level is 0
EXPECT_EQ(aTree.Level(0), 0);
// Change level
aTree.Level(0) = 5;
EXPECT_EQ(aTree.Level(0), 5);
}
TEST(BVH_TreeTest, AddLeafNodeWithBox)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
BVH_Box<double, 3> aBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0));
int aNodeIndex = aTree.AddLeafNode(aBox, 0, 5);
EXPECT_EQ(aNodeIndex, 0);
EXPECT_TRUE(aTree.IsOuter(0));
EXPECT_EQ(aTree.BegPrimitive(0), 0);
EXPECT_EQ(aTree.EndPrimitive(0), 5);
}
TEST(BVH_TreeTest, AddInnerNodeWithBox)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
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> aRootBox(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(3.0, 1.0, 1.0));
int aLeaf1 = aTree.AddLeafNode(aBox1, 0, 5);
int aLeaf2 = aTree.AddLeafNode(aBox2, 6, 10);
int aRoot = aTree.AddInnerNode(aRootBox, aLeaf1, aLeaf2);
EXPECT_FALSE(aTree.IsOuter(aRoot));
EXPECT_EQ(aTree.template Child<0>(aRoot), aLeaf1);
EXPECT_EQ(aTree.template Child<1>(aRoot), aLeaf2);
}
TEST(BVH_TreeTest, EstimateSAH)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
// Create a simple tree with root and two leaves
BVH_Vec3d aMin1(0.0, 0.0, 0.0);
BVH_Vec3d aMax1(1.0, 1.0, 1.0);
int aLeaf1 = aTree.AddLeafNode(aMin1, aMax1, 0, 0);
BVH_Vec3d aMin2(2.0, 0.0, 0.0);
BVH_Vec3d aMax2(3.0, 1.0, 1.0);
int aLeaf2 = aTree.AddLeafNode(aMin2, aMax2, 1, 1);
BVH_Vec3d aMinRoot(0.0, 0.0, 0.0);
BVH_Vec3d aMaxRoot(3.0, 1.0, 1.0);
aTree.AddInnerNode(aMinRoot, aMaxRoot, aLeaf1, aLeaf2);
double aSAH = aTree.EstimateSAH();
// SAH should be positive
EXPECT_GT(aSAH, 0.0);
}
TEST(BVH_TreeTest, NbPrimitives)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 5, 15);
// NbPrimitives = EndPrimitive - BegPrimitive + 1 = 15 - 5 + 1 = 11
EXPECT_EQ(aTree.NbPrimitives(0), 11);
}
TEST(BVH_TreeTest, SinglePrimitive)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 7, 7);
EXPECT_EQ(aTree.NbPrimitives(0), 1);
EXPECT_EQ(aTree.BegPrimitive(0), 7);
EXPECT_EQ(aTree.EndPrimitive(0), 7);
}
TEST(BVH_TreeTest, Float3DTree)
{
BVH_Tree<float, 3, BVH_BinaryTree> aTree;
BVH_Vec3f aMin(0.0f, 0.0f, 0.0f);
BVH_Vec3f aMax(1.0f, 1.0f, 1.0f);
aTree.AddLeafNode(aMin, aMax, 0, 5);
EXPECT_EQ(aTree.Length(), 1);
EXPECT_NEAR(aTree.MinPoint(0).x(), 0.0f, 1e-5f);
EXPECT_NEAR(aTree.MaxPoint(0).x(), 1.0f, 1e-5f);
}
TEST(BVH_TreeTest, Tree2D)
{
BVH_Tree<double, 2, BVH_BinaryTree> aTree;
BVH_Vec2d aMin(0.0, 0.0);
BVH_Vec2d aMax(1.0, 1.0);
aTree.AddLeafNode(aMin, aMax, 0, 5);
EXPECT_EQ(aTree.Length(), 1);
EXPECT_NEAR(aTree.MinPoint(0).x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aTree.MinPoint(0).y(), 0.0, Precision::Confusion());
}
TEST(BVH_TreeTest, Tree4D)
{
BVH_Tree<double, 4, BVH_BinaryTree> aTree;
BVH_Vec4d aMin(0.0, 0.0, 0.0, 0.0);
BVH_Vec4d aMax(1.0, 1.0, 1.0, 1.0);
aTree.AddLeafNode(aMin, aMax, 0, 5);
EXPECT_EQ(aTree.Length(), 1);
EXPECT_NEAR(aTree.MinPoint(0).x(), 0.0, Precision::Confusion());
EXPECT_NEAR(aTree.MinPoint(0).w(), 0.0, Precision::Confusion());
}
TEST(BVH_TreeTest, MultipleLeaves)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
for (int i = 0; i < 10; ++i)
{
BVH_Vec3d aMin(i * 1.0, 0.0, 0.0);
BVH_Vec3d aMax(i * 1.0 + 1.0, 1.0, 1.0);
aTree.AddLeafNode(aMin, aMax, i, i);
}
EXPECT_EQ(aTree.Length(), 10);
for (int i = 0; i < 10; ++i)
{
EXPECT_TRUE(aTree.IsOuter(i));
EXPECT_EQ(aTree.BegPrimitive(i), i);
EXPECT_EQ(aTree.EndPrimitive(i), i);
}
}
TEST(BVH_TreeTest, DeepTree)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
// Create leaves
BVH_Vec3d aMin1(0.0, 0.0, 0.0);
BVH_Vec3d aMax1(1.0, 1.0, 1.0);
int aLeaf1 = aTree.AddLeafNode(aMin1, aMax1, 0, 0);
BVH_Vec3d aMin2(1.0, 0.0, 0.0);
BVH_Vec3d aMax2(2.0, 1.0, 1.0);
int aLeaf2 = aTree.AddLeafNode(aMin2, aMax2, 1, 1);
BVH_Vec3d aMin3(2.0, 0.0, 0.0);
BVH_Vec3d aMax3(3.0, 1.0, 1.0);
int aLeaf3 = aTree.AddLeafNode(aMin3, aMax3, 2, 2);
BVH_Vec3d aMin4(3.0, 0.0, 0.0);
BVH_Vec3d aMax4(4.0, 1.0, 1.0);
int aLeaf4 = aTree.AddLeafNode(aMin4, aMax4, 3, 3);
// Create intermediate nodes
BVH_Vec3d aMinI1(0.0, 0.0, 0.0);
BVH_Vec3d aMaxI1(2.0, 1.0, 1.0);
int aInner1 = aTree.AddInnerNode(aMinI1, aMaxI1, aLeaf1, aLeaf2);
BVH_Vec3d aMinI2(2.0, 0.0, 0.0);
BVH_Vec3d aMaxI2(4.0, 1.0, 1.0);
int aInner2 = aTree.AddInnerNode(aMinI2, aMaxI2, aLeaf3, aLeaf4);
// Create root
BVH_Vec3d aMinRoot(0.0, 0.0, 0.0);
BVH_Vec3d aMaxRoot(4.0, 1.0, 1.0);
int aRoot = aTree.AddInnerNode(aMinRoot, aMaxRoot, aInner1, aInner2);
EXPECT_EQ(aTree.Length(), 7);
EXPECT_FALSE(aTree.IsOuter(aRoot));
EXPECT_FALSE(aTree.IsOuter(aInner1));
EXPECT_FALSE(aTree.IsOuter(aInner2));
EXPECT_TRUE(aTree.IsOuter(aLeaf1));
EXPECT_TRUE(aTree.IsOuter(aLeaf4));
}
TEST(BVH_TreeTest, ModifyPrimitiveIndices)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 5);
// Modify primitive indices
aTree.BegPrimitive(0) = 10;
aTree.EndPrimitive(0) = 20;
EXPECT_EQ(aTree.BegPrimitive(0), 10);
EXPECT_EQ(aTree.EndPrimitive(0), 20);
EXPECT_EQ(aTree.NbPrimitives(0), 11);
}
TEST(BVH_TreeTest, ModifyMinMaxPoints)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
// Modify min/max points
aTree.MinPoint(0) = BVH_Vec3d(-1.0, -1.0, -1.0);
aTree.MaxPoint(0) = BVH_Vec3d(2.0, 2.0, 2.0);
EXPECT_NEAR(aTree.MinPoint(0).x(), -1.0, Precision::Confusion());
EXPECT_NEAR(aTree.MaxPoint(0).x(), 2.0, Precision::Confusion());
}
TEST(BVH_TreeTest, ChangeChild)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
int aLeaf1 = aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
int aLeaf2 = aTree.AddLeafNode(BVH_Vec3d(1.0, 0.0, 0.0), BVH_Vec3d(2.0, 1.0, 1.0), 1, 1);
int aLeaf3 = aTree.AddLeafNode(BVH_Vec3d(2.0, 0.0, 0.0), BVH_Vec3d(3.0, 1.0, 1.0), 2, 2);
int aRoot =
aTree.AddInnerNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(2.0, 1.0, 1.0), aLeaf1, aLeaf2);
EXPECT_EQ(aTree.template Child<0>(aRoot), aLeaf1);
EXPECT_EQ(aTree.template Child<1>(aRoot), aLeaf2);
// Change children
aTree.template ChangeChild<0>(aRoot) = aLeaf3;
EXPECT_EQ(aTree.template Child<0>(aRoot), aLeaf3);
EXPECT_EQ(aTree.template Child<1>(aRoot), aLeaf2);
}
TEST(BVH_TreeTest, NodeInfoBuffer)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 5, 10);
// Access node info buffer
const BVH_Array4i& aBuffer = aTree.NodeInfoBuffer();
int aSize = BVH::Array<int, 4>::Size(aBuffer);
EXPECT_EQ(aSize, 1);
}
TEST(BVH_TreeTest, MinMaxPointBuffers)
{
BVH_Tree<double, 3, BVH_BinaryTree> aTree;
aTree.AddLeafNode(BVH_Vec3d(0.0, 0.0, 0.0), BVH_Vec3d(1.0, 1.0, 1.0), 0, 0);
// Access point buffers
const auto& aMinBuffer = aTree.MinPointBuffer();
const auto& aMaxBuffer = aTree.MaxPointBuffer();
int aMinSize = BVH::Array<double, 3>::Size(aMinBuffer);
int aMaxSize = BVH::Array<double, 3>::Size(aMaxBuffer);
EXPECT_EQ(aMinSize, 1);
EXPECT_EQ(aMaxSize, 1);
}