Skip to content
38 changes: 33 additions & 5 deletions indra/llmath/tests/llbbox_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,11 @@ namespace

CHECK(bbox1.getExtentLocal() == LLVector3(2.0f, 2.0f, 2.0f));

// Local: the rotation and position do not enter into it.
LLBBox bbox2(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)),
LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 5.0f, 6.0f));

CHECK(bbox1.getExtentLocal() == LLVector3(2.0f, 2.0f, 2.0f));
CHECK(bbox2.getExtentLocal() == LLVector3(2.0f, 3.0f, 4.0f));
}

TEST_CASE("LLBBox: addPointLocal grows the box", "[LLBBox]")
Expand Down Expand Up @@ -217,9 +218,6 @@ namespace

TEST_CASE("LLBBox: addBBoxLocal after addPointAgent on a rotated box", "[LLBBox]")
{
//
// test the addBBoxAgent() method
//

LLBBox bbox1(LLVector3(1.0f, 1.0f, 1.0f), LLQuaternion(1.0, 0.0, 0.0, 1.0),
LLVector3(2.0f, 2.0f, 2.0f), LLVector3(4.0f, 4.0f, 4.0f));
Expand All @@ -236,6 +234,36 @@ namespace
CHECK(bbox1.getMaxLocal() == LLVector3(10.0f, 10.0f, 10.0f));
}

TEST_CASE("LLBBox: addBBoxAgent into an empty box takes the other box's frame", "[LLBBox]")
{
const LLQuaternion quarter(ANGLE, LLVector3(0.0f, 0.0f, 1.0f));
LLBBox bbox1;
bbox1.addBBoxAgent(LLBBox(LLVector3(1.0f, 2.0f, 3.0f), quarter,
LLVector3(0.0f, 0.0f, 0.0f), LLVector3(2.0f, 4.0f, 6.0f)));

CHECK(bbox1.getPositionAgent() == LLVector3(1.0f, 2.0f, 3.0f));
CHECK(bbox1.getRotation() == quarter);
CHECK(APPROX_EQUAL(bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)));
CHECK(APPROX_EQUAL(bbox1.getMaxLocal(), LLVector3(2.0f, 4.0f, 6.0f)));
}

TEST_CASE("LLBBox: addBBoxAgent grows a box by the other box's corners in agent space", "[LLBBox]")
{
// An axis-aligned box at the origin, holding only the origin.
LLBBox bbox1;
bbox1.addPointLocal(LLVector3(0.0f, 0.0f, 0.0f));

// Two by one by one, turned a quarter about z, at (10, 0, 0): x goes
// to y and y to -x, so its corners span x 9 to 10, y 0 to 2, z 0 to 1.
bbox1.addBBoxAgent(LLBBox(LLVector3(10.0f, 0.0f, 0.0f), LLQuaternion(ANGLE, LLVector3(0.0f, 0.0f, 1.0f)),
LLVector3(0.0f, 0.0f, 0.0f), LLVector3(2.0f, 1.0f, 1.0f)));

CHECK(bbox1.getPositionAgent() == LLVector3(0.0f, 0.0f, 0.0f));
CHECK(bbox1.getRotation() == LLQuaternion());
CHECK(APPROX_EQUAL(bbox1.getMinLocal(), LLVector3(0.0f, 0.0f, 0.0f)));
CHECK(APPROX_EQUAL(bbox1.getMaxLocal(), LLVector3(10.0f, 2.0f, 1.0f)));
}

TEST_CASE("LLBBox: expand by zero then a positive then a negative amount", "[LLBBox]")
{
//
Expand Down
8 changes: 6 additions & 2 deletions indra/llmath/tests/llbboxlocal_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,17 @@ namespace
// test the getExtent() method
//

LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f));

CHECK(LLBBoxLocal().getExtent() == LLVector3(0.0f, 0.0f, 0.0f));

LLBBoxLocal bbox3(LLVector3(-1.0f, -1.0f, -1.0f), LLVector3(1.0f, 2.0f, 0.0f));

CHECK(bbox3.getExtent() == LLVector3(2.0f, 3.0f, 1.0f));

// Max less min, so negative where min is above max.
LLBBoxLocal bbox2(LLVector3(0.0f, 0.0f, 0.0f), LLVector3(-1.0f, -1.0f, -1.0f));

