Skip to content
Snippets Groups Projects
BoundingSphere.cpp 6.3 KiB
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"

// ThirdParty/logog
#include "logog/include/logog.hpp"

#include "MathTools.h"
#include "AnalyticalGeometry.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(const BoundingSphere &sphere)
: _center(sphere.getCenter()), _radius(sphere.getRadius())
{
}

BoundingSphere::BoundingSphere(const GeoLib::Point &p)
: _center(p), _radius(std::numeric_limits<double>::epsilon())
{
}

BoundingSphere::BoundingSphere(const GeoLib::Point &p, double radius)
: _center(p), _radius(radius)
{
}

BoundingSphere::BoundingSphere(const GeoLib::Point &p, const GeoLib::Point &q)
: _center(p), _radius(std::numeric_limits<double>::epsilon())
{
	const MathLib::Vector3 a(p, q);

    if (a.getLength() > 0)
    {
	    const MathLib::Vector3 o(0.5*a);
	    _radius = o.getLength() + std::numeric_limits<double>::epsilon();
	    _center = MathLib::Vector3(p) + o;
    }
}

BoundingSphere::BoundingSphere(const GeoLib::Point &p, const GeoLib::Point &q,  const GeoLib::Point &r)
{
	const MathLib::Vector3 a(p,r);
	const MathLib::Vector3 b(p,q);

	const MathLib::Vector3 cross_ab(crossProduct(a,b));

    if (cross_ab.getLength() > 0)
    {
	    const double denom = 2.0 * scalarProduct(cross_ab,cross_ab);
	    const MathLib::Vector3 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(const GeoLib::Point &p, const GeoLib::Point &q, const GeoLib::Point &r, const GeoLib::Point &s)
{
	const MathLib::Vector3 a(p, q);
	const MathLib::Vector3 b(p, r);
	const MathLib::Vector3 c(p, s);

    if (!GeoLib::isCoplanar(p, q, r, s))
    {
    	// det of matrix [a^T, b^T, c^T]^T
	    const double 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]));
	    const MathLib::Vector3 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;
    }
    else
    {
        BoundingSphere pqr(p, q , r);
        BoundingSphere pqs(p, q , s);
        BoundingSphere prs(p, r , s);
        BoundingSphere 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(const std::vector<GeoLib::Point*> &points)
: _center(0,0,0), _radius(-1)
{
	const std::size_t n_points (points.size());
	GeoLib::Point **sphere_points = new GeoLib::Point*[n_points];
	for(unsigned int i = 0; i < n_points; i++)
		sphere_points[i] = points[i];

	const BoundingSphere bounding_sphere = recurseCalculation(points, 0, points.size(), 0);
	delete[] sphere_points;
	
	this->_center = bounding_sphere.getCenter();
	this->_radius = bounding_sphere.getRadius();
}

BoundingSphere BoundingSphere::recurseCalculation(std::vector<GeoLib::Point*> sphere_points, std::size_t current_index, std::size_t n_points, std::size_t n_boundary_points)
{
	BoundingSphere sphere;
	switch(n_boundary_points)
	{
	case 0:
		sphere = BoundingSphere();
		break;
	case 1:
		sphere = BoundingSphere(*sphere_points[current_index-1]);
		sphere = BoundingSphere(*sphere_points[current_index-1], *sphere_points[current_index-2]);
		sphere = BoundingSphere(*sphere_points[current_index-1], *sphere_points[current_index-2], *sphere_points[current_index-3]);
		sphere = BoundingSphere(*sphere_points[current_index-1], *sphere_points[current_index-2], *sphere_points[current_index-3], *sphere_points[current_index-4]);
	for(std::size_t i=current_index; i<n_points; ++i)
        // current point is located outside of sphere
		if(sphere.sqrPointDist(*sphere_points[i]) > 0)
		{
			GeoLib::Point* tmp = sphere_points[i];
            std::copy(sphere_points.begin(), sphere_points.begin() + i, sphere_points.begin() + 1);
            sphere_points[0] = tmp;

    		sphere = recurseCalculation(sphere_points, current_index+1, i, n_boundary_points+1);
		}
	}
	return sphere;
}

double BoundingSphere::sqrPointDist(const GeoLib::Point pnt) const
{
	return MathLib::sqrDist(_center.getCoords(), pnt.getCoords())-(_radius*_radius);
}

Karsten Rink's avatar
Karsten Rink committed
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 fac (this->_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;
}

}