/indra/llmath/tests/mathmisc_test.cpp

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