00001 /************************************************************** ggt-head beg 00002 * 00003 * GGT: Generic Graphics Toolkit 00004 * 00005 * Original Authors: 00006 * Allen Bierbaum 00007 * 00008 * ----------------------------------------------------------------- 00009 * File: $RCSfile: Containment.h,v $ 00010 * Date modified: $Date: 2003/01/14 20:51:25 $ 00011 * Version: $Revision: 1.14 $ 00012 * ----------------------------------------------------------------- 00013 * 00014 *********************************************************** ggt-head end */ 00015 /*************************************************************** ggt-cpr beg 00016 * 00017 * GGT: The Generic Graphics Toolkit 00018 * Copyright (C) 2001,2002 Allen Bierbaum 00019 * 00020 * This library is free software; you can redistribute it and/or 00021 * modify it under the terms of the GNU Lesser General Public 00022 * License as published by the Free Software Foundation; either 00023 * version 2.1 of the License, or (at your option) any later version. 00024 * 00025 * This library is distributed in the hope that it will be useful, 00026 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00027 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00028 * Lesser General Public License for more details. 00029 * 00030 * You should have received a copy of the GNU Lesser General Public 00031 * License along with this library; if not, write to the Free Software 00032 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00033 * 00034 ************************************************************ ggt-cpr end */ 00035 #ifndef _GMTL_CONTAINMENT_H_ 00036 #define _GMTL_CONTAINMENT_H_ 00037 00038 // new stuff 00039 #include <vector> 00040 #include <gmtl/Sphere.h> 00041 #include <gmtl/AABox.h> 00042 #include <gmtl/VecOps.h> 00043 00044 // old stuff 00045 //#include <gmtl/OOBox.h> 00046 //#include <gmtl/AABox.h> 00047 //#include <gmtl/Fit/GaussPointsFit.h> 00048 //#include <gmtl/matVecFuncs.h> 00049 //#include <gmtl/Quat.h> 00050 00051 namespace gmtl 00052 { 00053 00054 //----------------------------------------------------------------------------- 00055 // Sphere 00056 //----------------------------------------------------------------------------- 00057 00067 template< class DATA_TYPE > 00068 bool isInVolume( const Sphere<DATA_TYPE>& container, 00069 const Point<DATA_TYPE, 3>& pt ) 00070 { 00071 // The point is inside the sphere if the vector computed from the center of 00072 // the sphere to the point has a magnitude less than or equal to the radius 00073 // of the sphere. 00074 // |pt - center| <= radius 00075 return ( length(pt - container.mCenter) <= container.mRadius ); 00076 } 00077 00087 template< class DATA_TYPE > 00088 bool isInVolume( const Sphere<DATA_TYPE>& container, 00089 const Sphere<DATA_TYPE>& sphere ) 00090 { 00091 // the sphere is inside container if the distance between the centers of the 00092 // spheres plus the radius of the inner sphere is less than or equal to the 00093 // radius of the containing sphere. 00094 // |sphere.center - container.center| + sphere.radius <= container.radius 00095 return ( length(sphere.mCenter - container.mCenter) + sphere.mRadius 00096 <= container.mRadius ); 00097 } 00098 00105 template< class DATA_TYPE > 00106 void extendVolume( Sphere<DATA_TYPE>& container, 00107 const Point<DATA_TYPE, 3>& pt ) 00108 { 00109 // check if we already contain the point 00110 if ( isInVolume( container, pt ) ) 00111 { 00112 return; 00113 } 00114 00115 // make a vector pointing from the center of the sphere to pt. this is the 00116 // direction in which we need to move the sphere's center 00117 Vec<DATA_TYPE, 3> dir = pt - container.mCenter; 00118 DATA_TYPE len = normalize( dir ); 00119 00120 // compute what the new radius should be 00121 DATA_TYPE newRadius = (len + container.mRadius) * DATA_TYPE(0.5); 00122 00123 // compute the new center for the sphere 00124 Point<DATA_TYPE, 3> newCenter = container.mCenter + 00125 (dir * (newRadius - container.mRadius)); 00126 00127 // modify container to its new values 00128 container.mCenter = newCenter; 00129 container.mRadius = newRadius; 00130 } 00131 00138 template< class DATA_TYPE > 00139 void extendVolume( Sphere<DATA_TYPE>& container, 00140 const Sphere<DATA_TYPE>& sphere ) 00141 { 00142 // check if we already contain the sphere 00143 if ( isInVolume( container, sphere ) ) 00144 { 00145 return; 00146 } 00147 00148 // make a vector pointing from the center of container to sphere. this is the 00149 // direction in which we need to move container's center 00150 Vec<DATA_TYPE, 3> dir = sphere.mCenter - container.mCenter; 00151 DATA_TYPE len = normalize( dir ); 00152 00153 // compute what the new radius should be 00154 DATA_TYPE newRadius = (len + sphere.mRadius + container.mRadius) * 00155 DATA_TYPE(0.5); 00156 00157 // compute the new center for container 00158 Point<DATA_TYPE, 3> newCenter = container.mCenter + 00159 (dir * (newRadius - container.mRadius)); 00160 00161 // modify container to its new values 00162 container.mCenter = newCenter; 00163 container.mRadius = newRadius; 00164 } 00165 00176 template< class DATA_TYPE > 00177 void makeVolume( Sphere<DATA_TYPE>& container, 00178 const std::vector< Point<DATA_TYPE, 3> >& pts ) 00179 { 00180 gmtlASSERT( pts.size() > 0 && "pts must contain at least 1 point" ); 00181 00182 // Implementation based on the Sphere Centered at Average of Points algorithm 00183 // found in "3D Game Engine Design" by Devud G, Eberly (pg. 27) 00184 typename std::vector< Point<DATA_TYPE, 3> >::const_iterator itr = pts.begin(); 00185 00186 // compute the average of the points as the center 00187 Point<DATA_TYPE, 3> sum = *itr; 00188 ++itr; 00189 while ( itr != pts.end() ) 00190 { 00191 sum += *itr; 00192 ++itr; 00193 } 00194 container.mCenter = sum / pts.size(); 00195 00196 // compute the distance from the computed center to point furthest from that 00197 // center as the radius 00198 DATA_TYPE radiusSqr(0); 00199 for ( itr = pts.begin(); itr != pts.end(); ++itr ) 00200 { 00201 float len = lengthSquared( *itr - container.mCenter ); 00202 if ( len > radiusSqr ) 00203 radiusSqr = len; 00204 } 00205 00206 container.mRadius = Math::sqrt( radiusSqr ); 00207 } 00208 00209 /* 00210 template< class DATA_TYPE > 00211 void makeVolume( Sphere<DATA_TYPE>& container, 00212 const std::vector< Point<DATA_TYPE, 3> >& pts ) 00213 { 00214 gmtlASSERT( pts.size() > 1 && "pts must contain at least 2 points" ); 00215 00216 // make a sphere around the first 2 points and then extend the sphere by each 00217 // point thereafter. we could probably be smarter about this ... 00218 std::vector< Point<DATA_TYPE, 3> >::const_iterator itr = pts.begin(); 00219 00220 // make the initial sphere 00221 const Point<DATA_TYPE, 3>& first = *itr; 00222 ++itr; 00223 const Point<DATA_TYPE, 3>& second = *itr; 00224 ++itr; 00225 const Vec<DATA_TYPE, 3> dir = second - first; 00226 container.mRadius = length(dir) * DATA_TYPE(0.5); 00227 container.mCenter = first + (dir * container.mRadius); 00228 00229 // iterate through the remaining points and extend the container to fit each 00230 // point. yay code reuse! 00231 while ( itr != pts.end() ) 00232 { 00233 extendVolume( container, *itr ); 00234 ++itr; 00235 } 00236 } 00237 */ 00248 /* 00249 template< class DATA_TYPE > 00250 void makeVolume( Sphere<DATA_TYPE>& container, 00251 const std::vector< Sphere<DATA_TYPE> >& spheres ) 00252 { 00253 gmtlASSERT( spheres.size() > 1 && "spheres must contain at least 2 points" ); 00254 00255 // make a sphere around the first 2 points and then extend the sphere by each 00256 // point thereafter. we could probably be smarter about this ... 00257 std::vector< Sphere<DATA_TYPE> >::const_iterator itr = spheres.begin(); 00258 00259 // make the initial sphere 00260 const Sphere<DATA_TYPE>& first = *itr; 00261 ++itr; 00262 const Sphere<DATA_TYPE>& second = *itr; 00263 ++itr; 00264 const Vec<DATA_TYPE, 3> dir = second.mCenter - first.mCenter; 00265 container.mRadius = (length(dir) + first.mRadius + second.mRadius) * 00266 DATA_TYPE(0.5); 00267 container.mCenter = first.mCenter + 00268 (dir * (container.mRadius - first.mRadius)); 00269 00270 // iterate through the remaining points and extend the container to fit each 00271 // point. yay code reuse! 00272 while ( itr != spheres.end() ) 00273 { 00274 extendVolume( container, *itr ); 00275 ++itr; 00276 } 00277 } 00278 */ 00288 template< class DATA_TYPE > 00289 bool isOnVolume( const Sphere<DATA_TYPE>& container, 00290 const Point<DATA_TYPE, 3>& pt ) 00291 { 00292 // |center - pt| - radius == 0 00293 return ( length(container.mCenter - pt) - container.mRadius == 0 ); 00294 } 00295 00306 template< class DATA_TYPE > 00307 bool isOnVolume( const Sphere<DATA_TYPE>& container, 00308 const Point<DATA_TYPE, 3>& pt, 00309 const DATA_TYPE& tol ) 00310 { 00311 gmtlASSERT( tol >= 0 && "tolerance must be positive" ); 00312 00313 // abs( |center-pt| - radius ) < tol 00314 return ( Math::abs( length(container.mCenter - pt) - container.mRadius ) 00315 <= tol ); 00316 } 00317 00318 //----------------------------------------------------------------------------- 00319 // AABox 00320 //----------------------------------------------------------------------------- 00321 00331 template< class DATA_TYPE > 00332 bool isInVolume(const AABox<DATA_TYPE>& container, 00333 const Point<DATA_TYPE, 3>& pt) 00334 { 00335 if (! container.isEmpty()) 00336 { 00337 return ( pt[0] >= container.mMin[0] && 00338 pt[1] >= container.mMin[1] && 00339 pt[2] >= container.mMin[2] && 00340 pt[0] <= container.mMax[0] && 00341 pt[1] <= container.mMax[1] && 00342 pt[2] <= container.mMax[2]); 00343 } 00344 else 00345 { 00346 return false; 00347 } 00348 } 00349 00359 template< class DATA_TYPE > 00360 bool isInVolume(const AABox<DATA_TYPE>& container, 00361 const AABox<DATA_TYPE>& box) 00362 { 00363 // Empty boxes don't overlap 00364 if (container.isEmpty() || box.isEmpty()) 00365 { 00366 return false; 00367 } 00368 00369 // Test that the boxes are not overlapping on any axis 00370 if (container.mMax[0] < box.mMin[0] || container.mMin[0] > box.mMax[0] || 00371 container.mMax[1] < box.mMin[1] || container.mMin[1] > box.mMax[1] || 00372 container.mMax[2] < box.mMin[2] || container.mMin[2] > box.mMax[2]) 00373 { 00374 return false; 00375 } 00376 else 00377 { 00378 return true; 00379 } 00380 } 00381 00388 template< class DATA_TYPE > 00389 void extendVolume(AABox<DATA_TYPE>& container, 00390 const Point<DATA_TYPE, 3>& pt) 00391 { 00392 if (! container.isEmpty()) 00393 { 00394 // X coord 00395 if (pt[0] > container.mMax[0]) 00396 { 00397 container.mMax[0] = pt[0]; 00398 } 00399 else if (pt[0] < container.mMin[0]) 00400 { 00401 container.mMin[0] = pt[0]; 00402 } 00403 00404 // Y coord 00405 if (pt[1] > container.mMax[1]) 00406 { 00407 container.mMax[1] = pt[1]; 00408 } 00409 else if (pt[1] < container.mMin[1]) 00410 { 00411 container.mMin[1] = pt[1]; 00412 } 00413 00414 // Z coord 00415 if (pt[2] > container.mMax[2]) 00416 { 00417 container.mMax[2] = pt[2]; 00418 } 00419 else if (pt[2] < container.mMin[2]) 00420 { 00421 container.mMin[2] = pt[2]; 00422 } 00423 } 00424 else 00425 { 00426 // Make a box with essentially zero volume at the point 00427 container.setMin(pt); 00428 container.setMax(pt); 00429 container.setEmpty(false); 00430 } 00431 } 00432 00439 template< class DATA_TYPE > 00440 void extendVolume(AABox<DATA_TYPE>& container, 00441 const AABox<DATA_TYPE>& box) 00442 { 00443 // Can't extend by an empty box 00444 if (box.isEmpty()) 00445 { 00446 return; 00447 } 00448 00449 // An empty container is extended to be the box 00450 if (container.isEmpty()) 00451 { 00452 container = box; 00453 } 00454 00455 // Just extend by the corners of the box 00456 extendVolume(container, box.getMin()); 00457 extendVolume(container, box.getMax()); 00458 } 00459 00465 template< class DATA_TYPE > 00466 void makeVolume(AABox<DATA_TYPE>& box, const Sphere<DATA_TYPE>& sph) 00467 { 00468 const gmtl::Point<DATA_TYPE, 3>& center = sph.getCenter(); 00469 const DATA_TYPE& radius = sph.getRadius(); 00470 00471 // Calculate the min and max points for the box 00472 gmtl::Point<DATA_TYPE, 3> min_pt(center[0] - radius, 00473 center[1] - radius, 00474 center[2] - radius); 00475 gmtl::Point<DATA_TYPE, 3> max_pt(center[0] + radius, 00476 center[1] + radius, 00477 center[2] + radius); 00478 00479 box.setMin(min_pt); 00480 box.setMax(max_pt); 00481 box.setEmpty(radius == DATA_TYPE(0)); 00482 } 00483 00484 /* 00485 //------------------------------------------------------------------------------ 00486 // Begin old GMTL code 00487 //------------------------------------------------------------------------------ 00488 inline void computeContainment( AABox& box, const std::vector<gmtl::Point3>& points, 00489 Point3& minPt, Point3& maxPt ) 00490 //void MgcContAlignedBox (int iQuantity, const MgcVector3* akPoint, 00491 // MgcVector3& rkMin, MgcVector3& rkMax) 00492 { 00493 if (points.empty()) 00494 return; 00495 00496 minPt = points[0]; 00497 maxPt = minPt; 00498 00499 for (unsigned i = 1; i < points.size(); i++) 00500 { 00501 if ( points[i][Xelt] < minPt[Xelt] ) 00502 minPt[Xelt] = points[i][Xelt]; 00503 else if ( points[i][Xelt] > maxPt[Xelt] ) 00504 maxPt[Xelt] = points[i][Xelt]; 00505 00506 if ( points[i][Yelt] < minPt[Yelt] ) 00507 minPt[Yelt] = points[i][Yelt]; 00508 else if ( points[i][Yelt] > maxPt[Yelt] ) 00509 maxPt[Yelt] = points[i][Yelt]; 00510 00511 if ( points[i][Zelt] < minPt[Zelt] ) 00512 minPt[Zelt] = points[i][Zelt]; 00513 else if ( points[i][Zelt] > maxPt[Zelt] ) 00514 maxPt[Zelt] = points[i][Zelt]; 00515 } 00516 00517 // Now update the box 00518 box.makeEmpty(); 00519 box.mMax = maxPt; 00520 box.mMin = minPt; 00521 } 00522 //---------------------------------------------------------------------------- 00523 //---------------------------------------------------------------------------- 00524 //MgcBox3 MgcContOrientedBox (int iQuantity, const MgcVector3* akPoint) 00525 inline void computeContainment( OOBox& box, const std::vector<gmtl::Point3>& points) 00526 { 00527 //MgcGaussPointsFit(iQuantity,akPoint,kBox.Center(),kBox.Axes(), 00528 // kBox.Extents()); 00529 00530 gmtl::GaussPointsFit(points.size(), &points[0], box.center(), box.axes(), box.halfLens()); 00531 00532 // Let C be the box center and let U0, U1, and U2 be the box axes. Each 00533 // input point is of the form X = C + y0*U0 + y1*U1 + y2*U2. The 00534 // following code computes min(y0), max(y0), min(y1), max(y1), min(y2), 00535 // and max(y2). The box center is then adjusted to be 00536 // C' = C + 0.5*(min(y0)+max(y0))*U0 + 0.5*(min(y1)+max(y1))*U1 + 00537 // 0.5*(min(y2)+max(y2))*U2 00538 #ifdef _DEBUG 00539 gmtl::OOBox box_test; 00540 box_test = box; 00541 gmtl::Vec3 ax0 = box_test.axis(0); 00542 gmtl::Vec3 ax1 = box_test.axis(1); 00543 gmtl::Vec3 ax2 = box_test.axis(2); 00544 #endif 00545 00546 gmtlASSERT(box.axis(0).isNormalized()); 00547 gmtlASSERT(box.axis(1).isNormalized()); 00548 gmtlASSERT(box.axis(2).isNormalized()); 00549 00550 // XXX: Sign is sometimes wrong here 00551 // This is hack code to make it "work right" 00552 Vec3 cross; 00553 cross = box.axis(0).cross(box.axis(1)); 00554 cross.normalize(); 00555 box.axis(2) = cross; 00556 00557 Vec3 kDiff = points[0] - box.center(); 00558 float fY0Min = kDiff.dot(box.axis(0)), fY0Max = fY0Min; 00559 float fY1Min = kDiff.dot(box.axis(1)), fY1Max = fY1Min; 00560 float fY2Min = kDiff.dot(box.axis(2)), fY2Max = fY2Min; 00561 00562 float fY0, fY1, fY2; 00563 00564 for (unsigned i = 1; i < points.size(); i++) 00565 { 00566 kDiff = points[i] - box.center(); 00567 00568 fY0 = kDiff.dot(box.axis(0)); 00569 if ( fY0 < fY0Min ) 00570 fY0Min = fY0; 00571 else if ( fY0 > fY0Max ) 00572 fY0Max = fY0; 00573 00574 fY1 = kDiff.dot(box.axis(1)); 00575 if ( fY1 < fY1Min ) 00576 fY1Min = fY1; 00577 else if ( fY1 > fY1Max ) 00578 fY1Max = fY1; 00579 00580 fY2 = kDiff.dot(box.axis(2)); 00581 if ( fY2 < fY2Min ) 00582 fY2Min = fY2; 00583 else if ( fY2 > fY2Max ) 00584 fY2Max = fY2; 00585 } 00586 00587 box.center() += (0.5*(fY0Min+fY0Max))*box.axis(0) + 00588 (0.5*(fY1Min+fY1Max))*box.axis(1) + 00589 (0.5*(fY2Min+fY2Max))*box.axis(2); 00590 00591 box.halfLen(0) = 0.5*(fY0Max - fY0Min); 00592 box.halfLen(1) = 0.5*(fY1Max - fY1Min); 00593 box.halfLen(2) = 0.5*(fY2Max - fY2Min); 00594 } 00595 00596 */ 00597 /* 00598 inline void computeContainment (OOBox& out_box, const OOBox& box0, const OOBox& box1, bool fast=true) 00599 { 00600 gmtl::OOBox ret_box; // The resulting box 00601 00602 if (fast) 00603 { 00604 ret_box.center() = 0.5*(box0.center() + box1.center()); // Center at average 00605 00606 // Average the quats to get a new orientation 00607 Quat quat0, quat1; 00608 quat0.makeAxes(box0.axis(0), box0.axis(1), box0.axis(2)); 00609 quat1.makeAxes(box1.axis(0), box1.axis(1), box1.axis(2)); 00610 if ( quat0.dot(quat1) < 0.0 ) 00611 quat1 = -quat1; 00612 00613 Quat full_quat = quat0 + quat1; 00614 00615 //float inv_len = 1.0/Math::sqrt(full_quat.norm()); 00616 //full_quat = inv_len*full_quat; 00617 full_quat.normalize(); 00618 full_quat.getAxes(ret_box.axis(0), ret_box.axis(1), ret_box.axis(2)); 00619 00620 // Now that we have new orientation, extend half lens to cover the volume 00621 // 00622 unsigned i, j; 00623 Point3 verts[8]; 00624 Vec3 diff; 00625 float aDot; 00626 00627 ret_box.halfLen(0) = 0.0; 00628 ret_box.halfLen(1) = 0.0; 00629 ret_box.halfLen(2) = 0.0; 00630 00631 box0.getVerts(verts); 00632 for (i = 0; i < 8; i++) 00633 { 00634 diff = verts[i] - ret_box.center(); 00635 for (j = 0; j < 3; j++) // For each axis of box0 00636 { 00637 aDot = Math::abs(diff.dot(ret_box.axis(j))); 00638 if ( aDot > ret_box.halfLen(j) ) 00639 ret_box.halfLen(j) = aDot; 00640 } 00641 } 00642 00643 box1.getVerts(verts); 00644 for (i = 0; i < 8; i++) 00645 { 00646 diff = verts[i] - ret_box.center(); 00647 for (j = 0; j < 3; j++) 00648 { 00649 aDot = Math::abs(diff.dot(ret_box.axis(j))); 00650 if ( aDot > ret_box.halfLen(j) ) 00651 ret_box.halfLen(j) = aDot; 00652 } 00653 } 00654 } 00655 else // Tighter fit 00656 { 00657 // Hack that will do it correctly, but is slow 00658 Point3 verts[8]; 00659 std::vector<gmtl::Point3> vert_points; 00660 00661 box0.getVerts(verts); 00662 for (unsigned i=0;i<8;i++) vert_points.push_back(verts[i]); 00663 box1.getVerts(verts); 00664 for (unsigned i=0;i<8;i++) vert_points.push_back(verts[i]); 00665 00666 computeContainment(ret_box, vert_points); 00667 } 00668 00669 out_box = ret_box; 00670 } 00671 */ 00672 00673 00674 } 00675 00676 #endif