UNSCOPED_INFO("min above max");
CHECK(bbox2.getExtent() == LLVector3(-1.0f, -1.0f, -1.0f));
}

TEST_CASE("LLBBoxLocal: addPoint grows the box only for points outside it", "[LLBBoxLocal]")
Expand Down
76 changes: 62 additions & 14 deletions indra/llmath/tests/llquaternion_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "linden_common.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <string_view>

#include "../v4math.h"
Expand All @@ -49,7 +50,45 @@ namespace
CHECK(llquat.mQ[3] == 1.f);
}

// The rotation of a matrix comes back as the quaternion it was made from,
// or its negation, which is the same rotation.
void require_same_rotation(const LLQuaternion& actual, const LLQuaternion& expected)
{
using Catch::Matchers::WithinAbs;
INFO("expected " << expected << ", got " << actual);
const F32 sign = dot(actual, expected) < 0.f ? -1.f : 1.f;
for (int i = 0; i < 4; ++i)
{
CAPTURE(i);
REQUIRE_THAT(actual.mQ[i], WithinAbs(sign * expected.mQ[i], 1e-6));
}
}

//test case for explicit LLQuaternion(const LLMatrix4 &mat) fn.
TEST_CASE("LLQuaternion: from an LLMatrix4, the matrix's rotation, its translation ignored", "[LLQuaternion]")
{
CHECK(LLQuaternion(LLMatrix4()) == LLQuaternion::DEFAULT);

const LLQuaternion turn(1.1f, LLVector3(2.f, -1.f, 3.f));
require_same_rotation(LLQuaternion(LLMatrix4(turn)), turn);
require_same_rotation(LLQuaternion(LLMatrix4(turn, LLVector4(5.f, -6.f, 7.f, 1.f))), turn);

// Past a half turn, where w is negative.
const LLQuaternion wide(4.f, LLVector3(-1.f, 0.5f, 0.25f));
require_same_rotation(LLQuaternion(LLMatrix4(wide)), wide);
}

//test case for explicit LLQuaternion(const LLMatrix3 &mat) fn.
TEST_CASE("LLQuaternion: from an LLMatrix3, the matrix's rotation", "[LLQuaternion]")
{
CHECK(LLQuaternion(LLMatrix3()) == LLQuaternion::DEFAULT);

const LLQuaternion turn(1.1f, LLVector3(2.f, -1.f, 3.f));
require_same_rotation(LLQuaternion(LLMatrix3(turn)), turn);
const LLQuaternion wide(4.f, LLVector3(-1.f, 0.5f, 0.25f));
require_same_rotation(LLQuaternion(LLMatrix3(wide)), wide);
}

TEST_CASE("LLQuaternion: LLMatrix4 initRows fills the matrix row by row", "[LLQuaternion]")
{
LLMatrix4 llmat;
Expand Down Expand Up @@ -122,12 +161,6 @@ namespace
res_quat.setQuatInit(x_val, y_val, z_val, w_val);
res_quat.normQuat();

CHECK(is_approx_equal(0.0f, res_quat.mQ[0]));
CHECK(is_approx_equal(0.0f, res_quat.mQ[1]));
CHECK(is_approx_equal(0.0f, res_quat.mQ[2]));
CHECK(is_approx_equal(1.0f, res_quat.mQ[3]));


CHECK(is_approx_equal(0.0f, res_quat.mQ[0]));
CHECK(is_approx_equal(0.0f, res_quat.mQ[1]));
CHECK(is_approx_equal(0.0f, res_quat.mQ[2]));
Expand Down Expand Up @@ -229,7 +262,7 @@ namespace
CHECK(is_approx_equal_fraction(0.283559f, res_nlerp.mQ[2], 16));
CHECK(is_approx_equal_fraction(0.898506f, res_nlerp.mQ[3], 16));

//test case for nlerp(F32 t, const LLQuaternion &q) fn.
//test case for slerp(F32 u, const LLQuaternion &q) fn.
res_slerp = slerp(value1, quat1);
CHECK(is_approx_equal_fraction(1.0f, res_slerp.mQ[0], 16));
CHECK(is_approx_equal_fraction(2.0f, res_slerp.mQ[1], 16));
Expand Down Expand Up @@ -519,9 +552,10 @@ namespace
result = OrderToString(LLQuaternion::ZXY);
CHECK(std::string_view(result) == "ZXY");
CHECK(std::string_view(result) != "XYZ");
CHECK(std::string_view(result) != "YZX");
CHECK(std::string_view(result) != "XZY");
CHECK(std::string_view(result) != "YXZ");
CHECK(std::string_view(result) != "ZYX");
CHECK(std::string_view(result) != "XYZ");

result = OrderToString(LLQuaternion::XZY);
CHECK(std::string_view(result) == "XZY");
Expand Down Expand Up @@ -569,19 +603,33 @@ namespace
CHECK(is_approx_equal_fraction(0.945733f, vect.mV[0], 16));
}

