/********************************************************************************
* *
* D o u b l e - P r e c i s i o n S p h e r e C l a s s *
* *
*********************************************************************************
* Copyright (C) 2004,2020 by Jeroen van der Zijp. All Rights Reserved. *
*********************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License as published by *
* the Free Software Foundation; either version 3 of the License, or *
* (at your option) any later version. *
* *
* This library is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public License *
* along with this program. If not, see *
********************************************************************************/
#include "xincs.h"
#include "fxver.h"
#include "fxdefs.h"
#include "fxmath.h"
#include "FXArray.h"
#include "FXHash.h"
#include "FXStream.h"
#include "FXVec2d.h"
#include "FXVec3d.h"
#include "FXVec4d.h"
#include "FXSphered.h"
#include "FXRanged.h"
#include "FXMat3d.h"
#include "FXMat4d.h"
/*
Notes:
- Negative radius represents empty bounding sphere.
*/
using namespace FX;
/************************** S p h e r e C l a s s *************************/
namespace FX {
// Initialize from bounding box
FXSphered::FXSphered(const FXRanged& bounds):center(bounds.center()),radius(bounds.diameter()*0.5){
}
// Test if sphere contains point x,y,z
FXbool FXSphered::contains(FXdouble x,FXdouble y,FXdouble z) const {
return 0.0<=radius && Math::sqr(center.x-x)+Math::sqr(center.y-y)+Math::sqr(center.z-z)<=Math::sqr(radius);
}
// Test if sphere contains point p
FXbool FXSphered::contains(const FXVec3d& p) const {
return contains(p.x,p.y,p.z);
}
// Test if sphere contains another box
FXbool FXSphered::contains(const FXRanged& box) const {
if(box.lower.x<=box.upper.x && box.lower.y<=box.upper.y && box.lower.z<=box.upper.z){
return contains(box.corner(0)) && contains(box.corner(1)) && contains(box.corner(2)) && contains(box.corner(3)) && contains(box.corner(4)) && contains(box.corner(5)) && contains(box.corner(6)) && contains(box.corner(7));
}
return false;
}
// Test if sphere properly contains another sphere
FXbool FXSphered::contains(const FXSphered& sphere) const {
if(0.0<=sphere.radius && sphere.radius<=radius){
FXdouble dx=center.x-sphere.center.x;
FXdouble dy=center.y-sphere.center.y;
FXdouble dz=center.z-sphere.center.z;
return sphere.radius+Math::sqrt(dx*dx+dy*dy+dz*dz)<=radius;
}
return false;
}
// Include point
FXSphered& FXSphered::include(FXdouble x,FXdouble y,FXdouble z){
if(0.0<=radius){
FXdouble dx=x-center.x;
FXdouble dy=y-center.y;
FXdouble dz=z-center.z;
FXdouble dist=Math::sqrt(dx*dx+dy*dy+dz*dz);
if(radius=radius) return 1;
// Overlap
return 0;
}
// Intersect sphere with ray u-v
FXbool FXSphered::intersect(const FXVec3d& u,const FXVec3d& v) const {
FXdouble hits[2];
return intersect(u,v-u,hits) && 0.0<=hits[1] && hits[0]<=1.0;
}
// Intersect box with ray pos+lambda*dir, returning true if hit
FXbool FXSphered::intersect(const FXVec3d& pos,const FXVec3d& dir,FXdouble hit[]) const {
if(0.0<=radius){
FXVec3d m=center-pos;
FXdouble m2=m.length2();
FXdouble d2=dir.length2();
FXdouble r2=radius*radius;
FXdouble b=dir*m;
FXdouble disc=b*b-d2*(m2-r2);
if(0.0<=disc){
disc=Math::sqrt(disc);
hit[0]=(-b-disc)/d2;
hit[1]=(-b+disc)/d2;
return true;
}
}
return false;
}
// Test if box overlaps with sphere (QRI Larsson, Moeller, Lengyel)
FXbool overlap(const FXSphered& a,const FXRanged& b){
if(0.0<=a.radius){
FXdouble e1,e2,e3;
if((e1=Math::fmax(b.lower.x-a.center.x,0.0)+Math::fmax(a.center.x-b.upper.x,0.0))>a.radius) return false;
if((e2=Math::fmax(b.lower.y-a.center.y,0.0)+Math::fmax(a.center.y-b.upper.y,0.0))>a.radius) return false;
if((e3=Math::fmax(b.lower.z-a.center.z,0.0)+Math::fmax(a.center.z-b.upper.z,0.0))>a.radius) return false;
return e1*e1+e2*e2+e3*e3<=a.radius*a.radius;
}
return false;
}
// Test if box overlaps with sphere; algorithm due to Arvo (GEMS I)
FXbool overlap(const FXRanged& a,const FXSphered& b){
return overlap(b,a);
}
// Test if spheres overlap
FXbool overlap(const FXSphered& a,const FXSphered& b){
if(0.0<=a.radius && 0.0<=b.radius){
FXdouble dx=a.center.x-b.center.x;
FXdouble dy=a.center.y-b.center.y;
FXdouble dz=a.center.z-b.center.z;
return (dx*dx+dy*dy+dz*dz)>(FXStream& store,FXSphered& sphere){
store >> sphere.center >> sphere.radius;
return store;
}
}