Newer
Older
/**
* \file Calculation of a minimum bounding sphere for a vector of points
* \author Karsten Rink
* \date 2014-07-11
* \brief Implementation of the BoundingSphere class.
*
* \copyright
* Copyright (c) 2013, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*
*/
#include "BoundingSphere.h"
namespace GeoLib {
BoundingSphere::BoundingSphere()
: _center(std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max()), _radius(-1)
BoundingSphere::BoundingSphere(BoundingSphere const& sphere)
: _center(sphere.getCenter()), _radius(sphere.getRadius())
{
}
BoundingSphere::BoundingSphere(GeoLib::Point const& p)
: _center(p), _radius(std::numeric_limits<double>::epsilon())
{
}
BoundingSphere::BoundingSphere(GeoLib::Point const& p, double radius)
: _center(p), _radius(radius)
{
}
BoundingSphere::BoundingSphere(GeoLib::Point const& p, GeoLib::Point const& q)
: _center(p), _radius(std::numeric_limits<double>::epsilon())
MathLib::Vector3 const a(p, q);
MathLib::Vector3 const o(0.5*a);
_radius = o.getLength() + std::numeric_limits<double>::epsilon();
_center = MathLib::Vector3(p) + o;
BoundingSphere::BoundingSphere(GeoLib::Point const& p, GeoLib::Point const& q, GeoLib::Point const& r)
MathLib::Vector3 const a(p,r);
MathLib::Vector3 const b(p,q);
MathLib::Vector3 const cross_ab(crossProduct(a,b));
double const denom = 2.0 * scalarProduct(cross_ab,cross_ab);
MathLib::Vector3 const o = (scalarProduct(b,b) * crossProduct(cross_ab, a)
+ scalarProduct(a,a) * crossProduct(b, cross_ab))
* (1.0 / denom);
_radius = o.getLength() + std::numeric_limits<double>::epsilon();
_center = MathLib::Vector3(p) + o;
}
else
{
BoundingSphere two_pnts_sphere;
if (a.getLength() > b.getLength())
two_pnts_sphere = BoundingSphere(p,r);
else
two_pnts_sphere = BoundingSphere(p,q);
_radius = two_pnts_sphere.getRadius();
_center = two_pnts_sphere.getCenter();
}
BoundingSphere::BoundingSphere(GeoLib::Point const& p, GeoLib::Point const& q, GeoLib::Point const& r, GeoLib::Point const& s)
MathLib::Vector3 const a(p, q);
MathLib::Vector3 const b(p, r);
MathLib::Vector3 const c(p, s);
if (!GeoLib::isCoplanar(p, q, r, s))
{
// det of matrix [a^T, b^T, c^T]^T
double const denom = 2.0 * (a[0] * (b[1] * c[2] - c[1] * b[2])
- b[0] * (a[1] * c[2] - c[1] * a[2])
+ c[0] * (a[1] * b[2] - b[1] * a[2]));
MathLib::Vector3 const o = (scalarProduct(c,c) * crossProduct(a,b)
+ scalarProduct(b,b) * crossProduct(c,a)
+ scalarProduct(a,a) * crossProduct(b,c))
* (1.0 / denom);
_radius = o.getLength() + std::numeric_limits<double>::epsilon();
_center = MathLib::Vector3(p) + o;
BoundingSphere const pqr(p, q , r);
BoundingSphere const pqs(p, q , s);
BoundingSphere const prs(p, r , s);
BoundingSphere const qrs(q, r , s);
_radius = pqr.getRadius();
_center = pqr.getCenter();
if (_radius < pqs.getRadius())
{
_radius = pqs.getRadius();
_center = pqs.getCenter();
}
if (_radius < prs.getRadius())
{
_radius = prs.getRadius();
_center = prs.getCenter();
}
if (_radius < qrs.getRadius())
{
_radius = qrs.getRadius();
_center = qrs.getCenter();
}
}
BoundingSphere::BoundingSphere(std::vector<GeoLib::Point*> const& points)
: _center(0,0,0), _radius(-1)
{
std::size_t const n_points (points.size());
std::vector<GeoLib::Point*> sphere_points(points);
BoundingSphere const bounding_sphere = recurseCalculation(sphere_points, 0, sphere_points.size(), 0);
_center = bounding_sphere.getCenter();
_radius = bounding_sphere.getRadius();
BoundingSphere BoundingSphere::recurseCalculation(std::vector<GeoLib::Point*> sphere_points, std::size_t start_idx, std::size_t length, std::size_t n_boundary_points)
BoundingSphere sphere;
switch(n_boundary_points)
{
case 0:
sphere = BoundingSphere();
break;
case 1:
sphere = BoundingSphere(*sphere_points[start_idx-1]);
break;
case 2:
sphere = BoundingSphere(*sphere_points[start_idx-1], *sphere_points[start_idx-2]);
break;
case 3:
sphere = BoundingSphere(*sphere_points[start_idx-1], *sphere_points[start_idx-2], *sphere_points[start_idx-3]);
sphere = BoundingSphere(*sphere_points[start_idx-1], *sphere_points[start_idx-2], *sphere_points[start_idx-3], *sphere_points[start_idx-4]);
return sphere;
}
for(std::size_t i=0; i<length; ++i)
{
// current point is located outside of sphere
if (sphere.sqrPointDist(*sphere_points[start_idx+i]) > 0)
GeoLib::Point* tmp = sphere_points[start_idx+i];
std::copy(sphere_points.begin() + start_idx, sphere_points.begin() + (start_idx + i), sphere_points.begin() + (start_idx + 1));
sphere_points[start_idx] = tmp;
}
sphere = recurseCalculation(sphere_points, start_idx+1, i, n_boundary_points+1);
}
}
return sphere;
double BoundingSphere::sqrPointDist(GeoLib::Point const& pnt) const
return MathLib::sqrDist(_center.getCoords(), pnt.getCoords())-(_radius*_radius);
std::vector<GeoLib::Point*>* BoundingSphere::getRandomSpherePoints(std::size_t n_points) const
std::vector<GeoLib::Point*> *pnts = new std::vector<GeoLib::Point*>;
pnts->reserve(n_points);
srand ( static_cast<unsigned>(time(NULL)) );
for (std::size_t k(0); k<n_points; ++k)
{
MathLib::Vector3 vec (0,0,0);
double sum (0);
for (unsigned i=0; i<3; ++i)
{
vec[i] = (double)rand()-(RAND_MAX/2.0);
sum+=(vec[i]*vec[i]);
}
double const fac (_radius/sqrt(sum));
pnts->push_back(new GeoLib::Point(_center[0]+vec[0]*fac, _center[1]+vec[1]*fac, _center[2]+vec[2]*fac));
}
return pnts;