You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

401 lines
12 KiB

////////////////////////////////////////////////////////////////////
// OBB.inl
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
// D E F I N E S ///////////////////////////////////////////////////
// S T R U C T S ///////////////////////////////////////////////////
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(bool)
:
m_rot(MATRIX::Identity()),
m_pos(POINT::Zero()),
m_ext(POINT::Zero())
{
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const AABB& aabb)
{
Set(aabb);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax)
{
Set(rot, ptMin, ptMax);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n)
{
Set(pts, n);
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>::TOBB(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s)
{
Set(pts, n, tris, s);
} // constructor
template <typename TYPE, int DIMS>
template <typename CTYPE>
inline TOBB<TYPE,DIMS>::TOBB(const TOBB<CTYPE,DIMS>& rhs)
:
m_rot(rhs.m_rot.template cast<TYPE>()),
m_pos(rhs.m_pos.template cast<TYPE>()),
m_ext(rhs.m_ext.template cast<TYPE>())
{
} // copy constructor
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const AABB& aabb)
{
m_rot.setIdentity();
m_pos = aabb.GetCenter();
m_ext = aabb.GetSize()/TYPE(2);
}
// build from rotation matrix from world to local, and local min/max corners
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& rot, const POINT& ptMin, const POINT& ptMax)
{
m_rot = rot;
m_pos = (ptMax + ptMin) * TYPE(0.5);
m_ext = (ptMax - ptMin) * TYPE(0.5);
}
// Inspired from "Fitting Oriented Bounding Boxes" by James Gregson
// http://jamesgregson.blogspot.ro/2011/03/latex-test.html
// build an OBB from a vector of input points. This
// method just forms the covariance matrix and hands
// it to the build_from_covariance_matrix method
// which handles fitting the box to the points
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n)
{
ASSERT(n >= DIMS);
// loop over the points to find the mean point
// location and to build the covariance matrix;
// note that we only have
// to build terms for the upper triangular
// portion since the matrix is symmetric
POINT mu(POINT::Zero());
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0;
for (size_t i=0; i<n; ++i) {
const POINT& p = pts[i];
mu += p;
cxx += p(0)*p(0);
cxy += p(0)*p(1);
cxz += p(0)*p(2);
cyy += p(1)*p(1);
cyz += p(1)*p(2);
czz += p(2)*p(2);
}
const TYPE invN(TYPE(1)/TYPE(n));
cxx = (cxx - mu(0)*mu(0)*invN)*invN;
cxy = (cxy - mu(0)*mu(1)*invN)*invN;
cxz = (cxz - mu(0)*mu(2)*invN)*invN;
cyy = (cyy - mu(1)*mu(1)*invN)*invN;
cyz = (cyz - mu(1)*mu(2)*invN)*invN;
czz = (czz - mu(2)*mu(2)*invN)*invN;
// now build the covariance matrix
MATRIX C;
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz;
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz;
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz;
// set the OBB parameters from the covariance matrix
Set(C, pts, n);
}
// builds an OBB from triangles specified as an array of
// points with integer indices into the point array. Forms
// the covariance matrix for the triangles, then uses the
// method build_from_covariance_matrix method to fit
// the box. ALL points will be fit in the box, regardless
// of whether they are indexed by a triangle or not.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const POINT* pts, size_t n, const TRIANGLE* tris, size_t s)
{
ASSERT(n >= DIMS);
// loop over the triangles this time to find the
// mean location
POINT mu(POINT::Zero());
TYPE Am=0;
TYPE cxx=0, cxy=0, cxz=0, cyy=0, cyz=0, czz=0;
for (size_t i=0; i<s; ++i) {
ASSERT(tris[i](0)<n && tris[i](1)<n && tris[i](2)<n);
const POINT& p = pts[tris[i](0)];
const POINT& q = pts[tris[i](1)];
const POINT& r = pts[tris[i](2)];
const POINT mui = (p+q+r)/TYPE(3);
const TYPE Ai = (q-p).cross(r-p).normalize()/TYPE(2);
mu += mui*Ai;
Am += Ai;
// these bits set the c terms to Am*E[xx], Am*E[xy], Am*E[xz]....
const TYPE Ai12 = Ai/TYPE(12);
cxx += (TYPE(9)*mui(0)*mui(0) + p(0)*p(0) + q(0)*q(0) + r(0)*r(0))*Ai12;
cxy += (TYPE(9)*mui(0)*mui(1) + p(0)*p(1) + q(0)*q(1) + r(0)*r(1))*Ai12;
cxz += (TYPE(9)*mui(0)*mui(2) + p(0)*p(2) + q(0)*q(2) + r(0)*r(2))*Ai12;
cyy += (TYPE(9)*mui(1)*mui(1) + p(1)*p(1) + q(1)*q(1) + r(1)*r(1))*Ai12;
cyz += (TYPE(9)*mui(1)*mui(2) + p(1)*p(2) + q(1)*q(2) + r(1)*r(2))*Ai12;
czz += (TYPE(9)*mui(2)*mui(2) + p(2)*p(2) + q(2)*q(2) + r(2)*r(2))*Ai12;
}
// divide out the Am fraction from the average position and
// covariance terms
mu /= Am;
cxx /= Am; cxy /= Am; cxz /= Am; cyy /= Am; cyz /= Am; czz /= Am;
// now subtract off the E[x]*E[x], E[x]*E[y], ... terms
cxx -= mu(0)*mu(0); cxy -= mu(0)*mu(1); cxz -= mu(0)*mu(2);
cyy -= mu(1)*mu(1); cyz -= mu(1)*mu(2); czz -= mu(2)*mu(2);
// now build the covariance matrix
MATRIX C;
C(0,0)=cxx; C(0,1)=cxy; C(0,2)=cxz;
C(1,0)=cxy; C(1,1)=cyy; C(1,2)=cyz;
C(2,0)=cxz; C(1,2)=cyz; C(2,2)=czz;
// set the obb parameters from the covariance matrix
Set(C, pts, n);
}
// method to set the OBB parameters which produce a box oriented according to
// the covariance matrix C, and that contains the given points
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Set(const MATRIX& C, const POINT* pts, size_t n)
{
// extract rotation from the covariance matrix
SetRotation(C);
// extract size and center from the given points
SetBounds(pts, n);
}
// method to set the OBB rotation which produce a box oriented according to
// the covariance matrix C (only the rotations is set)
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::SetRotation(const MATRIX& C)
{
// extract the eigenvalues and eigenvectors from C
const Eigen::SelfAdjointEigenSolver<MATRIX> es(C);
ASSERT(es.info() == Eigen::Success);
// find the right, up and forward vectors from the eigenvectors
// and set the rotation matrix using the eigenvectors
ASSERT(es.eigenvalues()(0) < es.eigenvalues()(1) && es.eigenvalues()(1) < es.eigenvalues()(2));
m_rot = es.eigenvectors().transpose();
if (m_rot.determinant() < 0)
m_rot = -m_rot;
}
// method to set the OBB center and size that contains the given points
// the rotations should be already set
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::SetBounds(const POINT* pts, size_t n)
{
ASSERT(n >= DIMS);
ASSERT(ISEQUAL((m_rot*m_rot.transpose()).trace(), TYPE(3)) && ISEQUAL(m_rot.determinant(), TYPE(1)));
// build the bounding box extents in the rotated frame
const TYPE tmax = std::numeric_limits<TYPE>::max();
POINT minim(tmax, tmax, tmax), maxim(-tmax, -tmax, -tmax);
for (size_t i=0; i<n; ++i) {
const POINT p_prime(m_rot * pts[i]);
if (minim(0) > p_prime(0)) minim(0) = p_prime(0);
if (minim(1) > p_prime(1)) minim(1) = p_prime(1);
if (minim(2) > p_prime(2)) minim(2) = p_prime(2);
if (maxim(0) < p_prime(0)) maxim(0) = p_prime(0);
if (maxim(1) < p_prime(1)) maxim(1) = p_prime(1);
if (maxim(2) < p_prime(2)) maxim(2) = p_prime(2);
}
// set the center of the OBB to be the average of the
// minimum and maximum, and the extents be half of the
// difference between the minimum and maximum
const POINT center((maxim+minim)*TYPE(0.5));
m_pos = m_rot.transpose() * center;
m_ext = (maxim-minim)*TYPE(0.5);
} // Set
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildBegin()
{
m_rot = MATRIX::Zero();
m_pos = POINT::Zero();
m_ext = POINT::Zero();
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildAdd(const POINT& p)
{
// store mean in m_pos
m_pos += p;
// store covariance params in m_rot
m_rot(0,0) += p(0)*p(0);
m_rot(0,1) += p(0)*p(1);
m_rot(0,2) += p(0)*p(2);
m_rot(1,0) += p(1)*p(1);
m_rot(1,1) += p(1)*p(2);
m_rot(1,2) += p(2)*p(2);
// store count in m_ext
++(*((size_t*)m_ext.data()));
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::BuildEnd()
{
const TYPE invN(TYPE(1)/TYPE(*((size_t*)m_ext.data())));
const TYPE cxx = (m_rot(0,0) - m_pos(0)*m_pos(0)*invN)*invN;
const TYPE cxy = (m_rot(0,1) - m_pos(0)*m_pos(1)*invN)*invN;
const TYPE cxz = (m_rot(0,2) - m_pos(0)*m_pos(2)*invN)*invN;
const TYPE cyy = (m_rot(1,0) - m_pos(1)*m_pos(1)*invN)*invN;
const TYPE cyz = (m_rot(1,1) - m_pos(1)*m_pos(2)*invN)*invN;
const TYPE czz = (m_rot(1,2) - m_pos(2)*m_pos(2)*invN)*invN;
// now build the covariance matrix
MATRIX C;
C(0,0) = cxx; C(0,1) = cxy; C(0,2) = cxz;
C(1,0) = cxy; C(1,1) = cyy; C(1,2) = cyz;
C(2,0) = cxz; C(2,1) = cyz; C(2,2) = czz;
SetRotation(C);
} // Build
/*----------------------------------------------------------------*/
// check if the oriented bounding box has positive size
template <typename TYPE, int DIMS>
inline bool TOBB<TYPE,DIMS>::IsValid() const
{
return m_ext.minCoeff() > TYPE(0);
} // IsValid
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::Enlarge(TYPE x)
{
m_ext.array() += x;
return *this;
}
template <typename TYPE, int DIMS>
inline TOBB<TYPE,DIMS>& TOBB<TYPE,DIMS>::EnlargePercent(TYPE x)
{
m_ext *= x;
return *this;
} // Enlarge
/*----------------------------------------------------------------*/
// Update the box by the given pos delta.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Translate(const POINT& d)
{
m_pos += d;
}
// Update the box by the given transform.
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::Transform(const MATRIX& m)
{
m_rot = m * m_rot;
m_pos = m * m_pos;
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetCenter() const
{
return m_pos;
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetCenter(POINT& ptCenter) const
{
ptCenter = m_pos;
} // GetCenter
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::POINT TOBB<TYPE,DIMS>::GetSize() const
{
return m_ext*2;
}
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetSize(POINT& ptSize) const
{
ptSize = m_ext*2;
} // GetSize
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
inline void TOBB<TYPE,DIMS>::GetCorners(POINT pts[numCorners]) const
{
if (DIMS == 2) {
const POINT pEAxis[2] = {
m_rot.row(0)*m_ext[0],
m_rot.row(1)*m_ext[1]
};
const POINT pos(m_rot.transpose()*m_pos);
pts[0] = pos - pEAxis[0] - pEAxis[1];
pts[1] = pos + pEAxis[0] - pEAxis[1];
pts[2] = pos + pEAxis[0] + pEAxis[1];
pts[3] = pos - pEAxis[0] + pEAxis[1];
}
if (DIMS == 3) {
const POINT pEAxis[3] = {
m_rot.row(0)*m_ext[0],
m_rot.row(1)*m_ext[1],
m_rot.row(2)*m_ext[2]
};
const POINT pos(m_rot.transpose()*m_pos);
pts[0] = pos - pEAxis[0] - pEAxis[1] - pEAxis[2];
pts[1] = pos - pEAxis[0] - pEAxis[1] + pEAxis[2];
pts[2] = pos + pEAxis[0] - pEAxis[1] - pEAxis[2];
pts[3] = pos + pEAxis[0] - pEAxis[1] + pEAxis[2];
pts[4] = pos + pEAxis[0] + pEAxis[1] - pEAxis[2];
pts[5] = pos + pEAxis[0] + pEAxis[1] + pEAxis[2];
pts[6] = pos - pEAxis[0] + pEAxis[1] - pEAxis[2];
pts[7] = pos - pEAxis[0] + pEAxis[1] + pEAxis[2];
}
} // GetCorners
// constructs the corner of the aligned bounding box in world space
template <typename TYPE, int DIMS>
inline typename TOBB<TYPE,DIMS>::AABB TOBB<TYPE,DIMS>::GetAABB() const
{
POINT pts[numCorners];
GetCorners(pts);
return AABB(pts, numCorners);
} // GetAABB
/*----------------------------------------------------------------*/
// computes the volume of the OBB, which is a measure of
// how tight the fit is (better OBBs will have smaller volumes)
template <typename TYPE, int DIMS>
inline TYPE TOBB<TYPE,DIMS>::GetVolume() const
{
return m_ext.prod()*numCorners;
}
/*----------------------------------------------------------------*/
template <typename TYPE, int DIMS>
bool TOBB<TYPE,DIMS>::Intersects(const POINT& pt) const
{
const POINT dist(m_rot * (pt - m_pos));
if (DIMS == 2) {
return ABS(dist[0]) <= m_ext[0]
&& ABS(dist[1]) <= m_ext[1];
}
if (DIMS == 3) {
return ABS(dist[0]) <= m_ext[0]
&& ABS(dist[1]) <= m_ext[1]
&& ABS(dist[2]) <= m_ext[2];
}
} // Intersects(POINT)
/*----------------------------------------------------------------*/