diff options
Diffstat (limited to 'indra/llmath/tests/mathmisc_test.cpp')
-rw-r--r-- | indra/llmath/tests/mathmisc_test.cpp | 1446 |
1 files changed, 723 insertions, 723 deletions
diff --git a/indra/llmath/tests/mathmisc_test.cpp b/indra/llmath/tests/mathmisc_test.cpp index 63d35bee96..ff0899e975 100644 --- a/indra/llmath/tests/mathmisc_test.cpp +++ b/indra/llmath/tests/mathmisc_test.cpp @@ -1,723 +1,723 @@ -/**
- * @file math.cpp
- * @author Phoenix
- * @date 2005-09-26
- * @brief Tests for the llmath library.
- *
- * $LicenseInfo:firstyear=2005&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/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("BasicLindenMath");
-
- 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 = ll_round(val);
- ensure("float ll_round value 1", (430903 == val1));
- val = -430903.9f;
- val1 = ll_round(val);
- ensure("float ll_round value 2", (-430904 == val1));
- }
-
- template<> template<>
- void math_object::test<9>()
- {
- F32 val = 430905.2654f, nearest = 100.f;
- val = ll_round(val, nearest);
- ensure("float ll_round value 1", (430900 == val));
- val = -430905.2654f, nearest = 10.f;
- val = ll_round(val, nearest);
- ensure("float ll_round value 1", (-430910 == val));
- }
-
- template<> template<>
- void math_object::test<10>()
- {
- F64 val = 430905.2654, nearest = 100.0;
- val = ll_round(val, nearest);
- ensure("double ll_round value 1", (430900 == val));
- val = -430905.2654, nearest = 10.0;
- val = ll_round(val, nearest);
- ensure("double ll_round 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("LLUUID");
-
- 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("LLCrc");
-
- 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>()
- {
- skip("See SNOW-620. Neither the test nor the code being tested seem good. Also sim-only.");
-
- // 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));
- }
- }
-}
-
+/** + * @file math.cpp + * @author Phoenix + * @date 2005-09-26 + * @brief Tests for the llmath library. + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/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("BasicLindenMath"); + + 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 = ll_round(val); + ensure("float ll_round value 1", (430903 == val1)); + val = -430903.9f; + val1 = ll_round(val); + ensure("float ll_round value 2", (-430904 == val1)); + } + + template<> template<> + void math_object::test<9>() + { + F32 val = 430905.2654f, nearest = 100.f; + val = ll_round(val, nearest); + ensure("float ll_round value 1", (430900 == val)); + val = -430905.2654f, nearest = 10.f; + val = ll_round(val, nearest); + ensure("float ll_round value 1", (-430910 == val)); + } + + template<> template<> + void math_object::test<10>() + { + F64 val = 430905.2654, nearest = 100.0; + val = ll_round(val, nearest); + ensure("double ll_round value 1", (430900 == val)); + val = -430905.2654, nearest = 10.0; + val = ll_round(val, nearest); + ensure("double ll_round 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("LLUUID"); + + 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("LLCrc"); + + 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>() + { + skip("See SNOW-620. Neither the test nor the code being tested seem good. Also sim-only."); + + // 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)); + } + } +} + |