summaryrefslogtreecommitdiff
path: root/indra/llmath/tests
diff options
context:
space:
mode:
authorAdam Moss <moss@lindenlab.com>2009-10-08 13:36:46 +0000
committerAdam Moss <moss@lindenlab.com>2009-10-08 13:36:46 +0000
commit5fe65644b5ac422b2d7f1558f9e38e8d0dbc7674 (patch)
treee508f307e902b9312df7124b43eafda8f0c38436 /indra/llmath/tests
parentec08dec2074be37e1b40a4385169ba5ed68b1923 (diff)
DEV-41088 convert legacy 'math' test module into 'mathmisc' integration test.
Diffstat (limited to 'indra/llmath/tests')
-rw-r--r--indra/llmath/tests/llquaternion_test.cpp2
-rw-r--r--indra/llmath/tests/mathmisc_test.cpp727
2 files changed, 727 insertions, 2 deletions
diff --git a/indra/llmath/tests/llquaternion_test.cpp b/indra/llmath/tests/llquaternion_test.cpp
index d071a1a8bd..bfa475d91b 100644
--- a/indra/llmath/tests/llquaternion_test.cpp
+++ b/indra/llmath/tests/llquaternion_test.cpp
@@ -32,8 +32,6 @@
* $/LicenseInfo$
*/
-#include <cmath>
-
#include "linden_common.h"
#include "../test/lltut.h"
diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp
new file mode 100644
index 0000000000..ea42f6e001
--- /dev/null
+++ b/indra/llmath/tests/mathmisc_test.cpp
@@ -0,0 +1,727 @@
+/**
+ * @file math.cpp
+ * @author Phoenix
+ * @date 2005-09-26
+ * @brief Tests for the llmath library.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ *
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../test/lltut.h"
+
+#include "llcrc.h"
+#include "llrand.h"
+#include "lluuid.h"
+
+#include "../llline.h"
+#include "../llmath.h"
+#include "../llsphere.h"
+#include "../v3math.h"
+
+namespace tut
+{
+ struct math_data
+ {
+ };
+ typedef test_group<math_data> math_test;
+ typedef math_test::object math_object;
+ tut::math_test tm("basic_linden_math");
+
+ template<> template<>
+ void math_object::test<1>()
+ {
+ S32 val = 89543;
+ val = llabs(val);
+ ensure("integer absolute value 1", (89543 == val));
+ val = -500;
+ val = llabs(val);
+ ensure("integer absolute value 2", (500 == val));
+ }
+
+ template<> template<>
+ void math_object::test<2>()
+ {
+ F32 val = -2583.4f;
+ val = llabs(val);
+ ensure("float absolute value 1", (2583.4f == val));
+ val = 430903.f;
+ val = llabs(val);
+ ensure("float absolute value 2", (430903.f == val));
+ }
+
+ template<> template<>
+ void math_object::test<3>()
+ {
+ F64 val = 387439393.987329839;
+ val = llabs(val);
+ ensure("double absolute value 1", (387439393.987329839 == val));
+ val = -8937843.9394878;
+ val = llabs(val);
+ ensure("double absolute value 2", (8937843.9394878 == val));
+ }
+
+ template<> template<>
+ void math_object::test<4>()
+ {
+ F32 val = 430903.9f;
+ S32 val1 = lltrunc(val);
+ ensure("float truncate value 1", (430903 == val1));
+ val = -2303.9f;
+ val1 = lltrunc(val);
+ ensure("float truncate value 2", (-2303 == val1));
+ }
+
+ template<> template<>
+ void math_object::test<5>()
+ {
+ F64 val = 387439393.987329839 ;
+ S32 val1 = lltrunc(val);
+ ensure("float truncate value 1", (387439393 == val1));
+ val = -387439393.987329839;
+ val1 = lltrunc(val);
+ ensure("float truncate value 2", (-387439393 == val1));
+ }
+
+ template<> template<>
+ void math_object::test<6>()
+ {
+ F32 val = 430903.2f;
+ S32 val1 = llfloor(val);
+ ensure("float llfloor value 1", (430903 == val1));
+ val = -430903.9f;
+ val1 = llfloor(val);
+ ensure("float llfloor value 2", (-430904 == val1));
+ }
+
+ template<> template<>
+ void math_object::test<7>()
+ {
+ F32 val = 430903.2f;
+ S32 val1 = llceil(val);
+ ensure("float llceil value 1", (430904 == val1));
+ val = -430903.9f;
+ val1 = llceil(val);
+ ensure("float llceil value 2", (-430903 == val1));
+ }
+
+ template<> template<>
+ void math_object::test<8>()
+ {
+ F32 val = 430903.2f;
+ S32 val1 = llround(val);
+ ensure("float llround value 1", (430903 == val1));
+ val = -430903.9f;
+ val1 = llround(val);
+ ensure("float llround value 2", (-430904 == val1));
+ }
+
+ template<> template<>
+ void math_object::test<9>()
+ {
+ F32 val = 430905.2654f, nearest = 100.f;
+ val = llround(val, nearest);
+ ensure("float llround value 1", (430900 == val));
+ val = -430905.2654f, nearest = 10.f;
+ val = llround(val, nearest);
+ ensure("float llround value 1", (-430910 == val));
+ }
+
+ template<> template<>
+ void math_object::test<10>()
+ {
+ F64 val = 430905.2654, nearest = 100.0;
+ val = llround(val, nearest);
+ ensure("double llround value 1", (430900 == val));
+ val = -430905.2654, nearest = 10.0;
+ val = llround(val, nearest);
+ ensure("double llround value 1", (-430910.00000 == val));
+ }
+
+ template<> template<>
+ void math_object::test<11>()
+ {
+ const F32 F_PI = 3.1415926535897932384626433832795f;
+ F32 angle = 3506.f;
+ angle = llsimple_angle(angle);
+ ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI));
+ angle = -431.f;
+ angle = llsimple_angle(angle);
+ ensure("llsimple_angle value 1", (angle <=F_PI && angle >= -F_PI));
+ }
+}
+
+namespace tut
+{
+ struct uuid_data
+ {
+ LLUUID id;
+ };
+ typedef test_group<uuid_data> uuid_test;
+ typedef uuid_test::object uuid_object;
+ tut::uuid_test tu("uuid");
+
+ template<> template<>
+ void uuid_object::test<1>()
+ {
+ ensure("uuid null", id.isNull());
+ id.generate();
+ ensure("generate not null", id.notNull());
+ id.setNull();
+ ensure("set null", id.isNull());
+ }
+
+ template<> template<>
+ void uuid_object::test<2>()
+ {
+ id.generate();
+ LLUUID a(id);
+ ensure_equals("copy equal", id, a);
+ a.generate();
+ ensure_not_equals("generate not equal", id, a);
+ a = id;
+ ensure_equals("assignment equal", id, a);
+ }
+
+ template<> template<>
+ void uuid_object::test<3>()
+ {
+ id.generate();
+ LLUUID copy(id);
+ LLUUID mask;
+ mask.generate();
+ copy ^= mask;
+ ensure_not_equals("mask not equal", id, copy);
+ copy ^= mask;
+ ensure_equals("mask back", id, copy);
+ }
+
+ template<> template<>
+ void uuid_object::test<4>()
+ {
+ id.generate();
+ std::string id_str = id.asString();
+ LLUUID copy(id_str.c_str());
+ ensure_equals("string serialization", id, copy);
+ }
+
+}
+
+namespace tut
+{
+ struct crc_data
+ {
+ };
+ typedef test_group<crc_data> crc_test;
+ typedef crc_test::object crc_object;
+ tut::crc_test tc("crc");
+
+ template<> template<>
+ void crc_object::test<1>()
+ {
+ /* Test buffer update and individual char update */
+ const char TEST_BUFFER[] = "hello &#$)$&Nd0";
+ LLCRC c1, c2;
+ c1.update((U8*)TEST_BUFFER, sizeof(TEST_BUFFER) - 1);
+ char* rh = (char*)TEST_BUFFER;
+ while(*rh != '\0')
+ {
+ c2.update(*rh);
+ ++rh;
+ }
+ ensure_equals("crc update 1", c1.getCRC(), c2.getCRC());
+ }
+
+ template<> template<>
+ void crc_object::test<2>()
+ {
+ /* Test mixing of buffer and individual char update */
+ const char TEST_BUFFER1[] = "Split Buffer one $^%$%#@$";
+ const char TEST_BUFFER2[] = "Split Buffer two )(8723#5dsds";
+ LLCRC c1, c2;
+ c1.update((U8*)TEST_BUFFER1, sizeof(TEST_BUFFER1) - 1);
+ char* rh = (char*)TEST_BUFFER2;
+ while(*rh != '\0')
+ {
+ c1.update(*rh);
+ ++rh;
+ }
+
+ rh = (char*)TEST_BUFFER1;
+ while(*rh != '\0')
+ {
+ c2.update(*rh);
+ ++rh;
+ }
+ c2.update((U8*)TEST_BUFFER2, sizeof(TEST_BUFFER2) - 1);
+
+ ensure_equals("crc update 2", c1.getCRC(), c2.getCRC());
+ }
+}
+
+namespace tut
+{
+ struct sphere_data
+ {
+ };
+ typedef test_group<sphere_data> sphere_test;
+ typedef sphere_test::object sphere_object;
+ tut::sphere_test tsphere("LLSphere");
+
+ template<> template<>
+ void sphere_object::test<1>()
+ {
+ // test LLSphere::contains() and ::overlaps()
+ S32 number_of_tests = 10;
+ for (S32 test = 0; test < number_of_tests; ++test)
+ {
+ LLVector3 first_center(1.f, 1.f, 1.f);
+ F32 first_radius = 3.f;
+ LLSphere first_sphere( first_center, first_radius );
+
+ F32 half_millimeter = 0.0005f;
+ LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+ direction.normalize();
+
+ F32 distance = ll_frand(first_radius - 2.f * half_millimeter);
+ LLVector3 second_center = first_center + distance * direction;
+ F32 second_radius = first_radius - distance - half_millimeter;
+ LLSphere second_sphere( second_center, second_radius );
+ ensure("first sphere should contain the second", first_sphere.contains(second_sphere));
+ ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere));
+
+ distance = first_radius + ll_frand(first_radius);
+ second_center = first_center + distance * direction;
+ second_radius = distance - first_radius + half_millimeter;
+ second_sphere.set( second_center, second_radius );
+ ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere));
+ ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere));
+
+ distance = first_radius + ll_frand(first_radius) + half_millimeter;
+ second_center = first_center + distance * direction;
+ second_radius = distance - first_radius - half_millimeter;
+ second_sphere.set( second_center, second_radius );
+ ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere));
+ ensure("first sphere should NOT overlap the second", !first_sphere.overlaps(second_sphere));
+ }
+ }
+
+ template<> template<>
+ void sphere_object::test<2>()
+ {
+ // test LLSphere::getBoundingSphere()
+ S32 number_of_tests = 100;
+ S32 number_of_spheres = 10;
+ F32 sphere_center_range = 32.f;
+ F32 sphere_radius_range = 5.f;
+
+ for (S32 test = 0; test < number_of_tests; ++test)
+ {
+ // gegnerate a bunch of random sphere
+ std::vector< LLSphere > sphere_list;
+ for (S32 sphere_count=0; sphere_count < number_of_spheres; ++sphere_count)
+ {
+ LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
+ direction.normalize();
+ F32 distance = ll_frand(sphere_center_range);
+ LLVector3 center = distance * direction;
+ F32 radius = ll_frand(sphere_radius_range);
+ LLSphere sphere( center, radius );
+ sphere_list.push_back(sphere);
+ }
+
+ // compute the bounding sphere
+ LLSphere bounding_sphere = LLSphere::getBoundingSphere(sphere_list);
+
+ // make sure all spheres are inside the bounding sphere
+ {
+ std::vector< LLSphere >::const_iterator sphere_itr;
+ for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
+ {
+ ensure("sphere should be contained by the bounding sphere", bounding_sphere.contains(*sphere_itr));
+ }
+ }
+
+ // TODO -- improve LLSphere::getBoundingSphere() to the point where
+ // we can reduce the 'expansion' in the two tests below to about
+ // 2 mm or less
+
+ F32 expansion = 0.005f;
+ // move all spheres out a little bit
+ // and count how many are NOT contained
+ {
+ std::vector< LLVector3 > uncontained_directions;
+ std::vector< LLSphere >::iterator sphere_itr;
+ for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
+ {
+ LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter();
+ direction.normalize();
+
+ sphere_itr->setCenter( sphere_itr->getCenter() + expansion * direction );
+ if (! bounding_sphere.contains( *sphere_itr ) )
+ {
+ uncontained_directions.push_back(direction);
+ }
+ }
+ ensure("when moving spheres out there should be at least two uncontained spheres",
+ uncontained_directions.size() > 1);
+
+ /* TODO -- when the bounding sphere algorithm is improved we can open up this test
+ * at the moment it occasionally fails when the sphere collection is tight and small
+ * (2 meters or less)
+ if (2 == uncontained_directions.size() )
+ {
+ // if there were only two uncontained spheres then
+ // the two directions should be nearly opposite
+ F32 dir_dot = uncontained_directions[0] * uncontained_directions[1];
+ ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f);
+ }
+ */
+ }
+
+ // compute the new bounding sphere
+ bounding_sphere = LLSphere::getBoundingSphere(sphere_list);
+
+ // increase the size of all spheres a little bit
+ // and count how many are NOT contained
+ {
+ std::vector< LLVector3 > uncontained_directions;
+ std::vector< LLSphere >::iterator sphere_itr;
+ for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
+ {
+ LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter();
+ direction.normalize();
+
+ sphere_itr->setRadius( sphere_itr->getRadius() + expansion );
+ if (! bounding_sphere.contains( *sphere_itr ) )
+ {
+ uncontained_directions.push_back(direction);
+ }
+ }
+ ensure("when boosting sphere radii there should be at least two uncontained spheres",
+ uncontained_directions.size() > 1);
+
+ /* TODO -- when the bounding sphere algorithm is improved we can open up this test
+ * at the moment it occasionally fails when the sphere collection is tight and small
+ * (2 meters or less)
+ if (2 == uncontained_directions.size() )
+ {
+ // if there were only two uncontained spheres then
+ // the two directions should be nearly opposite
+ F32 dir_dot = uncontained_directions[0] * uncontained_directions[1];
+ ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f);
+ }
+ */
+ }
+ }
+ }
+}
+
+namespace tut
+{
+ F32 SMALL_RADIUS = 1.0f;
+ F32 MEDIUM_RADIUS = 5.0f;
+ F32 LARGE_RADIUS = 10.0f;
+
+ struct line_data
+ {
+ };
+ typedef test_group<line_data> line_test;
+ typedef line_test::object line_object;
+ tut::line_test tline("LLLine");
+
+ template<> template<>
+ void line_object::test<1>()
+ {
+ // this is a test for LLLine::intersects(point) which returns TRUE
+ // if the line passes within some tolerance of point
+
+ // these tests will have some floating point error,
+ // so we need to specify how much error is ok
+ F32 allowable_relative_error = 0.00001f;
+ S32 number_of_tests = 100;
+ for (S32 test = 0; test < number_of_tests; ++test)
+ {
+ // generate some random point to be on the line
+ LLVector3 point_on_line( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ point_on_line.normalize();
+ point_on_line *= ll_frand(LARGE_RADIUS);
+
+ // generate some random point to "intersect"
+ LLVector3 random_direction ( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ random_direction.normalize();
+
+ LLVector3 random_offset( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ random_offset.normalize();
+ random_offset *= ll_frand(SMALL_RADIUS);
+
+ LLVector3 point = point_on_line + MEDIUM_RADIUS * random_direction
+ + random_offset;
+
+ // compute the axis of approach (a unit vector between the points)
+ LLVector3 axis_of_approach = point - point_on_line;
+ axis_of_approach.normalize();
+
+ // compute the direction of the the first line (perp to axis_of_approach)
+ LLVector3 first_dir( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ first_dir.normalize();
+ F32 dot = first_dir * axis_of_approach;
+ first_dir -= dot * axis_of_approach; // subtract component parallel to axis
+ first_dir.normalize();
+
+ // construct the line
+ LLVector3 another_point_on_line = point_on_line + ll_frand(LARGE_RADIUS) * first_dir;
+ LLLine line(another_point_on_line, point_on_line);
+
+ // test that the intersection point is within MEDIUM_RADIUS + SMALL_RADIUS
+ F32 test_radius = MEDIUM_RADIUS + SMALL_RADIUS;
+ test_radius += (LARGE_RADIUS * allowable_relative_error);
+ ensure("line should pass near intersection point", line.intersects(point, test_radius));
+
+ test_radius = allowable_relative_error * (point - point_on_line).length();
+ ensure("line should intersect point used to define it", line.intersects(point_on_line, test_radius));
+ }
+ }
+
+ template<> template<>
+ void line_object::test<2>()
+ {
+ /*
+ These tests fail intermittently on all platforms - see DEV-16600
+ Commenting this out until dev has time to investigate.
+
+ // this is a test for LLLine::nearestApproach(LLLIne) method
+ // which computes the point on a line nearest another line
+
+ // these tests will have some floating point error,
+ // so we need to specify how much error is ok
+ // TODO -- make nearestApproach() algorithm more accurate so
+ // we can tighten the allowable_error. Most tests are tighter
+ // than one milimeter, however when doing randomized testing
+ // you can walk into inaccurate cases.
+ F32 allowable_relative_error = 0.001f;
+ S32 number_of_tests = 100;
+ for (S32 test = 0; test < number_of_tests; ++test)
+ {
+ // generate two points to be our known nearest approaches
+ LLVector3 some_point( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ some_point.normalize();
+ some_point *= ll_frand(LARGE_RADIUS);
+
+ LLVector3 another_point( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ another_point.normalize();
+ another_point *= ll_frand(LARGE_RADIUS);
+
+ // compute the axis of approach (a unit vector between the points)
+ LLVector3 axis_of_approach = another_point - some_point;
+ axis_of_approach.normalize();
+
+ // compute the direction of the the first line (perp to axis_of_approach)
+ LLVector3 first_dir( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ F32 dot = first_dir * axis_of_approach;
+ first_dir -= dot * axis_of_approach; // subtract component parallel to axis
+ first_dir.normalize(); // normalize
+
+ // compute the direction of the the second line
+ LLVector3 second_dir( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ dot = second_dir * axis_of_approach;
+ second_dir -= dot * axis_of_approach;
+ second_dir.normalize();
+
+ // make sure the lines aren't too parallel,
+ dot = fabsf(first_dir * second_dir);
+ if (dot > 0.99f)
+ {
+ // skip this test, we're not interested in testing
+ // the intractible cases
+ continue;
+ }
+
+ // construct the lines
+ LLVector3 first_point = some_point + ll_frand(LARGE_RADIUS) * first_dir;
+ LLLine first_line(first_point, some_point);
+
+ LLVector3 second_point = another_point + ll_frand(LARGE_RADIUS) * second_dir;
+ LLLine second_line(second_point, another_point);
+
+ // compute the points of nearest approach
+ LLVector3 some_computed_point = first_line.nearestApproach(second_line);
+ LLVector3 another_computed_point = second_line.nearestApproach(first_line);
+
+ // compute the error
+ F32 first_error = (some_point - some_computed_point).length();
+ F32 scale = llmax((some_point - another_point).length(), some_point.length());
+ scale = llmax(scale, another_point.length());
+ scale = llmax(scale, 1.f);
+ F32 first_relative_error = first_error / scale;
+
+ F32 second_error = (another_point - another_computed_point).length();
+ F32 second_relative_error = second_error / scale;
+
+ //if (first_relative_error > allowable_relative_error)
+ //{
+ // std::cout << "first_error = " << first_error
+ // << " first_relative_error = " << first_relative_error
+ // << " scale = " << scale
+ // << " dir_dot = " << (first_dir * second_dir)
+ // << std::endl;
+ //}
+ //if (second_relative_error > allowable_relative_error)
+ //{
+ // std::cout << "second_error = " << second_error
+ // << " second_relative_error = " << second_relative_error
+ // << " scale = " << scale
+ // << " dist = " << (some_point - another_point).length()
+ // << " dir_dot = " << (first_dir * second_dir)
+ // << std::endl;
+ //}
+
+ // test that the errors are small
+
+ ensure("first line should accurately compute its closest approach",
+ first_relative_error <= allowable_relative_error);
+ ensure("second line should accurately compute its closest approach",
+ second_relative_error <= allowable_relative_error);
+ }
+ */
+ }
+
+ F32 ALMOST_PARALLEL = 0.99f;
+ template<> template<>
+ void line_object::test<3>()
+ {
+ // this is a test for LLLine::getIntersectionBetweenTwoPlanes() method
+
+ // first some known tests
+ LLLine xy_plane(LLVector3(0.f, 0.f, 2.f), LLVector3(0.f, 0.f, 3.f));
+ LLLine yz_plane(LLVector3(2.f, 0.f, 0.f), LLVector3(3.f, 0.f, 0.f));
+ LLLine zx_plane(LLVector3(0.f, 2.f, 0.f), LLVector3(0.f, 3.f, 0.f));
+
+ LLLine x_line;
+ LLLine y_line;
+ LLLine z_line;
+
+ bool x_success = LLLine::getIntersectionBetweenTwoPlanes(x_line, xy_plane, zx_plane);
+ bool y_success = LLLine::getIntersectionBetweenTwoPlanes(y_line, yz_plane, xy_plane);
+ bool z_success = LLLine::getIntersectionBetweenTwoPlanes(z_line, zx_plane, yz_plane);
+
+ ensure("xy and zx planes should intersect", x_success);
+ ensure("yz and xy planes should intersect", y_success);
+ ensure("zx and yz planes should intersect", z_success);
+
+ LLVector3 direction = x_line.getDirection();
+ ensure("x_line should be parallel to x_axis", fabs(direction.mV[VX]) == 1.f
+ && 0.f == direction.mV[VY]
+ && 0.f == direction.mV[VZ] );
+ direction = y_line.getDirection();
+ ensure("y_line should be parallel to y_axis", 0.f == direction.mV[VX]
+ && fabs(direction.mV[VY]) == 1.f
+ && 0.f == direction.mV[VZ] );
+ direction = z_line.getDirection();
+ ensure("z_line should be parallel to z_axis", 0.f == direction.mV[VX]
+ && 0.f == direction.mV[VY]
+ && fabs(direction.mV[VZ]) == 1.f );
+
+ // next some random tests
+ F32 allowable_relative_error = 0.0001f;
+ S32 number_of_tests = 20;
+ for (S32 test = 0; test < number_of_tests; ++test)
+ {
+ // generate the known line
+ LLVector3 some_point( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ some_point.normalize();
+ some_point *= ll_frand(LARGE_RADIUS);
+ LLVector3 another_point( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ another_point.normalize();
+ another_point *= ll_frand(LARGE_RADIUS);
+ LLLine known_intersection(some_point, another_point);
+
+ // compute a plane that intersect the line
+ LLVector3 point_on_plane( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ point_on_plane.normalize();
+ point_on_plane *= ll_frand(LARGE_RADIUS);
+ LLVector3 plane_normal = (point_on_plane - some_point) % known_intersection.getDirection();
+ plane_normal.normalize();
+ LLLine first_plane(point_on_plane, point_on_plane + plane_normal);
+
+ // compute a different plane that intersect the line
+ LLVector3 point_on_different_plane( ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f,
+ ll_frand(2.f) - 1.f);
+ point_on_different_plane.normalize();
+ point_on_different_plane *= ll_frand(LARGE_RADIUS);
+ LLVector3 different_plane_normal = (point_on_different_plane - another_point) % known_intersection.getDirection();
+ different_plane_normal.normalize();
+ LLLine second_plane(point_on_different_plane, point_on_different_plane + different_plane_normal);
+
+ if (fabs(plane_normal * different_plane_normal) > ALMOST_PARALLEL)
+ {
+ // the two planes are approximately parallel, so we won't test this case
+ continue;
+ }
+
+ LLLine measured_intersection;
+ bool success = LLLine::getIntersectionBetweenTwoPlanes(
+ measured_intersection,
+ first_plane,
+ second_plane);
+
+ ensure("plane intersection should succeed", success);
+
+ F32 dot = fabs(known_intersection.getDirection() * measured_intersection.getDirection());
+ ensure("measured intersection should be parallel to known intersection",
+ dot > ALMOST_PARALLEL);
+
+ ensure("measured intersection should pass near known point",
+ measured_intersection.intersects(some_point, LARGE_RADIUS * allowable_relative_error));
+ }
+ }
+}
+