TEST_CASE("LLQuaternion: getEulerAngles leaves an identity quaternion unchanged", "[LLQuaternion]")
TEST_CASE("LLQuaternion: getEulerAngles gives back the angles setEulerAngles took", "[LLQuaternion]")
{
//test case for void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const fn
using Catch::Matchers::WithinAbs;

// The identity has no angles, whatever the outputs held before.
F32 roll = -12.0f;
F32 pitch = -22.43f;
F32 yaw = 11.0f;

LLQuaternion llquat;
llquat.getEulerAngles(&roll, &pitch, &yaw);
CHECK(is_approx_equal(0.000f, llquat.mQ[0]));
CHECK(is_approx_equal(0.000f, llquat.mQ[1]));
CHECK(is_approx_equal(0.000f, llquat.mQ[2]));
CHECK(is_approx_equal(1.000f, llquat.mQ[3]));
CHECK_THAT(roll, WithinAbs(0.f, 1e-6));
CHECK_THAT(pitch, WithinAbs(0.f, 1e-6));
CHECK_THAT(yaw, WithinAbs(0.f, 1e-6));

// Angles within range, pitch short of a quarter turn, round trip.
const F32 angles[][3] = { { 0.3f, -0.2f, 1.1f }, { -2.5f, 1.2f, -0.4f }, { 1.f, 0.f, 0.f } };
for (const auto& set : angles)
{
CAPTURE(set[0], set[1], set[2]);
LLQuaternion turned;
turned.setEulerAngles(set[0], set[1], set[2]);
turned.getEulerAngles(&roll, &pitch, &yaw);
CHECK_THAT(roll, WithinAbs(set[0], 1e-5));
CHECK_THAT(pitch, WithinAbs(set[1], 1e-5));
CHECK_THAT(yaw, WithinAbs(set[2], 1e-5));
}
}

TEST_CASE("LLQuaternion: product and dot evaluate at compile time", "[LLQuaternion]")
Expand Down
26 changes: 23 additions & 3 deletions indra/llmath/tests/llrect_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,8 +311,25 @@ namespace
CHECK_FALSE(rect1 == rect2);
CHECK(rect1 != rect2);

CHECK(rect1 == rect1);
CHECK_FALSE(rect1 != rect1);
// Another rect with the same edges, not the same rect.
LLRectf same;
same.set(-1.0f, 1.0f, 1.0f, -1.0f);
CHECK(rect1 == same);
CHECK_FALSE(rect1 != same);

// Each edge counts.
const LLRectf apart[] = {
LLRectf(-1.5f, 1.0f, 1.0f, -1.0f),
LLRectf(-1.0f, 1.5f, 1.0f, -1.0f),
LLRectf(-1.0f, 1.0f, 1.5f, -1.0f),
LLRectf(-1.0f, 1.0f, 1.0f, -1.5f),
};
for (const LLRectf& other : apart)
{
CAPTURE(other);
CHECK(rect1 != other);
CHECK_FALSE(rect1 == other);
}
}

TEST_CASE("LLRect: copy constructor gives an equal rect", "[LLRect]")
Expand Down Expand Up @@ -478,7 +495,10 @@ namespace
LLRectf rect(1.0f, 3.0f, 3.0f, 1.0f);

