PageRenderTime 58ms CodeModel.GetById 12ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 1ms

/indra/llmath/tests/mathmisc_test.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 723 lines | 463 code | 73 blank | 187 comment | 54 complexity | 80eafdf00fa14748c6257de32f5f3019 MD5 | raw file
  1/** 
  2 * @file math.cpp
  3 * @author Phoenix
  4 * @date 2005-09-26
  5 * @brief Tests for the llmath library.
  6 *
  7 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#include "linden_common.h"
 30#include "../test/lltut.h"
 31
 32#include "llcrc.h"
 33#include "llrand.h"
 34#include "lluuid.h"
 35
 36#include "../llline.h"
 37#include "../llmath.h"
 38#include "../llsphere.h"
 39#include "../v3math.h"
 40
 41namespace tut
 42{
 43	struct math_data
 44	{
 45	};
 46	typedef test_group<math_data> math_test;
 47	typedef math_test::object math_object;
 48	tut::math_test tm("BasicLindenMath");
 49
 50	template<> template<>
 51	void math_object::test<1>()
 52	{
 53		S32 val = 89543;
 54		val = llabs(val);
 55		ensure("integer absolute value 1", (89543 == val));
 56		val = -500;
 57		val = llabs(val);
 58		ensure("integer absolute value 2", (500 == val));
 59	}
 60
 61	template<> template<>
 62	void math_object::test<2>()
 63	{
 64		F32 val = -2583.4f;
 65		val = llabs(val);
 66		ensure("float absolute value 1", (2583.4f == val));
 67		val = 430903.f;
 68		val = llabs(val);
 69		ensure("float absolute value 2", (430903.f == val));
 70	}
 71
 72	template<> template<>
 73	void math_object::test<3>()
 74	{
 75		F64 val = 387439393.987329839;
 76		val = llabs(val);
 77		ensure("double absolute value 1", (387439393.987329839 == val));
 78		val = -8937843.9394878;
 79		val = llabs(val);
 80		ensure("double absolute value 2", (8937843.9394878 == val));
 81	}
 82
 83	template<> template<>
 84	void math_object::test<4>()
 85	{
 86		F32 val = 430903.9f;
 87		S32 val1 = lltrunc(val);
 88		ensure("float truncate value 1", (430903 == val1));
 89		val = -2303.9f;
 90		val1 = lltrunc(val);
 91		ensure("float truncate value 2", (-2303  == val1));
 92	}
 93
 94	template<> template<>
 95	void math_object::test<5>()
 96	{
 97		F64 val = 387439393.987329839 ;
 98		S32 val1 = lltrunc(val);
 99		ensure("float truncate value 1", (387439393 == val1));
100		val = -387439393.987329839;
101		val1 = lltrunc(val);
102		ensure("float truncate value 2", (-387439393  == val1));
103	}
104
105	template<> template<>
106	void math_object::test<6>()
107	{
108		F32 val = 430903.2f;
109		S32 val1 = llfloor(val);
110		ensure("float llfloor value 1", (430903 == val1));
111		val = -430903.9f;
112		val1 = llfloor(val);
113		ensure("float llfloor value 2", (-430904 == val1));
114	}
115
116	template<> template<>
117	void math_object::test<7>()
118	{
119		F32 val = 430903.2f;
120		S32 val1 = llceil(val);
121		ensure("float llceil value 1", (430904 == val1));
122		val = -430903.9f;
123		val1 = llceil(val);
124		ensure("float llceil value 2", (-430903 == val1));
125	}
126
127	template<> template<>
128	void math_object::test<8>()
129	{
130		F32 val = 430903.2f;
131		S32 val1 = llround(val);
132		ensure("float llround value 1", (430903 == val1));
133		val = -430903.9f;
134		val1 = llround(val);
135		ensure("float llround value 2", (-430904 == val1));
136	}
137
138	template<> template<>
139	void math_object::test<9>()
140	{
141		F32 val = 430905.2654f, nearest = 100.f;
142		val = llround(val, nearest);
143		ensure("float llround value 1", (430900 == val));
144		val = -430905.2654f, nearest = 10.f;
145		val = llround(val, nearest);
146		ensure("float llround value 1", (-430910 == val));
147	}
148
149	template<> template<>
150	void math_object::test<10>()
151	{
152		F64 val = 430905.2654, nearest = 100.0;
153		val = llround(val, nearest);
154		ensure("double llround value 1", (430900 == val));
155		val = -430905.2654, nearest = 10.0;
156		val = llround(val, nearest);
157		ensure("double llround value 1", (-430910.00000 == val));
158	}
159
160	template<> template<>
161	void math_object::test<11>()
162	{
163		const F32	F_PI		= 3.1415926535897932384626433832795f;
164		F32 angle = 3506.f;
165		angle =  llsimple_angle(angle);
166		ensure("llsimple_angle  value 1", (angle <=F_PI && angle >= -F_PI));
167		angle = -431.f;
168		angle =  llsimple_angle(angle);
169		ensure("llsimple_angle  value 1", (angle <=F_PI && angle >= -F_PI));
170	}
171}
172
173namespace tut
174{
175	struct uuid_data
176	{
177		LLUUID id;
178	};
179	typedef test_group<uuid_data> uuid_test;
180	typedef uuid_test::object uuid_object;
181	tut::uuid_test tu("LLUUID");
182
183	template<> template<>
184	void uuid_object::test<1>()
185	{
186		ensure("uuid null", id.isNull());
187		id.generate();
188		ensure("generate not null", id.notNull());
189		id.setNull();
190		ensure("set null", id.isNull());
191	}
192
193	template<> template<>
194	void uuid_object::test<2>()
195	{
196		id.generate();
197		LLUUID a(id);
198		ensure_equals("copy equal", id, a);
199		a.generate();
200		ensure_not_equals("generate not equal", id, a);
201		a = id;
202		ensure_equals("assignment equal", id, a);
203	}
204
205	template<> template<>
206	void uuid_object::test<3>()
207	{
208		id.generate();
209		LLUUID copy(id);
210		LLUUID mask;
211		mask.generate();
212		copy ^= mask;
213		ensure_not_equals("mask not equal", id, copy);
214		copy ^= mask;
215		ensure_equals("mask back", id, copy);
216	}
217
218	template<> template<>
219	void uuid_object::test<4>()
220	{
221		id.generate();
222		std::string id_str = id.asString();
223		LLUUID copy(id_str.c_str());
224		ensure_equals("string serialization", id, copy);
225	}
226	
227}
228
229namespace tut
230{
231	struct crc_data
232	{
233	};
234	typedef test_group<crc_data> crc_test;
235	typedef crc_test::object crc_object;
236	tut::crc_test tc("LLCrc");
237
238	template<> template<>
239	void crc_object::test<1>()
240	{
241		/* Test buffer update and individual char update */
242		const char TEST_BUFFER[] = "hello &#$)$&Nd0";
243		LLCRC c1, c2;
244		c1.update((U8*)TEST_BUFFER, sizeof(TEST_BUFFER) - 1);
245		char* rh = (char*)TEST_BUFFER;
246		while(*rh != '\0')
247		{
248			c2.update(*rh);
249			++rh;
250		}
251		ensure_equals("crc update 1", c1.getCRC(), c2.getCRC());
252	}
253
254	template<> template<>
255	void crc_object::test<2>()
256	{
257		/* Test mixing of buffer and individual char update */
258		const char TEST_BUFFER1[] = "Split Buffer one $^%$%#@$";
259		const char TEST_BUFFER2[] = "Split Buffer two )(8723#5dsds";
260		LLCRC c1, c2;
261		c1.update((U8*)TEST_BUFFER1, sizeof(TEST_BUFFER1) - 1);
262		char* rh = (char*)TEST_BUFFER2;
263		while(*rh != '\0')
264		{
265			c1.update(*rh);
266			++rh;
267		}
268
269		rh = (char*)TEST_BUFFER1;
270		while(*rh != '\0')
271		{
272			c2.update(*rh);
273			++rh;
274		}
275		c2.update((U8*)TEST_BUFFER2, sizeof(TEST_BUFFER2) - 1);
276
277		ensure_equals("crc update 2", c1.getCRC(), c2.getCRC());
278	}
279}
280
281namespace tut
282{
283	struct sphere_data
284	{
285	};
286	typedef test_group<sphere_data> sphere_test;
287	typedef sphere_test::object sphere_object;
288	tut::sphere_test tsphere("LLSphere");
289
290	template<> template<>
291	void sphere_object::test<1>()
292	{
293		// test LLSphere::contains() and ::overlaps()
294		S32 number_of_tests = 10;
295		for (S32 test = 0; test < number_of_tests; ++test)
296		{
297			LLVector3 first_center(1.f, 1.f, 1.f);
298			F32 first_radius = 3.f;
299			LLSphere first_sphere( first_center, first_radius );
300	
301			F32 half_millimeter = 0.0005f;
302			LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
303			direction.normalize();
304	
305			F32 distance = ll_frand(first_radius - 2.f * half_millimeter);
306			LLVector3 second_center = first_center + distance * direction;
307			F32 second_radius = first_radius - distance - half_millimeter;
308			LLSphere second_sphere( second_center, second_radius );
309			ensure("first sphere should contain the second", first_sphere.contains(second_sphere));
310			ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere));
311	
312			distance = first_radius + ll_frand(first_radius);
313			second_center = first_center + distance * direction;
314			second_radius = distance - first_radius + half_millimeter;
315			second_sphere.set( second_center, second_radius );
316			ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere));
317			ensure("first sphere should overlap the second", first_sphere.overlaps(second_sphere));
318	
319			distance = first_radius + ll_frand(first_radius) + half_millimeter;
320			second_center = first_center + distance * direction;
321			second_radius = distance - first_radius - half_millimeter;
322			second_sphere.set( second_center, second_radius );
323			ensure("first sphere should NOT contain the second", !first_sphere.contains(second_sphere));
324			ensure("first sphere should NOT overlap the second", !first_sphere.overlaps(second_sphere));
325		}
326	}
327
328	template<> template<>
329	void sphere_object::test<2>()
330	{
331		skip("See SNOW-620.  Neither the test nor the code being tested seem good.  Also sim-only.");
332
333		// test LLSphere::getBoundingSphere()
334		S32 number_of_tests = 100;
335		S32 number_of_spheres = 10;
336		F32 sphere_center_range = 32.f;
337		F32 sphere_radius_range = 5.f;
338
339		for (S32 test = 0; test < number_of_tests; ++test)
340		{
341			// gegnerate a bunch of random sphere
342			std::vector< LLSphere > sphere_list;
343			for (S32 sphere_count=0; sphere_count < number_of_spheres; ++sphere_count)
344			{
345				LLVector3 direction( ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
346				direction.normalize();
347				F32 distance = ll_frand(sphere_center_range);
348				LLVector3 center = distance * direction;
349				F32 radius = ll_frand(sphere_radius_range);
350				LLSphere sphere( center, radius );
351				sphere_list.push_back(sphere);
352			}
353
354			// compute the bounding sphere
355			LLSphere bounding_sphere = LLSphere::getBoundingSphere(sphere_list);
356
357			// make sure all spheres are inside the bounding sphere
358			{
359				std::vector< LLSphere >::const_iterator sphere_itr;
360				for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
361				{
362					ensure("sphere should be contained by the bounding sphere", bounding_sphere.contains(*sphere_itr));
363				}
364			}
365
366			// TODO -- improve LLSphere::getBoundingSphere() to the point where
367			// we can reduce the 'expansion' in the two tests below to about 
368			// 2 mm or less
369
370			F32 expansion = 0.005f;
371			// move all spheres out a little bit 
372			// and count how many are NOT contained
373			{
374				std::vector< LLVector3 > uncontained_directions;
375				std::vector< LLSphere >::iterator sphere_itr;
376				for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
377				{
378					LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter();
379					direction.normalize();
380	
381					sphere_itr->setCenter( sphere_itr->getCenter() + expansion * direction );
382					if (! bounding_sphere.contains( *sphere_itr ) )
383					{
384						uncontained_directions.push_back(direction);
385					}
386				}
387				ensure("when moving spheres out there should be at least two uncontained spheres", 
388						uncontained_directions.size() > 1);
389
390				/* TODO -- when the bounding sphere algorithm is improved we can open up this test
391				 * at the moment it occasionally fails when the sphere collection is tight and small
392				 * (2 meters or less)
393				if (2 == uncontained_directions.size() )
394				{
395					// if there were only two uncontained spheres then
396					// the two directions should be nearly opposite
397					F32 dir_dot = uncontained_directions[0] * uncontained_directions[1];
398					ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f);
399				}
400				*/
401			}
402
403			// compute the new bounding sphere
404			bounding_sphere = LLSphere::getBoundingSphere(sphere_list);
405
406			// increase the size of all spheres a little bit
407			// and count how many are NOT contained
408			{
409				std::vector< LLVector3 > uncontained_directions;
410				std::vector< LLSphere >::iterator sphere_itr;
411				for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
412				{
413					LLVector3 direction = sphere_itr->getCenter() - bounding_sphere.getCenter();
414					direction.normalize();
415	
416					sphere_itr->setRadius( sphere_itr->getRadius() + expansion );
417					if (! bounding_sphere.contains( *sphere_itr ) )
418					{
419						uncontained_directions.push_back(direction);
420					}
421				}
422				ensure("when boosting sphere radii there should be at least two uncontained spheres", 
423						uncontained_directions.size() > 1);
424
425				/* TODO -- when the bounding sphere algorithm is improved we can open up this test
426				 * at the moment it occasionally fails when the sphere collection is tight and small
427				 * (2 meters or less)
428				if (2 == uncontained_directions.size() )
429				{
430					// if there were only two uncontained spheres then
431					// the two directions should be nearly opposite
432					F32 dir_dot = uncontained_directions[0] * uncontained_directions[1];
433					ensure("two uncontained spheres should lie opposite the bounding center", dir_dot < -0.95f);
434				}
435				*/
436			}
437		}
438	}
439}
440
441namespace tut
442{
443	F32 SMALL_RADIUS = 1.0f;
444	F32 MEDIUM_RADIUS = 5.0f;
445	F32 LARGE_RADIUS = 10.0f;
446
447	struct line_data
448	{
449	};
450	typedef test_group<line_data> line_test;
451	typedef line_test::object line_object;
452	tut::line_test tline("LLLine");
453
454	template<> template<>
455	void line_object::test<1>()
456	{
457		// this is a test for LLLine::intersects(point) which returns TRUE 
458		// if the line passes within some tolerance of point
459
460		// these tests will have some floating point error, 
461		// so we need to specify how much error is ok
462		F32 allowable_relative_error = 0.00001f;
463		S32 number_of_tests = 100;
464		for (S32 test = 0; test < number_of_tests; ++test)
465		{
466			// generate some random point to be on the line
467			LLVector3 point_on_line( ll_frand(2.f) - 1.f, 
468			   						 ll_frand(2.f) - 1.f, 
469			   						 ll_frand(2.f) - 1.f);
470			point_on_line.normalize();
471			point_on_line *= ll_frand(LARGE_RADIUS);
472
473			// generate some random point to "intersect"
474			LLVector3 random_direction ( ll_frand(2.f) - 1.f, 
475			   							 ll_frand(2.f) - 1.f, 
476			   							 ll_frand(2.f) - 1.f);
477			random_direction.normalize();
478
479			LLVector3 random_offset( ll_frand(2.f) - 1.f, 
480			   						 ll_frand(2.f) - 1.f, 
481			   						 ll_frand(2.f) - 1.f);
482			random_offset.normalize();
483			random_offset *= ll_frand(SMALL_RADIUS);
484
485			LLVector3 point = point_on_line + MEDIUM_RADIUS * random_direction
486				+ random_offset;
487	
488			// compute the axis of approach (a unit vector between the points)
489			LLVector3 axis_of_approach = point - point_on_line;
490			axis_of_approach.normalize();
491	
492			// compute the direction of the the first line (perp to axis_of_approach)
493			LLVector3 first_dir( ll_frand(2.f) - 1.f, 
494			   					 ll_frand(2.f) - 1.f, 
495			   					 ll_frand(2.f) - 1.f);
496			first_dir.normalize();
497			F32 dot = first_dir * axis_of_approach;		
498			first_dir -= dot * axis_of_approach;	// subtract component parallel to axis
499			first_dir.normalize();
500	
501			// construct the line
502			LLVector3 another_point_on_line = point_on_line + ll_frand(LARGE_RADIUS) * first_dir;
503			LLLine line(another_point_on_line, point_on_line);
504	
505			// test that the intersection point is within MEDIUM_RADIUS + SMALL_RADIUS
506			F32 test_radius = MEDIUM_RADIUS + SMALL_RADIUS;
507			test_radius += (LARGE_RADIUS * allowable_relative_error);
508			ensure("line should pass near intersection point", line.intersects(point, test_radius));
509
510			test_radius = allowable_relative_error * (point - point_on_line).length();
511			ensure("line should intersect point used to define it", line.intersects(point_on_line, test_radius));
512		}
513	}
514
515	template<> template<>
516	void line_object::test<2>()
517	{
518          /*
519            These tests fail intermittently on all platforms - see DEV-16600
520            Commenting this out until dev has time to investigate.
521            
522		// this is a test for LLLine::nearestApproach(LLLIne) method
523		// which computes the point on a line nearest another line
524
525		// these tests will have some floating point error, 
526		// so we need to specify how much error is ok
527		// TODO -- make nearestApproach() algorithm more accurate so
528		// we can tighten the allowable_error.  Most tests are tighter
529		// than one milimeter, however when doing randomized testing
530		// you can walk into inaccurate cases.
531		F32 allowable_relative_error = 0.001f;
532		S32 number_of_tests = 100;
533		for (S32 test = 0; test < number_of_tests; ++test)
534		{
535			// generate two points to be our known nearest approaches
536			LLVector3 some_point( ll_frand(2.f) - 1.f, 
537			   					  ll_frand(2.f) - 1.f, 
538			   					  ll_frand(2.f) - 1.f);
539			some_point.normalize();
540			some_point *= ll_frand(LARGE_RADIUS);
541
542			LLVector3 another_point( ll_frand(2.f) - 1.f, 
543			   						 ll_frand(2.f) - 1.f, 
544			   						 ll_frand(2.f) - 1.f);
545			another_point.normalize();
546			another_point *= ll_frand(LARGE_RADIUS);
547
548			// compute the axis of approach (a unit vector between the points)
549			LLVector3 axis_of_approach = another_point - some_point;
550			axis_of_approach.normalize();
551	
552			// compute the direction of the the first line (perp to axis_of_approach)
553			LLVector3 first_dir( ll_frand(2.f) - 1.f, 
554			   					 ll_frand(2.f) - 1.f, 
555			   					 ll_frand(2.f) - 1.f);
556			F32 dot = first_dir * axis_of_approach;		
557			first_dir -= dot * axis_of_approach;		// subtract component parallel to axis
558			first_dir.normalize();						// normalize
559	
560			// compute the direction of the the second line
561			LLVector3 second_dir( ll_frand(2.f) - 1.f, 
562			   					  ll_frand(2.f) - 1.f, 
563			   					  ll_frand(2.f) - 1.f);
564			dot = second_dir * axis_of_approach;		
565			second_dir -= dot * axis_of_approach;
566			second_dir.normalize();
567
568			// make sure the lines aren't too parallel, 
569			dot = fabsf(first_dir * second_dir);
570			if (dot > 0.99f)
571			{
572				// skip this test, we're not interested in testing 
573				// the intractible cases
574				continue;
575			}
576	
577			// construct the lines
578			LLVector3 first_point = some_point + ll_frand(LARGE_RADIUS) * first_dir;
579			LLLine first_line(first_point, some_point);
580	
581			LLVector3 second_point = another_point + ll_frand(LARGE_RADIUS) * second_dir;
582			LLLine second_line(second_point, another_point);
583	
584			// compute the points of nearest approach
585			LLVector3 some_computed_point = first_line.nearestApproach(second_line);
586			LLVector3 another_computed_point = second_line.nearestApproach(first_line);
587	
588			// compute the error
589			F32 first_error = (some_point - some_computed_point).length();
590			F32 scale = llmax((some_point - another_point).length(), some_point.length());
591			scale = llmax(scale, another_point.length());
592			scale = llmax(scale, 1.f);
593			F32 first_relative_error = first_error / scale;
594
595			F32 second_error = (another_point - another_computed_point).length();
596			F32 second_relative_error = second_error / scale;
597
598			//if (first_relative_error > allowable_relative_error)
599			//{
600			//	std::cout << "first_error = " << first_error 
601			//		<< "  first_relative_error = " << first_relative_error 
602			//		<< "  scale = " << scale 
603			//		<< "  dir_dot = " << (first_dir * second_dir)
604			//		<< std::endl;
605			//}
606			//if (second_relative_error > allowable_relative_error)
607			//{
608			//	std::cout << "second_error = " << second_error 
609			//		<< "  second_relative_error = " << second_relative_error 
610			//		<< "  scale = " << scale 
611			//		<< "  dist = " << (some_point - another_point).length()
612			//		<< "  dir_dot = " << (first_dir * second_dir)
613			//		<< std::endl;
614			//}
615
616			// test that the errors are small
617
618			ensure("first line should accurately compute its closest approach", 
619					first_relative_error <= allowable_relative_error);
620			ensure("second line should accurately compute its closest approach", 
621					second_relative_error <= allowable_relative_error);
622		}
623          */
624	}
625
626	F32 ALMOST_PARALLEL = 0.99f;
627	template<> template<>
628	void line_object::test<3>()
629	{
630		// this is a test for LLLine::getIntersectionBetweenTwoPlanes() method
631
632		// first some known tests
633		LLLine xy_plane(LLVector3(0.f, 0.f, 2.f), LLVector3(0.f, 0.f, 3.f));
634		LLLine yz_plane(LLVector3(2.f, 0.f, 0.f), LLVector3(3.f, 0.f, 0.f));
635		LLLine zx_plane(LLVector3(0.f, 2.f, 0.f), LLVector3(0.f, 3.f, 0.f));
636
637		LLLine x_line;
638		LLLine y_line;
639		LLLine z_line;
640
641		bool x_success = LLLine::getIntersectionBetweenTwoPlanes(x_line, xy_plane, zx_plane);
642		bool y_success = LLLine::getIntersectionBetweenTwoPlanes(y_line, yz_plane, xy_plane);
643		bool z_success = LLLine::getIntersectionBetweenTwoPlanes(z_line, zx_plane, yz_plane);
644
645		ensure("xy and zx planes should intersect", x_success);
646		ensure("yz and xy planes should intersect", y_success);
647		ensure("zx and yz planes should intersect", z_success);
648
649		LLVector3 direction = x_line.getDirection();
650		ensure("x_line should be parallel to x_axis", fabs(direction.mV[VX]) == 1.f
651				                                      && 0.f == direction.mV[VY]
652				                                      && 0.f == direction.mV[VZ] );
653		direction = y_line.getDirection();
654		ensure("y_line should be parallel to y_axis", 0.f == direction.mV[VX]
655													  && fabs(direction.mV[VY]) == 1.f
656				                                      && 0.f == direction.mV[VZ] );
657		direction = z_line.getDirection();
658		ensure("z_line should be parallel to z_axis", 0.f == direction.mV[VX]
659				                                      && 0.f == direction.mV[VY]
660													  && fabs(direction.mV[VZ]) == 1.f );
661
662		// next some random tests
663		F32 allowable_relative_error = 0.0001f;
664		S32 number_of_tests = 20;
665		for (S32 test = 0; test < number_of_tests; ++test)
666		{
667			// generate the known line
668			LLVector3 some_point( ll_frand(2.f) - 1.f, 
669			   					  ll_frand(2.f) - 1.f, 
670			   					  ll_frand(2.f) - 1.f);
671			some_point.normalize();
672			some_point *= ll_frand(LARGE_RADIUS);
673			LLVector3 another_point( ll_frand(2.f) - 1.f, 
674			   						 ll_frand(2.f) - 1.f, 
675			   						 ll_frand(2.f) - 1.f);
676			another_point.normalize();
677			another_point *= ll_frand(LARGE_RADIUS);
678			LLLine known_intersection(some_point, another_point);
679
680			// compute a plane that intersect the line
681			LLVector3 point_on_plane( ll_frand(2.f) - 1.f, 
682			   						  ll_frand(2.f) - 1.f, 
683			   						  ll_frand(2.f) - 1.f);
684			point_on_plane.normalize();
685			point_on_plane *= ll_frand(LARGE_RADIUS);
686			LLVector3 plane_normal = (point_on_plane - some_point) % known_intersection.getDirection();
687			plane_normal.normalize();
688			LLLine first_plane(point_on_plane, point_on_plane + plane_normal);
689
690			// compute a different plane that intersect the line
691			LLVector3 point_on_different_plane( ll_frand(2.f) - 1.f, 
692			   									ll_frand(2.f) - 1.f, 
693			   									ll_frand(2.f) - 1.f);
694			point_on_different_plane.normalize();
695			point_on_different_plane *= ll_frand(LARGE_RADIUS);
696			LLVector3 different_plane_normal = (point_on_different_plane - another_point) % known_intersection.getDirection();
697			different_plane_normal.normalize();
698			LLLine second_plane(point_on_different_plane, point_on_different_plane + different_plane_normal);
699
700			if (fabs(plane_normal * different_plane_normal) > ALMOST_PARALLEL)
701			{
702				// the two planes are approximately parallel, so we won't test this case
703				continue;
704			}
705
706			LLLine measured_intersection;
707			bool success = LLLine::getIntersectionBetweenTwoPlanes(
708					measured_intersection,
709					first_plane,
710					second_plane);
711
712			ensure("plane intersection should succeed", success);
713
714			F32 dot = fabs(known_intersection.getDirection() * measured_intersection.getDirection());
715			ensure("measured intersection should be parallel to known intersection",
716					dot > ALMOST_PARALLEL);
717
718			ensure("measured intersection should pass near known point",
719					measured_intersection.intersects(some_point, LARGE_RADIUS * allowable_relative_error));
720		}
721	}
722}
723