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