Library of Assembled Shared Sources
sphere_3d.inl
Go to the documentation of this file.
1/** @file
2 * @author Bram de Greve (bram@cocamware.com)
3 * @author Tom De Muer (tom@cocamware.com)
4 *
5 * *** BEGIN LICENSE INFORMATION ***
6 *
7 * The contents of this file are subject to the Common Public Attribution License
8 * Version 1.0 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://lass.sourceforge.net/cpal-license. The License is based on the
11 * Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover
12 * use of software over a computer network and provide for limited attribution for
13 * the Original Developer. In addition, Exhibit A has been modified to be consistent
14 * with Exhibit B.
15 *
16 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
17 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
18 * language governing rights and limitations under the License.
19 *
20 * The Original Code is LASS - Library of Assembled Shared Sources.
21 *
22 * The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
23 * The Original Developer is the Initial Developer.
24 *
25 * All portions of the code written by the Initial Developer are:
26 * Copyright (C) 2004-2022 the Initial Developer.
27 * All Rights Reserved.
28 *
29 * Contributor(s):
30 *
31 * Alternatively, the contents of this file may be used under the terms of the
32 * GNU General Public License Version 2 or later (the GPL), in which case the
33 * provisions of GPL are applicable instead of those above. If you wish to allow use
34 * of your version of this file only under the terms of the GPL and not to allow
35 * others to use your version of this file under the CPAL, indicate your decision by
36 * deleting the provisions above and replace them with the notice and other
37 * provisions required by the GPL License. If you do not delete the provisions above,
38 * a recipient may use your version of this file under either the CPAL or the GPL.
39 *
40 * *** END LICENSE INFORMATION ***
41 */
42
43
44
45#ifndef LASS_GUARDIAN_OF_INCLUSION_PRIM_SPHERE3D_INL
46#define LASS_GUARDIAN_OF_INCLUSION_PRIM_SPHERE3D_INL
47
48
49#include "sphere_3d.h"
51
52namespace lass
53{
54namespace prim
55{
56
57// --- public --------------------------------------------------------------------------------------
58
59template <typename T>
60Sphere3D<T>::Sphere3D():
61 center_(),
62 radius_(TNumTraits::zero)
63{
64 LASS_ASSERT(center_.isZero());
65}
66
67
68
69template <typename T>
70Sphere3D<T>::Sphere3D(const TPoint& iCenter, TParam iRadius):
71 center_(iCenter),
72 radius_(iRadius)
73{
74}
75
76
77
78template <typename T>
79const typename Sphere3D<T>::TPoint&
80Sphere3D<T>::center() const
81{
82 return center_;
83}
84
85
86
87template <typename T>
88typename Sphere3D<T>::TPoint&
89Sphere3D<T>::center()
90{
91 return center_;
92}
93
94
95
96template<typename T>
97typename Sphere3D<T>::TConstReference
98Sphere3D<T>::radius() const
99{
100 return radius_;
101}
102
103
104
105template<typename T>
106typename Sphere3D<T>::TReference
107Sphere3D<T>::radius()
108{
109 return radius_;
114/** return area of surface of sphere
115 */
116template<typename T>
117const typename Sphere3D<T>::TValue
120 return 4 * TNumTraits::pi * num::sqr(radius_);
122
123
124
125/** return volume of sphere
126 */
127template<typename T>
128const typename Sphere3D<T>::TValue
130{
131 return (4 * TNumTraits::pi * num::sqr(radius_) * radius_) / 3;
132}
133
134
135
136/** Classify a point and tell and what side of the sphere surface it is.
137 * @return sInside, sSurface, sOutside
138 */
139template<typename T>
140Side Sphere3D<T>::classify(const TPoint& iPoint) const
141{
142 const TValue eq = equation(iPoint);
143 return eq > TNumTraits::zero ? sOutside : (eq < TNumTraits::zero ? sInside : sSurface);
144}
145
146
147
148/**
149 * (P - C)² - r²
150 */
151template<typename T>
152const typename Sphere3D<T>::TValue
153Sphere3D<T>::equation(const TPoint& iPoint) const
154{
155 const TVector difference = iPoint - center_;
156 return difference.squaredNorm() - num::sqr(radius_);
157}
158
159
160
161/** return signed distance of point to surface of sphere.
162 * negative distance means point is inside the sphere.
163 */
164template<typename T>
165const typename Sphere3D<T>::TValue
166Sphere3D<T>::signedDistance(const TPoint& iPoint) const
167{
168 const TValue eq = equation(iPoint);
169 return eq >= TNumTraits::zero ? num::sqrt(eq) : -num::sqrt(-eq);
170}
171
172
173
174/** return squared distance of point to surface of sphere.
175 */
176template<typename T>
177const typename Sphere3D<T>::TValue
178Sphere3D<T>::squaredDistance(const TPoint& iPoint) const
179{
180 return num::abs(equation(iPoint));
181}
182
183
184
185/** returns if point is on inside or on surface
186 * @return <tt>classify(iPoint) != sOutside</tt> but may be faster
187 */
188template<typename T>
189bool Sphere3D<T>::contains(const TPoint& iPoint) const
190{
191 const TValue eq = equation(iPoint);
192 return eq <= TNumTraits::zero;
193}
194
195
196
197/** Classify a point and tell and what side of the sphere surface it is.
198 * @return sInside, sSurface, sOutside
199 */
200template<typename T>
201Side Sphere3D<T>::classify(const TPoint& iPoint, TParam iRelativeTolerance) const
202{
203 const TValue eq = equation(iPoint, iRelativeTolerance);
204 return eq > TNumTraits::zero ? sOutside : (eq < TNumTraits::zero ? sInside : sSurface);
205}
206
207
208
209/**
210 * (P - C)² - r²
211 */
212template<typename T>
213const typename Sphere3D<T>::TValue
214Sphere3D<T>::equation(const TPoint& iPoint, TParam iRelativeTolerance) const
215{
216 const TVector difference = iPoint - center_;
217 const TValue d2 = difference.squaredNorm();
218 const TValue r2 = num::sqr(radius_);
219 return num::almostEqual(d2, r2, iRelativeTolerance) ? TNumTraits::zero : (d2 - r2);
220}
221
222
223
224/** return signed distance of point to surface of sphere.
225 * negative distance means point is inside the sphere.
226 */
227template<typename T>
228const typename Sphere3D<T>::TValue
229Sphere3D<T>::signedDistance(const TPoint& iPoint, TParam iRelativeTolerance) const
230{
231 const TValue eq = equation(iPoint, iRelativeTolerance);
232 return eq >= TNumTraits::zero ? num::sqrt(eq) : -num::sqrt(-eq);
233}
234
235
236
237/** return squared distance of point to surface of sphere.
238 */
239template<typename T>
240const typename Sphere3D<T>::TValue
241Sphere3D<T>::squaredDistance(const TPoint& iPoint, TParam iRelativeTolerance) const
242{
243 return num::abs(equation(iPoint, iRelativeTolerance));
244}
245
246
247
248/** returns if point is on inside or on surface
249 * @return <tt>classify(iPoint, iRelativeTolerance) != sOutside</tt> but may be faster
250 */
251template<typename T>
252bool Sphere3D<T>::contains(const TPoint& iPoint, TParam iRelativeTolerance) const
253{
254 const TValue eq = equation(iPoint, iRelativeTolerance);
255 return eq <= TNumTraits::zero;
256}
257
258
259
260/** return true if sphere has a non-negative radius
261 */
262template <typename T>
264{
265 return radius_ >= TNumTraits::zero;
266}
267
268
269
270// --- protected -----------------------------------------------------------------------------------
271
272
273
274// --- private -------------------------------------------------------------------------------------
275
276
277
278// --- free ----------------------------------------------------------------------------------------
279
280/** @relates lass::prim::Sphere3D
281 */
282template <typename T>
283const T squaredDistance(const Sphere3D<T>& sphere, const Point3D<T>& point)
284{
285 return sphere.squaredDistance(point);
286}
287
288
289
290/** @relates lass::prim::Sphere3D
291 */
292template <typename T>
293const T distance(const Sphere3D<T>& sphere, const Point3D<T>& point)
294{
295 return num::sqrt(sphere.squaredDistance(point));
296}
297
298
299
300/** @relates lass::prim::Sphere3D
301 */
302template <typename T>
303bool intersects(const Sphere3D<T>& a, const Sphere3D<T>& b)
304{
305 return (a.center() - b.center()).squaredNorm() <= num::sqr(a.radius() + b.radius());
306}
307
308
309namespace impl
310{
311 template <typename T>
312 Sphere3D<T> circumSphere2(const Point3D<T>& a, const Point3D<T>& b)
313 {
314 const auto center = (a + b).affine();
315 const auto radius = distance(a, b) / 2;
316 return Sphere3D<T>(center, radius);
317 }
318
319 template <typename T>
320 Sphere3D<T> circumSphere3(const Point3D<T>& a, const Point3D<T>& b, const Point3D<T>& c)
321 {
322 const auto v1 = b - a;
323 const auto v2 = c - a;
324 const auto n = cross(v1, v2);
325 const auto center = a + cross(v1.squaredNorm() * v2 - v2.squaredNorm() * v1, n) / (2 * n.squaredNorm());
326 const auto radius = distance(center, a);
327 return Sphere3D<T>(center, radius);
328 }
329
330 template <typename T>
331 Sphere3D<T> circumSphere4(const Point3D<T>& a, const Point3D<T>& b, const Point3D<T>& c, const Point3D<T>& d)
332 {
333 // alternative II from https://math.stackexchange.com/a/2414870
334 const auto v1 = b - a;
335 const auto v2 = c - a;
336 const auto v3 = d - a;
337 const auto d1 = v1.squaredNorm();
338 const auto d2 = v2.squaredNorm();
339 const auto d3 = v3.squaredNorm();
340 const auto center = a + (d1 * cross(v2, v3) + d2 * cross(v3, v1) + d3 * cross(v1, v2)) / (2 * dot(v1, cross(v2, v3)));
341 const auto radius = distance(center, a);
342 return Sphere3D<T>(center, radius);
343 }
344
345 template <typename T, typename ForwardIterator>
346 Sphere3D<T> boundingSphere(std::vector<ForwardIterator>& points, size_t size, size_t bounds)
347 {
348 // Welzl, E. (1991). Smallest enclosing disks (balls and ellipsoids). In: Maurer, H. (eds) New Results and New Trends
349 // in Computer Science. Lecture Notes in Computer Science, vol 555. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0038202
350
351 // Gartner, B. (1999).Fast and Robust Smallest Enclosing Balls.In: Nesetril, J. (eds) Algorithms - ESA' 99. ESA 1999.
352 // Lecture Notes in Computer Science, vol 1643. Springer, Berlin, Heidelberg.https://doi.org/10.1007/3-540-48481-7_29
353
354 Sphere3D<T> sphere;
355 size_t i;
356 switch (bounds)
357 {
358 case 0:
359 case 1:
360 case 2:
361 sphere = circumSphere2(*points[0], *points[1]);
362 i = 2;
363 break;
364 case 3:
365 sphere = circumSphere3(*points[0], *points[1], *points[2]);
366 i = 3;
367 break;
368 case 4:
369 return circumSphere4(*points[0], *points[1], *points[2], *points[3]);
370 default:
371 throw std::logic_error("invalid bounds");
372 }
373
374 for (; i < size; ++i)
375 {
376 T bestE = sphere.equation(*points[i]);
377 size_t best = i;
378 for (size_t k = i + 1; k < size; ++k)
379 {
380 const T e = sphere.equation(*points[k]);
381 if (e > bestE)
382 {
383 bestE = e;
384 best = k;
385 }
386 }
387 if (bestE <= 0)
388 {
389 return sphere;
390 }
391 ForwardIterator p = points[best];
392 for (size_t k = best; k > bounds; --k)
393 {
394 points[k] = points[k - 1];
395 }
396 points[bounds] = p;
397 sphere = boundingSphere<T>(points, i + 1, bounds + 1);
398 }
399
400 return sphere;
401 }
402}
403
404
405
406/** @relates lass::prim::Sphere3D
407 */
408template <typename T, typename ForwardIterator>
409Sphere3D<T> boundingSphere(ForwardIterator first, ForwardIterator last)
410{
411 using TSphere = Sphere3D<T>;
412 using TNumTraits = typename TSphere::TNumTraits;
413 using TPoint = typename TSphere::TPoint;
414
415 std::vector<ForwardIterator> points;
416 for (ForwardIterator p = first; p != last; ++p)
417 {
418 points.push_back(p);
419 }
420
421 const size_t size = points.size();
422 switch (size)
423 {
424 case 0:
425 {
426 const auto nan = TNumTraits::qNaN;
427 return TSphere(TPoint(nan, nan, nan), nan);
428 }
429 case 1:
430 return TSphere(*points[0], TNumTraits::zero);
431 }
432
433 return impl::boundingSphere<T>(points, size, 0);
434}
435
436
437/** @relates lass::prim::Sphere3D
438 */
439template <typename T>
440Sphere3D<T> boundingSphere(const std::vector<Point3D<T>>& points)
441{
442 return boundingSphere<T>(points.begin(), points.end());
443}
444
445
446
447/** @relates lass::prim::Sphere3D
448 */
449template<typename T>
450std::ostream& operator<<(std::ostream& ioOStream, const Sphere3D<T>& iSphere)
451{
452 LASS_ENFORCE(ioOStream) << "{S=" << iSphere.center() << ", r=" << iSphere.radius() << "}";
453 return ioOStream;
454}
455
456
457
458/** @relates lass::prim::Sphere3D
459 */
460template<typename T>
461io::XmlOStream& operator<<(io::XmlOStream& ioOStream, const Sphere3D<T>& iSphere)
462{
463 LASS_ENFORCE_STREAM(ioOStream)
464 << "<Sphere3D>\n"
465 << "<center>" << iSphere.center() << "</center>\n"
466 << "<radius>" << iSphere.radius() << "</radius>\n"
467 << "</Sphere3D>\n";
468
469 return ioOStream;
470}
471
472
473
474}
475
476}
477
478#endif
479
480// --- END OF FILE ------------------------------------------------------------------------------
const TValue area() const
return area of surface of sphere
const TValue signedDistance(const TPoint &iPoint) const
return signed distance of point to surface of sphere.
const TValue squaredDistance(const TPoint &iPoint) const
return squared distance of point to surface of sphere.
bool isValid() const
return true if sphere has a non-negative radius
const TValue volume() const
return volume of sphere
bool contains(const TPoint &iPoint) const
returns if point is on inside or on surface
Side classify(const TPoint &iPoint) const
Classify a point and tell and what side of the sphere surface it is.
const TValue equation(const TPoint &iPoint) const
(P - C)² - r²
T sqr(const T &x)
return x * x
Definition basic_ops.h:162
T abs(const T &x)
if x < 0 return -x, else return x.
Definition basic_ops.h:145
set of geometrical primitives
Definition aabb_2d.h:81
Side
Different sides of a surface.
Definition side.h:79
@ sOutside
outside the surface
Definition side.h:86
@ sSurface
right on the surface
Definition side.h:87
@ sInside
inside the surface
Definition side.h:85
Library for Assembled Shared Sources.
Definition config.h:53
const TValue squaredNorm() const
Return squared norm of vector.