00001 #ifndef OPENTISSUE_CORE_GEOMETRY_GEOMETRY_COMPUTE_SMALLEST_SPHERE_H
00002 #define OPENTISSUE_CORE_GEOMETRY_GEOMETRY_COMPUTE_SMALLEST_SPHERE_H
00003
00004
00005
00006
00007
00008
00009
00010 #include <OpenTissue/configuration.h>
00011
00012 #include <OpenTissue/core/math/math_constants.h>
00013 #include <OpenTissue/core/math/math_precision.h>
00014 #include <OpenTissue/core/geometry/geometry_circumscribed_sphere.h>
00015 #include <OpenTissue/collision/intersect/intersect_point_sphere.h>
00016
00017 #include <cmath>
00018 #include <cassert>
00019
00020 namespace OpenTissue
00021 {
00022 namespace geometry
00023 {
00024 namespace detail
00025 {
00026
00030 class ComputeSmallestSphere
00031 {
00032 private:
00033
00038 template<typename vector3_type_>
00039 class support_type
00040 {
00041 public:
00042
00043 typedef vector3_type_ vector3_type;
00044 typedef typename vector3_type::value_type real_type;
00045
00046 public:
00047
00048 int m_cnt;
00049 vector3_type m_points[4];
00050
00051 public:
00052
00053 bool contains (vector3_type const & point)
00054 {
00055 real_type epsilon = math::working_precision<real_type>(10);
00056
00057 for (int i = 0; i < m_cnt; ++i)
00058 {
00059 vector3_type diff = point - m_points[i];
00060 if ( diff*diff < epsilon )
00061 return true;
00062 }
00063 return false;
00064 }
00065 };
00066
00074 template<typename vector3_type, typename sphere_type, typename real_type>
00075 bool contains(vector3_type const & p, sphere_type const & sphere, real_type & squared_distance)
00076 {
00077 real_type test = sqr_length(p - sphere.center());
00078 squared_distance = test - sphere.squared_radius();
00079 return squared_distance <= 0.0;
00080 }
00081
00082 template<typename vector3_type, typename support_type, typename sphere_type>
00083 void update_support1 ( vector3_type const & point, support_type & support, sphere_type & minimal)
00084 {
00085 vector3_type const & p0 = support.m_points[0];
00086 vector3_type const & p1 = point;
00087 compute_circumscribed_sphere(p0,p1,minimal);
00088 support.m_cnt = 2;
00089 support.m_points[1] = p1;
00090 }
00091
00092 template<typename vector3_type, typename support_type, typename sphere_type>
00093 void update_support2 ( vector3_type const & point, support_type & support, sphere_type & minimal)
00094 {
00095 typedef typename vector3_type::value_type real_type;
00096
00097 static sphere_type spheres[2];
00098 vector3_type const & p0 = support.m_points[0];
00099 vector3_type const & p1 = support.m_points[1];
00100 vector3_type const & p2 = point;
00101 real_type min_radius = math::detail::highest<real_type>();
00102 int index = -1;
00103 compute_circumscribed_sphere(p0,p2,spheres[0]);
00104 compute_circumscribed_sphere(p1,p2,spheres[1]);
00105 if ( OpenTissue::intersect::point_sphere(p1, spheres[0] ) )
00106 {
00107 min_radius = spheres[0].radius();
00108 index = 0;
00109 }
00110 if ( spheres[1].radius() < min_radius )
00111 {
00112 if ( OpenTissue::intersect::point_sphere(p0, spheres[1]) )
00113 {
00114 min_radius = spheres[1].radius();
00115 index = 1;
00116 }
00117 }
00118 if ( index != -1 )
00119 {
00120 minimal = spheres[index];
00121 support.m_points[1-index] = p2;
00122 }
00123 else
00124 {
00125 compute_circumscribed_sphere(p0,p1,p2, minimal);
00126 assert(minimal.radius() < min_radius);
00127 support.m_cnt = 3;
00128 support.m_points[2] = p2;
00129 }
00130 }
00131
00132 template<typename vector3_type, typename support_type, typename sphere_type>
00133 void update_support3 ( vector3_type const & point, support_type & support, sphere_type & minimal)
00134 {
00135 typedef typename vector3_type::value_type real_type;
00136
00137 vector3_type const & p0 = support.m_points[0];
00138 vector3_type const & p1 = support.m_points[1];
00139 vector3_type const & p2 = support.m_points[2];
00140 vector3_type const & p3 = point;
00141
00142 sphere_type spheres[6];
00143 real_type min_radius = math::detail::highest<real_type>();
00144 int index = -1;
00145
00146 compute_circumscribed_sphere(p0,p3,spheres[0]);
00147 compute_circumscribed_sphere(p1,p3,spheres[1]);
00148 compute_circumscribed_sphere(p2,p3,spheres[2]);
00149 compute_circumscribed_sphere(p0,p1,p3,spheres[3]);
00150 compute_circumscribed_sphere(p0,p2,p3,spheres[4]);
00151 compute_circumscribed_sphere(p1,p2,p3,spheres[5]);
00152
00153 if ( OpenTissue::intersect::point_sphere(p1, spheres[0]) && OpenTissue::intersect::point_sphere( p2, spheres[0] ) )
00154 {
00155 min_radius = spheres[0].radius();
00156 index = 0;
00157 }
00158 if ( spheres[1].radius() < min_radius && OpenTissue::intersect::point_sphere(p0,spheres[1]) && OpenTissue::intersect::point_sphere(p2,spheres[1]) )
00159 {
00160 min_radius = spheres[1].radius();
00161 index = 1;
00162 }
00163 if ( spheres[2].radius() < min_radius && OpenTissue::intersect::point_sphere(p0, spheres[2]) && OpenTissue::intersect::point_sphere(p1,spheres[2]) )
00164 {
00165 min_radius = spheres[2].radius();
00166 index = 2;
00167 }
00168 if ( spheres[3].radius() < min_radius && OpenTissue::intersect::point_sphere(p2, spheres[3]) )
00169 {
00170 min_radius = spheres[3].radius();
00171 index = 3;
00172 }
00173 if ( spheres[4].radius() < min_radius && OpenTissue::intersect::point_sphere(p1,spheres[4]) )
00174 {
00175 min_radius = spheres[4].radius();
00176 index = 4;
00177 }
00178 if ( spheres[5].radius() < min_radius && OpenTissue::intersect::point_sphere(p0, spheres[5]) )
00179 {
00180 min_radius = spheres[5].radius();
00181 index = 5;
00182 }
00183
00184 switch ( index )
00185 {
00186 case 0:
00187 minimal = spheres[0];
00188 support.m_cnt = 2;
00189 support.m_points[1] = p3;
00190 break;
00191 case 1:
00192 minimal = spheres[1];
00193 support.m_cnt = 2;
00194 support.m_points[0] = p3;
00195 break;
00196 case 2:
00197 minimal = spheres[2];
00198 support.m_cnt = 2;
00199 support.m_points[0] = support.m_points[2];
00200 support.m_points[1] = p3;
00201 break;
00202 case 3:
00203 minimal = spheres[3];
00204 support.m_points[2] = p3;
00205 break;
00206 case 4:
00207 minimal = spheres[4];
00208 support.m_points[1] = p3;
00209 break;
00210 case 5:
00211 minimal = spheres[5];
00212 support.m_points[0] = p3;
00213 break;
00214 default:
00215 compute_circumscribed_sphere(p0,p1,p2,p3,minimal);
00216 assert(minimal.radius() < min_radius);
00217 support.m_cnt = 4;
00218 support.m_points[3] = p3;
00219 break;
00220 }
00221 }
00222
00223 template<typename vector3_type, typename support_type, typename sphere_type>
00224 void update_support4 ( vector3_type const & point, support_type & support, sphere_type & minimal)
00225 {
00226 typedef typename vector3_type::value_type real_type;
00227
00228 vector3_type const * p[4] = { &support.m_points[0], &support.m_points[1], &support.m_points[2], &support.m_points[3] };
00229 vector3_type const & p4 = point;
00230
00231
00232 int pi1[4][4] =
00233 {
00234 {0, 1,2,3},
00235 {1, 0,2,3},
00236 {2, 0,1,3},
00237 {3, 0,1,2}
00238 };
00239
00240
00241 int pi2[6][4] =
00242 {
00243 {0,1, 2,3},
00244 {0,2, 1,3},
00245 {0,3, 1,2},
00246 {1,2, 0,3},
00247 {1,3, 0,2},
00248 {2,3, 0,1}
00249 };
00250
00251
00252 int pi3[4][4] =
00253 {
00254 {0,1,2, 3},
00255 {0,1,3, 2},
00256 {0,2,3, 1},
00257 {1,2,3, 0}
00258 };
00259
00260 sphere_type spheres[14];
00261 real_type min_radius = math::detail::highest<real_type>();
00262 int index = -1;
00263 real_type min_distance = math::detail::highest<real_type>();
00264 real_type squared_distance;
00265 int iMinIndex = -1;
00266 int k = 0;
00267
00268
00269 int j;
00270 for (j = 0; j < 4; ++j, ++k)
00271 {
00272 compute_circumscribed_sphere(*p[pi1[j][0]],p4,spheres[k]);
00273
00274 if ( spheres[k].radius() < min_radius )
00275 {
00276 if ( contains(*p[pi1[j][1]],spheres[k],squared_distance) && contains(*p[pi1[j][2]],spheres[k],squared_distance) && contains(*p[pi1[j][3]],spheres[k],squared_distance))
00277 {
00278 min_radius = spheres[k].radius();
00279 index = k;
00280 }
00281 else if ( squared_distance < min_distance )
00282 {
00283 min_distance = squared_distance;
00284 iMinIndex = k;
00285 }
00286 }
00287 }
00288
00289
00290 for (j = 0; j < 6; ++j, ++k)
00291 {
00292 compute_circumscribed_sphere(*p[pi2[j][0]],*p[pi2[j][1]],p4,spheres[k]);
00293 if ( spheres[k].radius() < min_radius )
00294 {
00295 if ( contains( *p[pi2[j][2]],spheres[k],squared_distance) && contains(*p[pi2[j][3]],spheres[k],squared_distance) )
00296 {
00297 min_radius = spheres[k].radius();
00298 index = k;
00299 }
00300 else if ( squared_distance < min_distance )
00301 {
00302 min_distance = squared_distance;
00303 iMinIndex = k;
00304 }
00305 }
00306 }
00307
00308
00309 for (j = 0; j < 4; ++j, ++k)
00310 {
00311 compute_circumscribed_sphere(*p[pi3[j][0]],*p[pi3[j][1]],*p[pi3[j][2]],p4,spheres[k]);
00312 if ( spheres[k].radius() < min_radius )
00313 {
00314 if ( contains(*p[pi3[j][3]],spheres[k],squared_distance) )
00315 {
00316 min_radius = spheres[k].radius();
00317 index = k;
00318 }
00319 else if ( squared_distance < min_distance )
00320 {
00321 min_distance = squared_distance;
00322 iMinIndex = k;
00323 }
00324 }
00325 }
00326
00327
00328
00329
00330
00331 if ( index == -1 )
00332 index = iMinIndex;
00333
00334 minimal = spheres[index];
00335
00336 switch ( index )
00337 {
00338 case 0:
00339 support.m_cnt = 2;
00340 support.m_points[1] = p4;
00341 break;
00342 case 1:
00343 support.m_cnt = 2;
00344 support.m_points[0] = p4;
00345 break;
00346 case 2:
00347 support.m_cnt = 2;
00348 support.m_points[0] = support.m_points[2];
00349 support.m_points[1] = p4;
00350 break;
00351 case 3:
00352 support.m_cnt = 2;
00353 support.m_points[0] = support.m_points[3];
00354 support.m_points[1] = p4;
00355 break;
00356 case 4:
00357 support.m_cnt = 3;
00358 support.m_points[2] = p4;
00359 break;
00360 case 5:
00361 support.m_cnt = 3;
00362 support.m_points[1] = p4;
00363 break;
00364 case 6:
00365 support.m_cnt = 3;
00366 support.m_points[1] = support.m_points[3];
00367 support.m_points[2] = p4;
00368 break;
00369 case 7:
00370 support.m_cnt = 3;
00371 support.m_points[0] = p4;
00372 break;
00373 case 8:
00374 support.m_cnt = 3;
00375 support.m_points[0] = support.m_points[3];
00376 support.m_points[2] = p4;
00377 break;
00378 case 9:
00379 support.m_cnt = 3;
00380 support.m_points[0] = support.m_points[3];
00381 support.m_points[1] = p4;
00382 break;
00383 case 10:
00384 support.m_points[3] = p4;
00385 break;
00386 case 11:
00387 support.m_points[2] = p4;
00388 break;
00389 case 12:
00390 support.m_points[1] = p4;
00391 break;
00392 case 13:
00393 support.m_points[0] = p4;
00394 break;
00395 }
00396 }
00397
00398 public:
00399
00400 template<typename vector3_iterator, typename sphere_type>
00401 void operator()(vector3_iterator begin, vector3_iterator end, sphere_type & minimal)
00402 {
00403 assert(begin!=end || !"ComputeSmallestSphere::operator(): begin was equal to end");
00404
00405 typedef typename sphere_type::real_type real_type;
00406 typedef typename sphere_type::vector3_type vector3_type;
00407 support_type<vector3_type> support;
00408
00409
00410
00411
00412
00413 vector3_iterator i = begin;
00414 compute_circumscribed_sphere(*i, minimal);
00415
00416 support.m_cnt = 1;
00417 support.m_points[0] = (*i); ++i;
00418
00419 while ( i != end )
00420 {
00421 if ( !support.contains( (*i) ) )
00422 {
00423 if ( !OpenTissue::intersect::point_sphere( (*i), minimal ) )
00424 {
00425 sphere_type sphere;
00426 switch(support.m_cnt)
00427 {
00428 case 1: update_support1( (*i), support, sphere); break;
00429 case 2: update_support2( (*i), support, sphere); break;
00430 case 3: update_support3( (*i), support, sphere); break;
00431 case 4: update_support4( (*i), support, sphere); break;
00432 default: break;
00433 }
00434 if ( sphere.radius() > minimal.radius() )
00435 {
00436 minimal = sphere;
00437 i = begin;
00438 continue;
00439 }
00440 }
00441 }
00442 ++i;
00443 }
00444 }
00445
00446 };
00447
00448 }
00449
00457 template<typename vector3_iterator, typename sphere_type>
00458 void compute_smallest_sphere(vector3_iterator begin, vector3_iterator end, sphere_type & sphere)
00459 {
00460 assert(begin!=end || !"compute_smallest_sphere(): begin was equal to end");
00461
00462 detail::ComputeSmallestSphere()(begin,end,sphere);
00463 }
00464
00465 }
00466 }
00467
00468
00469 #endif