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