PageRenderTime 191ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/test/prim_linkability_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 495 lines | 318 code | 71 blank | 106 comment | 18 complexity | 923058a6493ccf0ed5bdca24c0af7839 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file linkability.cpp
  3. * @author andrew@lindenlab.com
  4. * @date 2007-04-23
  5. * @brief Tests for the LLPrimLinkInfo template which computes the linkability of prims
  6. *
  7. * $LicenseInfo:firstyear=2007&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 "lltut.h"
  30. #include "llprimlinkinfo.h"
  31. #include "llrand.h"
  32. // helper function
  33. void randomize_sphere(LLSphere& sphere, F32 center_range, F32 radius_range)
  34. {
  35. F32 radius = ll_frand(2.f * radius_range) - radius_range;
  36. LLVector3 center;
  37. for (S32 i=0; i<3; ++i)
  38. {
  39. center.mV[i] = ll_frand(2.f * center_range) - center_range;
  40. }
  41. sphere.setRadius(radius);
  42. sphere.setCenter(center);
  43. }
  44. // helper function. Same as above with a min and max radius.
  45. void randomize_sphere(LLSphere& sphere, F32 center_range, F32 minimum_radius, F32 maximum_radius)
  46. {
  47. F32 radius = ll_frand(maximum_radius - minimum_radius) + minimum_radius;
  48. LLVector3 center;
  49. for (S32 i=0; i<3; ++i)
  50. {
  51. center.mV[i] = ll_frand(2.f * center_range) - center_range;
  52. }
  53. sphere.setRadius(radius);
  54. sphere.setCenter(center);
  55. }
  56. // helper function
  57. bool random_sort( const LLPrimLinkInfo< S32 >&, const LLPrimLinkInfo< S32 >& b)
  58. {
  59. return (ll_rand(64) < 32);
  60. }
  61. namespace tut
  62. {
  63. struct linkable_data
  64. {
  65. LLPrimLinkInfo<S32> info;
  66. };
  67. typedef test_group<linkable_data> linkable_test;
  68. typedef linkable_test::object linkable_object;
  69. tut::linkable_test wtf("prim linkability");
  70. template<> template<>
  71. void linkable_object::test<1>()
  72. {
  73. // Here we test the boundary of the LLPrimLinkInfo::canLink() method
  74. // between semi-random middle-sized objects.
  75. S32 number_of_tests = 100;
  76. for (S32 test = 0; test < number_of_tests; ++test)
  77. {
  78. // compute the radii that would provide the above max link distance
  79. F32 first_radius = 0.f;
  80. F32 second_radius = 0.f;
  81. // compute a random center for the first sphere
  82. // compute some random max link distance
  83. F32 max_link_span = ll_frand(MAX_OBJECT_SPAN);
  84. if (max_link_span < OBJECT_SPAN_BONUS)
  85. {
  86. max_link_span += OBJECT_SPAN_BONUS;
  87. }
  88. LLVector3 first_center(
  89. ll_frand(2.f * max_link_span) - max_link_span,
  90. ll_frand(2.f * max_link_span) - max_link_span,
  91. ll_frand(2.f * max_link_span) - max_link_span);
  92. // put the second sphere at the right distance from the origin
  93. // such that it is within the max_link_distance of the first
  94. LLVector3 direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  95. direction.normalize();
  96. F32 half_milimeter = 0.0005f;
  97. LLVector3 second_center;
  98. // max_span = 3 * (first_radius + second_radius) + OBJECT_SPAN_BONUS
  99. // make sure they link at short distances
  100. {
  101. second_center = first_center + (OBJECT_SPAN_BONUS - half_milimeter) * direction;
  102. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  103. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  104. ensure("these nearby objects should link", first_info.canLink(second_info) );
  105. }
  106. // make sure they fail to link if we move them apart just a little bit
  107. {
  108. second_center = first_center + (OBJECT_SPAN_BONUS + half_milimeter) * direction;
  109. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  110. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  111. ensure("these nearby objects should NOT link", !first_info.canLink(second_info) );
  112. }
  113. // make sure the objects link or not at medium distances
  114. {
  115. first_radius = 0.3f * ll_frand(max_link_span - OBJECT_SPAN_BONUS);
  116. // This is the exact second radius that will link at exactly our random max_link_distance
  117. second_radius = ((max_link_span - OBJECT_SPAN_BONUS) / 3.f) - first_radius;
  118. second_center = first_center + (max_link_span - first_radius - second_radius - half_milimeter) * direction;
  119. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  120. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  121. ensure("these objects should link", first_info.canLink(second_info) );
  122. }
  123. // make sure they fail to link if we move them apart just a little bit
  124. {
  125. // move the second sphere such that it is a little too far from the first
  126. second_center += (2.f * half_milimeter) * direction;
  127. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  128. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  129. ensure("these objects should NOT link", !first_info.canLink(second_info) );
  130. }
  131. // make sure things don't link at far distances
  132. {
  133. second_center = first_center + (MAX_OBJECT_SPAN + 2.f * half_milimeter) * direction;
  134. second_radius = 0.3f * MAX_OBJECT_SPAN;
  135. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  136. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  137. ensure("these objects should NOT link", !first_info.canLink(second_info) );
  138. }
  139. }
  140. }
  141. template<> template<>
  142. void linkable_object::test<2>()
  143. {
  144. // Consider a row of eight spheres in a row, each 10m in diameter and centered
  145. // at 10m intervals: 01234567.
  146. F32 radius = 5.f;
  147. F32 spacing = 10.f;
  148. LLVector3 line_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  149. line_direction.normalize();
  150. LLVector3 first_center(ll_frand(2.f * spacing) -spacing, ll_frand(2.f * spacing) - spacing, ll_frand(2.f * spacing) - spacing);
  151. LLPrimLinkInfo<S32> infos[8];
  152. for (S32 index = 0; index < 8; ++index)
  153. {
  154. LLVector3 center = first_center + ((F32)(index) * spacing) * line_direction;
  155. infos[index].set(index, LLSphere(center, radius));
  156. }
  157. // Max span for 2 spheres of 5m radius is 3 * (5 + 5) + 1 = 31m
  158. // spheres 0&2 have a 30m span (from outside edge to outside edge) and should link
  159. {
  160. LLPrimLinkInfo<S32> root_info = infos[0];
  161. std::list< LLPrimLinkInfo<S32> > info_list;
  162. info_list.push_back(infos[2]);
  163. root_info.mergeLinkableSet(info_list);
  164. S32 prim_count = root_info.getPrimCount();
  165. ensure_equals("0&2 prim count should be 2", prim_count, 2);
  166. ensure_equals("0&2 unlinkable list should have length 0", (S32) info_list.size(), 0);
  167. }
  168. // spheres 0&3 have a 40 meter span and should NOT link outright
  169. {
  170. LLPrimLinkInfo<S32> root_info = infos[0];
  171. std::list< LLPrimLinkInfo<S32> > info_list;
  172. info_list.push_back(infos[3]);
  173. root_info.mergeLinkableSet(info_list);
  174. S32 prim_count = root_info.getPrimCount();
  175. ensure_equals("0&4 prim count should be 1", prim_count, 1);
  176. ensure_equals("0&4 unlinkable list should have length 1", (S32) info_list.size(), 1);
  177. }
  178. // spheres 0-4 should link no matter what order : 01234
  179. // Total span = 50m, 012 link with a r=15.5 giving max span of 3 * (15.5 + 5) + 1 = 62.5, but the cap is 54m
  180. {
  181. LLPrimLinkInfo<S32> root_info = infos[0];
  182. std::list< LLPrimLinkInfo<S32> > info_list;
  183. for (S32 index = 1; index < 5; ++index)
  184. {
  185. info_list.push_back(infos[index]);
  186. }
  187. root_info.mergeLinkableSet(info_list);
  188. S32 prim_count = root_info.getPrimCount();
  189. ensure_equals("01234 prim count should be 5", prim_count, 5);
  190. ensure_equals("01234 unlinkable list should have length 0", (S32) info_list.size(), 0);
  191. }
  192. // spheres 0-5 should link no matter what order : 04321
  193. {
  194. LLPrimLinkInfo<S32> root_info = infos[0];
  195. std::list< LLPrimLinkInfo<S32> > info_list;
  196. for (S32 index = 4; index > 0; --index)
  197. {
  198. info_list.push_back(infos[index]);
  199. }
  200. root_info.mergeLinkableSet(info_list);
  201. S32 prim_count = root_info.getPrimCount();
  202. ensure_equals("04321 prim count should be 5", prim_count, 5);
  203. ensure_equals("04321 unlinkable list should have length 0", (S32) info_list.size(), 0);
  204. }
  205. // spheres 0-4 should link no matter what order : 01423
  206. {
  207. LLPrimLinkInfo<S32> root_info = infos[0];
  208. std::list< LLPrimLinkInfo<S32> > info_list;
  209. info_list.push_back(infos[1]);
  210. info_list.push_back(infos[4]);
  211. info_list.push_back(infos[2]);
  212. info_list.push_back(infos[3]);
  213. root_info.mergeLinkableSet(info_list);
  214. S32 prim_count = root_info.getPrimCount();
  215. ensure_equals("01423 prim count should be 5", prim_count, 5);
  216. ensure_equals("01423 unlinkable list should have length 0", (S32) info_list.size(), 0);
  217. }
  218. // spheres 0-5 should NOT fully link, only 0-4
  219. {
  220. LLPrimLinkInfo<S32> root_info = infos[0];
  221. std::list< LLPrimLinkInfo<S32> > info_list;
  222. for (S32 index = 1; index < 6; ++index)
  223. {
  224. info_list.push_back(infos[index]);
  225. }
  226. root_info.mergeLinkableSet(info_list);
  227. S32 prim_count = root_info.getPrimCount();
  228. ensure_equals("012345 prim count should be 5", prim_count, 5);
  229. ensure_equals("012345 unlinkable list should have length 1", (S32) info_list.size(), 1);
  230. std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
  231. if (info_itr != info_list.end())
  232. {
  233. // examine the contents of the unlinked info
  234. std::list<S32> unlinked_indecies;
  235. info_itr->getData(unlinked_indecies);
  236. // make sure there is only one index in the unlinked_info
  237. ensure_equals("012345 unlinkable index count should be 1", (S32) unlinked_indecies.size(), 1);
  238. // make sure its value is 6
  239. std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
  240. S32 unlinkable_index = *unlinked_index_itr;
  241. ensure_equals("012345 unlinkable index should be 5", (S32) unlinkable_index, 5);
  242. }
  243. }
  244. // spheres 0-7 should NOT fully link, only 0-5
  245. {
  246. LLPrimLinkInfo<S32> root_info = infos[0];
  247. std::list< LLPrimLinkInfo<S32> > info_list;
  248. for (S32 index = 1; index < 8; ++index)
  249. {
  250. info_list.push_back(infos[index]);
  251. }
  252. root_info.mergeLinkableSet(info_list);
  253. S32 prim_count = root_info.getPrimCount();
  254. ensure_equals("01234567 prim count should be 5", prim_count, 5);
  255. // Should be 1 linkinfo on unlinkable that has 2 prims
  256. ensure_equals("01234567 unlinkable list should have length 1", (S32) info_list.size(), 1);
  257. std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
  258. if (info_itr != info_list.end())
  259. {
  260. // make sure there is only one index in the unlinked_info
  261. std::list<S32> unlinked_indecies;
  262. info_itr->getData(unlinked_indecies);
  263. ensure_equals("0123456 unlinkable index count should be 3", (S32) unlinked_indecies.size(), 3);
  264. // make sure its values are 6 and 7
  265. std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
  266. S32 unlinkable_index = *unlinked_index_itr;
  267. ensure_equals("0123456 first unlinkable index should be 5", (S32) unlinkable_index, 5);
  268. ++unlinked_index_itr;
  269. unlinkable_index = *unlinked_index_itr;
  270. ensure_equals("0123456 second unlinkable index should be 6", (S32) unlinkable_index, 6);
  271. ++unlinked_index_itr;
  272. unlinkable_index = *unlinked_index_itr;
  273. ensure_equals("0123456 third unlinkable index should be 7", (S32) unlinkable_index, 7);
  274. }
  275. }
  276. }
  277. template<> template<>
  278. void linkable_object::test<3>()
  279. {
  280. // Here we test the link results between an LLPrimLinkInfo and a set of
  281. // randomized LLPrimLinkInfos where the expected results are known.
  282. S32 number_of_tests = 5;
  283. for (S32 test = 0; test < number_of_tests; ++test)
  284. {
  285. // the radii are known
  286. F32 first_radius = 1.f;
  287. F32 second_radius = 2.f;
  288. F32 third_radius = 3.f;
  289. // compute the distances
  290. F32 half_milimeter = 0.0005f;
  291. F32 max_first_second_span = 3.f * (first_radius + second_radius) + OBJECT_SPAN_BONUS;
  292. F32 linkable_distance = max_first_second_span - first_radius - second_radius - half_milimeter;
  293. F32 max_full_span = 3.f * (0.5f * max_first_second_span + third_radius) + OBJECT_SPAN_BONUS;
  294. F32 unlinkable_distance = max_full_span - 0.5f * linkable_distance - third_radius + half_milimeter;
  295. // compute some random directions
  296. LLVector3 first_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  297. first_direction.normalize();
  298. LLVector3 second_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  299. second_direction.normalize();
  300. LLVector3 third_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  301. third_direction.normalize();
  302. // compute the centers
  303. LLVector3 first_center = ll_frand(10.f) * first_direction;
  304. LLVector3 second_center = first_center + ll_frand(linkable_distance) * second_direction;
  305. LLVector3 first_join_center = 0.5f * (first_center + second_center);
  306. LLVector3 third_center = first_join_center + unlinkable_distance * third_direction;
  307. // make sure the second info links and the third does not
  308. {
  309. // initialize the infos
  310. S32 index = 0;
  311. LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
  312. LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
  313. LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
  314. // put the second and third infos in a list
  315. std::list< LLPrimLinkInfo<S32> > info_list;
  316. info_list.push_back(second_info);
  317. info_list.push_back(third_info);
  318. // merge the list with the first_info
  319. first_info.mergeLinkableSet(info_list);
  320. S32 prim_count = first_info.getPrimCount();
  321. ensure_equals("prim count should be 2", prim_count, 2);
  322. ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
  323. }
  324. // reverse the order and make sure we get the same results
  325. {
  326. // initialize the infos
  327. S32 index = 0;
  328. LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
  329. LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
  330. LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
  331. // build the list in the reverse order
  332. std::list< LLPrimLinkInfo<S32> > info_list;
  333. info_list.push_back(third_info);
  334. info_list.push_back(second_info);
  335. // merge the list with the first_info
  336. first_info.mergeLinkableSet(info_list);
  337. S32 prim_count = first_info.getPrimCount();
  338. ensure_equals("prim count should be 2", prim_count, 2);
  339. ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
  340. }
  341. }
  342. }
  343. template<> template<>
  344. void linkable_object::test<4>()
  345. {
  346. // Here we test whether linkability is invarient under permutations
  347. // of link order. To do this we generate a bunch of random spheres
  348. // and then try to link them in different ways.
  349. //
  350. // NOTE: the linkability will only be invarient if there is only one
  351. // linkable solution. Multiple solutions will exist if the set of
  352. // candidates are larger than the maximum linkable distance, or more
  353. // numerous than a single linked object can contain. This is easily
  354. // understood by considering a very large set of link candidates,
  355. // and first linking preferentially to the left until linking fails,
  356. // then doing the same to the right -- the final solutions will differ.
  357. // Hence for this test we must generate candidate sets that lie within
  358. // the linkability envelope of a single object.
  359. //
  360. // NOTE: a random set of objects will tend to either be totally linkable
  361. // or totally not. That is, the random orientations that
  362. F32 root_center_range = 0.f;
  363. F32 min_prim_radius = 0.1f;
  364. F32 max_prim_radius = 2.f;
  365. // Linkability is min(MAX_OBJECT_SPAN,3 *( R1 + R2 ) + BONUS)
  366. // 3 * (min_prim_radius + min_prim_radius) + OBJECT_SPAN_BONUS = 6 * min_prim_radius + OBJECT_SPAN_BONUS;
  367. // Use .45 instead of .5 to gaurantee objects are within the minimum span.
  368. F32 child_center_range = 0.45f * ( (6*min_prim_radius) + OBJECT_SPAN_BONUS );
  369. S32 number_of_tests = 100;
  370. S32 number_of_spheres = 10;
  371. S32 number_of_scrambles = 10;
  372. S32 number_of_random_bubble_sorts = 10;
  373. for (S32 test = 0; test < number_of_tests; ++test)
  374. {
  375. LLSphere sphere;
  376. S32 sphere_index = 0;
  377. // build the root piece
  378. randomize_sphere(sphere, root_center_range, min_prim_radius, max_prim_radius);
  379. info.set( sphere_index++, sphere );
  380. // build the unlinked pieces
  381. std::list< LLPrimLinkInfo<S32> > info_list;
  382. for (; sphere_index < number_of_spheres; ++sphere_index)
  383. {
  384. randomize_sphere(sphere, child_center_range, min_prim_radius, max_prim_radius);
  385. LLPrimLinkInfo<S32> child_info( sphere_index, sphere );
  386. info_list.push_back(child_info);
  387. }
  388. // declare the variables used to store the results
  389. std::list<S32> first_linked_list;
  390. {
  391. // the link attempt will modify our original info's, so we
  392. // have to make copies of the originals for testing
  393. LLPrimLinkInfo<S32> test_info( 0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
  394. std::list< LLPrimLinkInfo<S32> > test_list;
  395. test_list.assign(info_list.begin(), info_list.end());
  396. // try to link
  397. test_info.mergeLinkableSet(test_list);
  398. ensure("All prims should link, but did not.",test_list.empty());
  399. // store the results
  400. test_info.getData(first_linked_list);
  401. first_linked_list.sort();
  402. }
  403. // try to link the spheres in various random orders
  404. for (S32 scramble = 0; scramble < number_of_scrambles; ++scramble)
  405. {
  406. LLPrimLinkInfo<S32> test_info(0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
  407. // scramble the order of the info_list
  408. std::list< LLPrimLinkInfo<S32> > test_list;
  409. test_list.assign(info_list.begin(), info_list.end());
  410. for (S32 i = 0; i < number_of_random_bubble_sorts; i++)
  411. {
  412. test_list.sort(random_sort);
  413. }
  414. // try to link
  415. test_info.mergeLinkableSet(test_list);
  416. // get the results
  417. std::list<S32> linked_list;
  418. test_info.getData(linked_list);
  419. linked_list.sort();
  420. ensure_equals("linked set size should be order independent",linked_list.size(),first_linked_list.size());
  421. }
  422. }
  423. }
  424. }