1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkSphere.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkSphere.h"
16 #include "vtkMath.h"
17 #include "vtkObjectFactory.h"
18 
19 #include <algorithm>
20 
21 vtkStandardNewMacro(vtkSphere);
22 
23 //------------------------------------------------------------------------------
24 // Construct sphere with center at (0,0,0) and radius=0.5.
vtkSphere()25 vtkSphere::vtkSphere()
26 {
27   this->Radius = 0.5;
28 
29   this->Center[0] = 0.0;
30   this->Center[1] = 0.0;
31   this->Center[2] = 0.0;
32 }
33 
34 //------------------------------------------------------------------------------
35 // Evaluate sphere equation ((x-x0)^2 + (y-y0)^2 + (z-z0)^2) - R^2.
EvaluateFunction(double x[3])36 double vtkSphere::EvaluateFunction(double x[3])
37 {
38   return (((x[0] - this->Center[0]) * (x[0] - this->Center[0]) +
39             (x[1] - this->Center[1]) * (x[1] - this->Center[1]) +
40             (x[2] - this->Center[2]) * (x[2] - this->Center[2])) -
41     this->Radius * this->Radius);
42 }
43 
44 //------------------------------------------------------------------------------
45 // Evaluate sphere gradient.
EvaluateGradient(double x[3],double n[3])46 void vtkSphere::EvaluateGradient(double x[3], double n[3])
47 {
48   n[0] = 2.0 * (x[0] - this->Center[0]);
49   n[1] = 2.0 * (x[1] - this->Center[1]);
50   n[2] = 2.0 * (x[2] - this->Center[2]);
51 }
52 
53 // The following methods are used to compute bounding spheres.
54 //
55 #define VTK_ASSIGN_POINT(_x, _y)                                                                   \
56   {                                                                                                \
57     _x[0] = _y[0];                                                                                 \
58     _x[1] = _y[1];                                                                                 \
59     _x[2] = _y[2];                                                                                 \
60   }
61 //------------------------------------------------------------------------------
62 // Inspired by Graphics Gems Vol. I ("An Efficient Bounding Sphere" by Jack Ritter).
63 // The algorithm works in two parts: first an initial estimate of the largest sphere;
64 // second an adjustment to the sphere to make sure that it includes all the points.
65 // Typically this returns a bounding sphere that is ~5% larger than the minimal
66 // bounding sphere.
67 template <class T>
vtkSphereComputeBoundingSphere(T * pts,vtkIdType numPts,T sphere[4],vtkIdType hints[2])68 void vtkSphereComputeBoundingSphere(T* pts, vtkIdType numPts, T sphere[4], vtkIdType hints[2])
69 {
70   sphere[0] = sphere[1] = sphere[2] = sphere[3] = 0.0;
71   if (numPts < 1)
72   {
73     return;
74   }
75 
76   vtkIdType i;
77   T *p, d1[3], d2[3];
78   if (hints)
79   {
80     p = pts + 3 * hints[0];
81     VTK_ASSIGN_POINT(d1, p);
82     p = pts + 3 * hints[1];
83     VTK_ASSIGN_POINT(d2, p);
84   }
85   else // no hints provided, compute an initial guess
86   {
87     T xMin[3], xMax[3], yMin[3], yMax[3], zMin[3], zMax[3];
88     xMin[0] = xMin[1] = xMin[2] = VTK_FLOAT_MAX;
89     yMin[0] = yMin[1] = yMin[2] = VTK_FLOAT_MAX;
90     zMin[0] = zMin[1] = zMin[2] = VTK_FLOAT_MAX;
91     xMax[0] = xMax[1] = xMax[2] = -VTK_FLOAT_MAX;
92     yMax[0] = yMax[1] = yMax[2] = -VTK_FLOAT_MAX;
93     zMax[0] = zMax[1] = zMax[2] = -VTK_FLOAT_MAX;
94 
95     // First part: Estimate the points furthest apart to define the largest sphere.
96     // Find the points that span the greatest distance on the x-y-z axes. Use these
97     // two points to define a sphere centered between the two points.
98     for (p = pts, i = 0; i < numPts; ++i, p += 3)
99     {
100       if (p[0] < xMin[0])
101         VTK_ASSIGN_POINT(xMin, p);
102       if (p[0] > xMax[0])
103         VTK_ASSIGN_POINT(xMax, p);
104       if (p[1] < yMin[1])
105         VTK_ASSIGN_POINT(yMin, p);
106       if (p[1] > yMax[1])
107         VTK_ASSIGN_POINT(yMax, p);
108       if (p[2] < zMin[2])
109         VTK_ASSIGN_POINT(zMin, p);
110       if (p[2] > zMax[2])
111         VTK_ASSIGN_POINT(zMax, p);
112     }
113     T xSpan = (xMax[0] - xMin[0]) * (xMax[0] - xMin[0]) +
114       (xMax[1] - xMin[1]) * (xMax[1] - xMin[1]) + (xMax[2] - xMin[2]) * (xMax[2] - xMin[2]);
115     T ySpan = (yMax[0] - yMin[0]) * (yMax[0] - yMin[0]) +
116       (yMax[1] - yMin[1]) * (yMax[1] - yMin[1]) + (yMax[2] - yMin[2]) * (yMax[2] - yMin[2]);
117     T zSpan = (zMax[0] - zMin[0]) * (zMax[0] - zMin[0]) +
118       (zMax[1] - zMin[1]) * (zMax[1] - zMin[1]) + (zMax[2] - zMin[2]) * (zMax[2] - zMin[2]);
119 
120     if (xSpan > ySpan)
121     {
122       if (xSpan > zSpan)
123       {
124         VTK_ASSIGN_POINT(d1, xMin);
125         VTK_ASSIGN_POINT(d2, xMax);
126       }
127       else
128       {
129         VTK_ASSIGN_POINT(d1, zMin);
130         VTK_ASSIGN_POINT(d2, zMax);
131       }
132     }
133     else // ySpan > xSpan
134     {
135       if (ySpan > zSpan)
136       {
137         VTK_ASSIGN_POINT(d1, yMin);
138         VTK_ASSIGN_POINT(d2, yMax);
139       }
140       else
141       {
142         VTK_ASSIGN_POINT(d1, zMin);
143         VTK_ASSIGN_POINT(d2, zMax);
144       }
145     }
146   } // no hints provided
147 
148   // Compute initial estimated sphere
149   sphere[0] = (d1[0] + d2[0]) / 2.0;
150   sphere[1] = (d1[1] + d2[1]) / 2.0;
151   sphere[2] = (d1[2] + d2[2]) / 2.0;
152   T r2 = vtkMath::Distance2BetweenPoints(d1, d2) / 4.0;
153   sphere[3] = sqrt(r2);
154 
155   // Second part: Make a pass over the points to make sure that they fit inside the sphere.
156   // If not, adjust the sphere to fit the point.
157   T dist, dist2, delta;
158   for (p = pts, i = 0; i < numPts; ++i, p += 3)
159   {
160     dist2 = vtkMath::Distance2BetweenPoints(p, sphere);
161     if (dist2 > r2)
162     {
163       dist = sqrt(dist2);
164       sphere[3] = (sphere[3] + dist) / 2.0;
165       r2 = sphere[3] * sphere[3];
166       delta = dist - sphere[3];
167       sphere[0] = (sphere[3] * sphere[0] + delta * p[0]) / dist;
168       sphere[1] = (sphere[3] * sphere[1] + delta * p[1]) / dist;
169       sphere[2] = (sphere[3] * sphere[2] + delta * p[2]) / dist;
170     }
171   }
172 }
173 #undef VTK_ASSIGN_POINT
174 
175 #define VTK_ASSIGN_SPHERE(_x, _y)                                                                  \
176   {                                                                                                \
177     _x[0] = _y[0];                                                                                 \
178     _x[1] = _y[1];                                                                                 \
179     _x[2] = _y[2];                                                                                 \
180     _x[3] = _y[3];                                                                                 \
181   }
182 // An approximation to the bounding sphere of a set of spheres. The algorithm
183 // creates an iniitial approximation from two spheres that are expected to be
184 // the farthest apart (taking into account their radius). A second pass may
185 // grow the bounding sphere if the remaining spheres are not contained within
186 // it. The hints[2] array indicates two spheres that are expected to be the
187 // farthest apart.
188 //------------------------------------------------------------------------------
189 template <class T>
vtkSphereComputeBoundingSphere(T ** spheres,vtkIdType numSpheres,T sphere[4],vtkIdType hints[2])190 void vtkSphereComputeBoundingSphere(
191   T** spheres, vtkIdType numSpheres, T sphere[4], vtkIdType hints[2])
192 {
193   if (numSpheres < 1)
194   {
195     sphere[0] = sphere[1] = sphere[2] = sphere[3] = 0.0;
196     return;
197   }
198   else if (numSpheres == 1)
199   {
200     VTK_ASSIGN_SPHERE(sphere, spheres[0]);
201     return;
202   }
203 
204   // Okay two or more spheres
205   vtkIdType i, j;
206   T *s, s1[4], s2[4];
207   if (hints)
208   {
209     s = spheres[hints[0]];
210     VTK_ASSIGN_SPHERE(s1, s);
211     s = spheres[hints[1]];
212     VTK_ASSIGN_SPHERE(s2, s);
213   }
214   else // no hints provided, compute an initial guess
215   {
216     T xMin[4], xMax[4], yMin[4], yMax[4], zMin[4], zMax[4];
217     xMin[0] = xMin[1] = xMin[2] = VTK_FLOAT_MAX;
218     yMin[0] = yMin[1] = yMin[2] = VTK_FLOAT_MAX;
219     zMin[0] = zMin[1] = zMin[2] = VTK_FLOAT_MAX;
220     xMax[0] = xMax[1] = xMax[2] = -VTK_FLOAT_MAX;
221     yMax[0] = yMax[1] = yMax[2] = -VTK_FLOAT_MAX;
222     zMax[0] = zMax[1] = zMax[2] = -VTK_FLOAT_MAX;
223     xMin[3] = xMax[3] = yMin[3] = yMax[3] = zMin[3] = zMax[3] = 0.0;
224 
225     // First part: Estimate the points furthest apart to define the largest sphere.
226     // Find the points that span the greatest distance on the x-y-z axes. Use these
227     // two points to define a sphere centered between the two points.
228     for (i = 0; i < numSpheres; ++i)
229     {
230       s = spheres[i];
231       if ((s[0] - s[3]) < (xMin[0] - xMin[3]))
232         VTK_ASSIGN_SPHERE(xMin, s);
233       if ((s[0] + s[3]) > (xMax[0] + xMax[3]))
234         VTK_ASSIGN_SPHERE(xMax, s);
235       if ((s[1] - s[3]) < (yMin[1] - yMin[3]))
236         VTK_ASSIGN_SPHERE(yMin, s);
237       if ((s[1] + s[3]) > (yMax[1] + yMax[3]))
238         VTK_ASSIGN_SPHERE(yMax, s);
239       if ((s[2] - s[3]) < (zMin[2] - zMin[3]))
240         VTK_ASSIGN_SPHERE(zMin, s);
241       if ((s[2] + s[3]) > (zMax[2] + zMax[3]))
242         VTK_ASSIGN_SPHERE(zMax, s);
243     }
244     T xSpan =
245       ((xMax[0] + xMax[3]) - (xMin[0] - xMin[3])) * ((xMax[0] + xMax[3]) - (xMin[0] - xMin[3])) +
246       ((xMax[1] + xMax[3]) - (xMin[1] - xMin[3])) * ((xMax[1] + xMax[3]) - (xMin[1] - xMin[3])) +
247       ((xMax[2] + xMax[3]) - (xMin[2] - xMin[3])) * ((xMax[2] + xMax[3]) - (xMin[2] - xMin[3]));
248     T ySpan =
249       ((yMax[0] + yMax[3]) - (yMin[0] - yMin[3])) * ((yMax[0] + yMax[3]) - (yMin[0] - yMin[3])) +
250       ((yMax[1] + yMax[3]) - (yMin[1] - yMin[3])) * ((yMax[1] + yMax[3]) - (yMin[1] - yMin[3])) +
251       ((yMax[2] + yMax[3]) - (yMin[2] - yMin[3])) * ((yMax[2] + yMax[3]) - (yMin[2] - yMin[3]));
252     T zSpan =
253       ((zMax[0] + zMax[3]) - (zMin[0] - zMin[3])) * ((zMax[0] + zMax[3]) - (zMin[0] - zMin[3])) +
254       ((zMax[1] + zMax[3]) - (zMin[1] - zMin[3])) * ((zMax[1] + zMax[3]) - (zMin[1] - zMin[3])) +
255       ((zMax[2] + zMax[3]) - (zMin[2] - zMin[3])) * ((zMax[2] + zMax[3]) - (zMin[2] - zMin[3]));
256 
257     if (xSpan > ySpan)
258     {
259       if (xSpan > zSpan)
260       {
261         VTK_ASSIGN_SPHERE(s1, xMin);
262         VTK_ASSIGN_SPHERE(s2, xMax);
263       }
264       else
265       {
266         VTK_ASSIGN_SPHERE(s1, zMin);
267         VTK_ASSIGN_SPHERE(s2, zMax);
268       }
269     }
270     else // ySpan > xSpan
271     {
272       if (ySpan > zSpan)
273       {
274         VTK_ASSIGN_SPHERE(s1, yMin);
275         VTK_ASSIGN_SPHERE(s2, yMax);
276       }
277       else
278       {
279         VTK_ASSIGN_SPHERE(s1, zMin);
280         VTK_ASSIGN_SPHERE(s2, zMax);
281       }
282     }
283   } // no hints provided
284 
285   // Compute initial estimated sphere, take into account the radius of each sphere
286   T tmp, v[3], r2 = vtkMath::Distance2BetweenPoints(s1, s2) / 4.0;
287   sphere[3] = r2 > 0.0 ? sqrt(r2) : s1[3];
288   T t1 = -s1[3] / (2.0 * sphere[3]);
289   T t2 = 1.0 + s2[3] / (2.0 * sphere[3]);
290   for (i = 0; i < 3; ++i)
291   {
292     v[i] = s2[i] - s1[i];
293     tmp = s1[i] + t1 * v[i];
294     s2[i] = s1[i] + t2 * v[i];
295     s1[i] = tmp;
296     sphere[i] = (s1[i] + s2[i]) / 2.0;
297   }
298   r2 = vtkMath::Distance2BetweenPoints(s1, s2) / 4.0;
299   if (r2 > 0.0)
300   {
301     sphere[3] = sqrt(r2);
302   }
303   else
304   {
305     sphere[3] = s1[3];
306     r2 = sphere[3] * sphere[3];
307   }
308 
309   // Second part: Make a pass over the points to make sure that they fit inside the sphere.
310   // If not, adjust the sphere to fit the point.
311   T dist, dist2, fac, sR2;
312   for (i = 0; i < numSpheres; ++i)
313   {
314     s = spheres[i];
315     sR2 = s[3] * s[3];
316     dist2 = vtkMath::Distance2BetweenPoints(s, sphere);
317     if (dist2 <= 0.0)
318     {
319       dist2 = s[3];
320     }
321     if (sR2 > dist2) // approximation to avoid square roots if possible
322     {
323       fac = 2.0 * sR2;
324     }
325     else
326     {
327       fac = 2.0 * dist2;
328     }
329     if ((dist2 + fac + sR2) > r2) // approximate test
330     {
331       dist = sqrt(dist2);
332       if (((dist + s[3]) * (dist + s[3])) > r2) // more accurate test
333       {
334         for (j = 0; j < 3; ++j)
335         {
336           v[j] = s[j] - sphere[j];
337           s1[j] = sphere[j] - (sphere[3] / dist) * v[j];
338           s2[j] = sphere[j] + (1.0 + s[3] / dist) * v[j];
339           sphere[j] = (s1[j] + s2[j]) / 2.0;
340         }
341         r2 = vtkMath::Distance2BetweenPoints(s1, s2) / 4.0;
342         if (r2 > 0.0)
343         {
344           sphere[3] = sqrt(r2);
345         }
346         else
347         {
348           sphere[3] = std::max(s1[3], sphere[3]);
349           r2 = sphere[3] * sphere[3];
350         }
351       }
352     }
353   }
354 }
355 #undef VTK_ASSIGN_SPHERE
356 
357 // Type specific wrappers for the templated functions below
358 //------------------------------------------------------------------------------
ComputeBoundingSphere(float * pts,vtkIdType numPts,float sphere[4],vtkIdType hints[2])359 void vtkSphere::ComputeBoundingSphere(
360   float* pts, vtkIdType numPts, float sphere[4], vtkIdType hints[2])
361 {
362   vtkSphereComputeBoundingSphere(pts, numPts, sphere, hints);
363 }
364 //------------------------------------------------------------------------------
ComputeBoundingSphere(double * pts,vtkIdType numPts,double sphere[4],vtkIdType hints[2])365 void vtkSphere::ComputeBoundingSphere(
366   double* pts, vtkIdType numPts, double sphere[4], vtkIdType hints[2])
367 {
368   vtkSphereComputeBoundingSphere(pts, numPts, sphere, hints);
369 }
370 //------------------------------------------------------------------------------
ComputeBoundingSphere(float ** spheres,vtkIdType numSpheres,float sphere[4],vtkIdType hints[2])371 void vtkSphere::ComputeBoundingSphere(
372   float** spheres, vtkIdType numSpheres, float sphere[4], vtkIdType hints[2])
373 {
374   vtkSphereComputeBoundingSphere(spheres, numSpheres, sphere, hints);
375 }
376 //------------------------------------------------------------------------------
ComputeBoundingSphere(double ** spheres,vtkIdType numSpheres,double sphere[4],vtkIdType hints[2])377 void vtkSphere::ComputeBoundingSphere(
378   double** spheres, vtkIdType numSpheres, double sphere[4], vtkIdType hints[2])
379 {
380   vtkSphereComputeBoundingSphere(spheres, numSpheres, sphere, hints);
381 }
382 
383 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)384 void vtkSphere::PrintSelf(ostream& os, vtkIndent indent)
385 {
386   this->Superclass::PrintSelf(os, indent);
387 
388   os << indent << "Radius: " << this->Radius << "\n";
389   os << indent << "Center: (" << this->Center[0] << ", " << this->Center[1] << ", "
390      << this->Center[2] << ")\n";
391 }
392