aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmath/llsphere.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/llmath/llsphere.cpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/linden/indra/llmath/llsphere.cpp b/linden/indra/llmath/llsphere.cpp
new file mode 100644
index 0000000..62f6e27
--- /dev/null
+++ b/linden/indra/llmath/llsphere.cpp
@@ -0,0 +1,364 @@
1/**
2 * @file llsphere.cpp
3 * @author Andrew Meadows
4 * @brief Simple line class that can compute nearest approach between two lines
5 *
6 * $LicenseInfo:firstyear=2006&license=internal$
7 *
8 * Copyright (c) 2006-2008, Linden Research, Inc.
9 *
10 * The following source code is PROPRIETARY AND CONFIDENTIAL. Use of
11 * this source code is governed by the Linden Lab Source Code Disclosure
12 * Agreement ("Agreement") previously entered between you and Linden
13 * Lab. By accessing, using, copying, modifying or distributing this
14 * software, you acknowledge that you have been informed of your
15 * obligations under the Agreement and agree to abide by those obligations.
16 *
17 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
18 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
19 * COMPLETENESS OR PERFORMANCE.
20 * $/LicenseInfo$
21 */
22
23#include "llsphere.h"
24
25LLSphere::LLSphere()
26: mCenter(0.f, 0.f, 0.f),
27 mRadius(0.f)
28{ }
29
30LLSphere::LLSphere( const LLVector3& center, F32 radius)
31{
32 set(center, radius);
33}
34
35void LLSphere::set( const LLVector3& center, F32 radius )
36{
37 mCenter = center;
38 setRadius(radius);
39}
40
41void LLSphere::setCenter( const LLVector3& center)
42{
43 mCenter = center;
44}
45
46void LLSphere::setRadius( F32 radius)
47{
48 if (radius < 0.f)
49 {
50 radius = -radius;
51 }
52 mRadius = radius;
53}
54
55const LLVector3& LLSphere::getCenter() const
56{
57 return mCenter;
58}
59
60F32 LLSphere::getRadius() const
61{
62 return mRadius;
63}
64
65// returns 'TRUE' if this sphere completely contains other_sphere
66BOOL LLSphere::contains(const LLSphere& other_sphere) const
67{
68 F32 separation = (mCenter - other_sphere.mCenter).length();
69 return (mRadius >= separation + other_sphere.mRadius) ? TRUE : FALSE;
70}
71
72// returns 'TRUE' if this sphere completely contains other_sphere
73BOOL LLSphere::overlaps(const LLSphere& other_sphere) const
74{
75 F32 separation = (mCenter - other_sphere.mCenter).length();
76 return (separation <= mRadius + other_sphere.mRadius) ? TRUE : FALSE;
77}
78
79// returns overlap
80// negative overlap is closest approach
81F32 LLSphere::getOverlap(const LLSphere& other_sphere) const
82{
83 // separation is distance from other_sphere's edge and this center
84 return (mCenter - other_sphere.mCenter).length() - mRadius - other_sphere.mRadius;
85}
86
87bool LLSphere::operator==(const LLSphere& rhs) const
88{
89 // TODO? -- use approximate equality for centers?
90 return (mRadius == rhs.mRadius
91 && mCenter == rhs.mCenter);
92}
93
94std::ostream& operator<<( std::ostream& output_stream, const LLSphere& sphere)
95{
96 output_stream << "{center=" << sphere.mCenter << "," << "radius=" << sphere.mRadius << "}";
97 return output_stream;
98}
99
100// static
101// removes any spheres that are contained in others
102void LLSphere::collapse(std::vector<LLSphere>& sphere_list)
103{
104 std::vector<LLSphere>::iterator first_itr = sphere_list.begin();
105 while (first_itr != sphere_list.end())
106 {
107 bool delete_from_front = false;
108
109 std::vector<LLSphere>::iterator second_itr = first_itr;
110 ++second_itr;
111 while (second_itr != sphere_list.end())
112 {
113 if (second_itr->contains(*first_itr))
114 {
115 delete_from_front = true;
116 break;
117 }
118 else if (first_itr->contains(*second_itr))
119 {
120 sphere_list.erase(second_itr++);
121 }
122 else
123 {
124 ++second_itr;
125 }
126 }
127
128 if (delete_from_front)
129 {
130 sphere_list.erase(first_itr++);
131 }
132 else
133 {
134 ++first_itr;
135 }
136 }
137}
138
139// static
140// returns the bounding sphere that contains both spheres
141LLSphere LLSphere::getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere)
142{
143 LLVector3 direction = second_sphere.mCenter - first_sphere.mCenter;
144
145 // HACK -- it is possible to get enough floating point error in the
146 // other getBoundingSphere() method that we have to add some slop
147 // at the end. Unfortunately, this breaks the link-order invarience
148 // for the linkability tests... unless we also apply the same slop
149 // here.
150 F32 half_milimeter = 0.0005f;
151
152 F32 distance = direction.length();
153 if (0.f == distance)
154 {
155 direction.setVec(1.f, 0.f, 0.f);
156 }
157 else
158 {
159 direction.normVec();
160 }
161 // the 'edge' is measured from the first_sphere's center
162 F32 max_edge = 0.f;
163 F32 min_edge = 0.f;
164
165 max_edge = llmax(max_edge + first_sphere.getRadius(), max_edge + distance + second_sphere.getRadius() + half_milimeter);
166 min_edge = llmin(min_edge - first_sphere.getRadius(), min_edge + distance - second_sphere.getRadius() - half_milimeter);
167 F32 radius = 0.5f * (max_edge - min_edge);
168 LLVector3 center = first_sphere.mCenter + (0.5f * (max_edge + min_edge)) * direction;
169 return LLSphere(center, radius);
170}
171
172// static
173// returns the bounding sphere that contains an arbitrary set of spheres
174LLSphere LLSphere::getBoundingSphere(const std::vector<LLSphere>& sphere_list)
175{
176 // this algorithm can get relatively inaccurate when the sphere
177 // collection is 'small' (contained within a bounding sphere of about
178 // 2 meters or less)
179 // TODO -- improve the accuracy for small collections of spheres
180
181 LLSphere bounding_sphere( LLVector3(0.f, 0.f, 0.f), 0.f );
182 S32 sphere_count = sphere_list.size();
183 if (1 == sphere_count)
184 {
185 // trivial case -- single sphere
186 std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
187 bounding_sphere = *sphere_itr;
188 }
189 else if (2 == sphere_count)
190 {
191 // trivial case -- two spheres
192 std::vector<LLSphere>::const_iterator first_sphere = sphere_list.begin();
193 std::vector<LLSphere>::const_iterator second_sphere = first_sphere;
194 ++second_sphere;
195 bounding_sphere = LLSphere::getBoundingSphere(*first_sphere, *second_sphere);
196 }
197 else if (sphere_count > 0)
198 {
199 // non-trivial case -- we will approximate the solution
200 //
201 // NOTE -- there is a fancy/fast way to do this for large
202 // numbers of arbirary N-dimensional spheres -- you can look it
203 // up on the net. We're dealing with 3D spheres at collection
204 // sizes of 256 spheres or smaller, so we just use this
205 // brute force method.
206
207 // TODO -- perhaps would be worthwile to test for the solution where
208 // the largest spanning radius just happens to work. That is, where
209 // there are really two spheres that determine the bounding sphere,
210 // and all others are contained therein.
211
212 // compute the AABB
213 std::vector<LLSphere>::const_iterator first_itr = sphere_list.begin();
214 LLVector3 max_corner = first_itr->getCenter() + first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
215 LLVector3 min_corner = first_itr->getCenter() - first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
216 {
217 std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
218 for (++sphere_itr; sphere_itr != sphere_list.end(); ++sphere_itr)
219 {
220 LLVector3 center = sphere_itr->getCenter();
221 F32 radius = sphere_itr->getRadius();
222 for (S32 i=0; i<3; ++i)
223 {
224 if (center.mV[i] + radius > max_corner.mV[i])
225 {
226 max_corner.mV[i] = center.mV[i] + radius;
227 }
228 if (center.mV[i] - radius < min_corner.mV[i])
229 {
230 min_corner.mV[i] = center.mV[i] - radius;
231 }
232 }
233 }
234 }
235
236 // get the starting center and radius from the AABB
237 LLVector3 diagonal = max_corner - min_corner;
238 F32 bounding_radius = 0.5f * diagonal.length();
239 LLVector3 bounding_center = 0.5f * (max_corner + min_corner);
240
241 // compute the starting step-size
242 F32 minimum_radius = 0.5f * llmin(diagonal.mV[VX], llmin(diagonal.mV[VY], diagonal.mV[VZ]));
243 F32 step_length = bounding_radius - minimum_radius;
244 S32 step_count = 0;
245 S32 max_step_count = 12;
246 F32 half_milimeter = 0.0005f;
247
248 // wander the center around in search of tighter solutions
249 S32 last_dx = 2; // 2 is out of bounds --> no match
250 S32 last_dy = 2;
251 S32 last_dz = 2;
252
253 while (step_length > half_milimeter
254 && step_count < max_step_count)
255 {
256 // the algorithm for testing the maximum radius could be expensive enough
257 // that it makes sense to NOT duplicate testing when possible, so we keep
258 // track of where we last tested, and only test the new points
259
260 S32 best_dx = 0;
261 S32 best_dy = 0;
262 S32 best_dz = 0;
263
264 // sample near the center of the box
265 bool found_better_center = false;
266 for (S32 dx = -1; dx < 2; ++dx)
267 {
268 for (S32 dy = -1; dy < 2; ++dy)
269 {
270 for (S32 dz = -1; dz < 2; ++dz)
271 {
272 if (dx == 0 && dy == 0 && dz == 0)
273 {
274 continue;
275 }
276
277 // count the number of indecies that match the last_*'s
278 S32 match_count = 0;
279 if (last_dx == dx) ++match_count;
280 if (last_dy == dy) ++match_count;
281 if (last_dz == dz) ++match_count;
282 if (match_count == 2)
283 {
284 // we've already tested this point
285 continue;
286 }
287
288 LLVector3 center = bounding_center;
289 center.mV[VX] += (F32) dx * step_length;
290 center.mV[VY] += (F32) dy * step_length;
291 center.mV[VZ] += (F32) dz * step_length;
292
293 // compute the radius of the bounding sphere
294 F32 max_radius = 0.f;
295 std::vector<LLSphere>::const_iterator sphere_itr;
296 for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
297 {
298 F32 radius = (sphere_itr->getCenter() - center).length() + sphere_itr->getRadius();
299 if (radius > max_radius)
300 {
301 max_radius = radius;
302 }
303 }
304 if (max_radius < bounding_radius)
305 {
306 best_dx = dx;
307 best_dy = dy;
308 best_dz = dz;
309 bounding_center = center;
310 bounding_radius = max_radius;
311 found_better_center = true;
312 }
313 }
314 }
315 }
316 if (found_better_center)
317 {
318 // remember where we came from so we can avoid retesting
319 last_dx = -best_dx;
320 last_dy = -best_dy;
321 last_dz = -best_dz;
322 }
323 else
324 {
325 // reduce the step size
326 step_length *= 0.5f;
327 //++step_count;
328 // reset the last_*'s
329 last_dx = 2; // 2 is out of bounds --> no match
330 last_dy = 2;
331 last_dz = 2;
332 }
333 }
334
335 // HACK -- it is possible to get enough floating point error for the
336 // bounding sphere to too small on the order of 10e-6, but we only need
337 // it to be accurate to within about half a millimeter
338 bounding_radius += half_milimeter;
339
340 // this algorithm can get relatively inaccurate when the sphere
341 // collection is 'small' (contained within a bounding sphere of about
342 // 2 meters or less)
343 // TODO -- fix this
344 /* debug code
345 {
346 std::vector<LLSphere>::const_iterator sphere_itr;
347 for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
348 {
349 F32 radius = (sphere_itr->getCenter() - bounding_center).length() + sphere_itr->getRadius();
350 if (radius + 0.1f > bounding_radius)
351 {
352 std::cout << " rad = " << radius << " bounding - rad = " << (bounding_radius - radius) << std::endl;
353 }
354 }
355 std::cout << "\n" << std::endl;
356 }
357 */
358
359 bounding_sphere.set(bounding_center, bounding_radius);
360 }
361 return bounding_sphere;
362}
363
364