CHECK(rect.localPointInRect(0.0f, 0.0f));
CHECK_FALSE(rect.localPointInRect(-0.0001f, -0.001f));
CHECK_FALSE(rect.localPointInRect(-0.0001f, -0.0001f));
// Just outside on one side is outside.
CHECK_FALSE(rect.localPointInRect(-0.0001f, 0.0f));
CHECK_FALSE(rect.localPointInRect(0.0f, -0.0001f));
CHECK(rect.localPointInRect(1.0f, 1.0f));
CHECK_FALSE(rect.localPointInRect(2.0f, 2.0f));
CHECK(rect.localPointInRect(1.999f, 1.999f));
Expand Down
47 changes: 5 additions & 42 deletions indra/llmath/tests/llsdutil_math_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,54 +31,17 @@
#include "altestmatchers.h"
#include "m4math.h"
#include "v2math.h"
#include "v2math.h"
#include "v3color.h"
#include "v3math.h"
#include "v3dmath.h"
#include "v4coloru.h"
#include "v4math.h"
#include "llquaternion.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "stringize.h"
#include <set>
#include <boost/range.hpp>

namespace
{
struct llsdutil_math_data
{
void test_matches(const std::string& proto_key, const LLSD& possibles,
const char** begin, const char** end)
{
std::set<std::string> succeed(begin, end);
LLSD prototype(possibles[proto_key]);
for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap());
pi != pend; ++pi)
{
std::string match(llsd_matches(prototype, pi->second));
std::set<std::string>::const_iterator found = succeed.find(pi->first);
if (found != succeed.end())
{
// This test is supposed to succeed. Comparing to the
// empty string ensures that if the test fails, it will
// display the string received so we can tell what failed.
REQUIRE(match == "");
}
else
{
// This test is supposed to fail. If we get a false match,
// the string 'match' will be empty, which doesn't tell us
// much about which case went awry. So construct a more
// detailed description string.
INFO(proto_key << " shouldn't match " << pi->first);
REQUIRE_FALSE(match.empty());
}
}
}
};

TEST_CASE_METHOD(llsdutil_math_data, "llsdutil_math: LLVector3 round-trips through LLSD", "[llsdutil_math]")
TEST_CASE("llsdutil_math: LLVector3 round-trips through LLSD", "[llsdutil_math]")
{
LLSD sd;
LLVector3 vec1(-1.0, 2.0, -3.0);
Expand All @@ -97,7 +60,7 @@ namespace
sd.clear();
}

TEST_CASE_METHOD(llsdutil_math_data, "llsdutil_math: LLVector3d round-trips through LLSD", "[llsdutil_math]")
TEST_CASE("llsdutil_math: LLVector3d round-trips through LLSD", "[llsdutil_math]")
{
LLSD sd;
LLVector3d vec1((F64)(U64L(0xFEDCBA9876543210) << 2), -1., 0);
Expand All @@ -109,7 +72,7 @@ namespace
CHECK(vec1 == vec3);
}

TEST_CASE_METHOD(llsdutil_math_data, "llsdutil_math: LLVector2 and its LLSD round-trip both ways", "[llsdutil_math]")
TEST_CASE("llsdutil_math: LLVector2 and its LLSD round-trip both ways", "[llsdutil_math]")
{
LLSD sd;
LLVector2 vec((F32) -3., (F32) 4.2);
Expand All @@ -121,7 +84,7 @@ namespace
CHECK_THAT(sd, ll_test::LLSDEquals(sd2));
}

TEST_CASE_METHOD(llsdutil_math_data, "llsdutil_math: LLQuaternion and its LLSD round-trip both ways", "[llsdutil_math]")
TEST_CASE("llsdutil_math: LLQuaternion and its LLSD round-trip both ways", "[llsdutil_math]")
{
LLSD sd;
LLQuaternion quat((F32) 1., (F32) -0.98, (F32) 2.3, (F32) 0xffff);
Expand All @@ -133,7 +96,7 @@ namespace
CHECK_THAT(sd, ll_test::LLSDEquals(sd2));
}

TEST_CASE_METHOD(llsdutil_math_data, "llsdutil_math: LLColor4 and its LLSD round-trip both ways", "[llsdutil_math]")
TEST_CASE("llsdutil_math: LLColor4 and its LLSD round-trip both ways", "[llsdutil_math]")
{
LLSD sd;
LLColor4 c(1.0f, 2.2f, 4.0f, 7.f);
Expand Down
5 changes: 0 additions & 5 deletions indra/llmath/tests/m3math_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@
#pragma warning(disable: 4702)
#endif

#if LL_WINDOWS
// disable unreachable code warnings caused by usage of skip.
#pragma warning(disable: 4702)
#endif

namespace
{
//test case for setIdentity() fn.
Expand Down
Loading
Loading