Sphere Sphere::OptimalEnclosingSphere(const vec &a, const vec &b, const vec &c, const vec &d, const vec &e, int &redundantPoint) { Sphere s = OptimalEnclosingSphere(b,c,d,e); if (s.Contains(a, sEpsilon)) { redundantPoint = 0; return s; } s = OptimalEnclosingSphere(a,c,d,e); if (s.Contains(b, sEpsilon)) { redundantPoint = 1; return s; } s = OptimalEnclosingSphere(a,b,d,e); if (s.Contains(c, sEpsilon)) { redundantPoint = 2; return s; } s = OptimalEnclosingSphere(a,b,c,e); if (s.Contains(d, sEpsilon)) { redundantPoint = 3; return s; } s = OptimalEnclosingSphere(a,b,c,d); mathassert(s.Contains(e, sEpsilon)); redundantPoint = 4; return s; }
Sphere Sphere::OptimalEnclosingSphere(const vec &a, const vec &b) { Sphere s; s.pos = (a + b) * 0.5f; s.r = (b - s.pos).Length(); assume(s.pos.IsFinite()); assume(s.r >= 0.f); // Allow floating point inconsistency and expand the radius by a small epsilon so that the containment tests // really contain the points (note that the points must be sufficiently near enough to the origin) s.r += sEpsilon; mathassert(s.Contains(a)); mathassert(s.Contains(b)); return s; }
/** For reference, see http://realtimecollisiondetection.net/blog/?p=20 . */ Sphere Sphere::OptimalEnclosingSphere(const vec &a, const vec &b, const vec &c, const vec &d) { Sphere sphere; float s,t,u; const vec ab = b-a; const vec ac = c-a; const vec ad = d-a; bool success = FitSphereThroughPoints(ab, ac, ad, s, t, u); if (!success || s < 0.f || t < 0.f || u < 0.f || s+t+u > 1.f) { sphere = OptimalEnclosingSphere(a,b,c); if (!sphere.Contains(d)) { sphere = OptimalEnclosingSphere(a,b,d); if (!sphere.Contains(c)) { sphere = OptimalEnclosingSphere(a,c,d); if (!sphere.Contains(b)) { sphere = OptimalEnclosingSphere(b,c,d); sphere.r = Max(sphere.r, a.Distance(sphere.pos) + 1e-3f); // For numerical stability, expand the radius of the sphere so it certainly contains the fourth point. assume(sphere.Contains(a)); } } } } /* // Note: Trying to approach the problem like this, like was in the triangle case, is flawed: if (s < 0.f) sphere = OptimalEnclosingSphere(a, c, d); else if (t < 0.f) sphere = OptimalEnclosingSphere(a, b, d); else if (u < 0.f) sphere = OptimalEnclosingSphere(a, b, c); else if (s + t + u > 1.f) sphere = OptimalEnclosingSphere(b, c, d); */ else // The fitted sphere is inside the convex hull of the vertices (a,b,c,d), so it must be optimal. { const vec center = s*ab + t*ac + u*ad; sphere.pos = a + center; // Mathematically, the following would be correct, but it suffers from floating point inaccuracies, // since it only tests distance against one point. //sphere.r = center.Length(); // For robustness, take the radius to be the distance to the farthest point (though the distance are all // equal). sphere.r = Sqrt(Max(sphere.pos.DistanceSq(a), sphere.pos.DistanceSq(b), sphere.pos.DistanceSq(c), sphere.pos.DistanceSq(d))); } // Allow floating point inconsistency and expand the radius by a small epsilon so that the containment tests // really contain the points (note that the points must be sufficiently near enough to the origin) sphere.r += 2.f*sEpsilon; // We test against one epsilon, so expand using 2 epsilons. #ifdef MATH_ASSERT_CORRECTNESS if (!sphere.Contains(a, sEpsilon) || !sphere.Contains(b, sEpsilon) || !sphere.Contains(c, sEpsilon) || !sphere.Contains(d, sEpsilon)) { LOGE("Pos: %s, r: %f", sphere.pos.ToString().c_str(), sphere.r); LOGE("A: %s, dist: %f", a.ToString().c_str(), a.Distance(sphere.pos)); LOGE("B: %s, dist: %f", b.ToString().c_str(), b.Distance(sphere.pos)); LOGE("C: %s, dist: %f", c.ToString().c_str(), c.Distance(sphere.pos)); LOGE("D: %s, dist: %f", d.ToString().c_str(), d.Distance(sphere.pos)); mathassert(false); } #endif return sphere; }
/** For reference, see http://realtimecollisiondetection.net/blog/?p=20 . */ Sphere Sphere::OptimalEnclosingSphere(const vec &a, const vec &b, const vec &c) { Sphere sphere; vec ab = b-a; vec ac = c-a; float s, t; bool areCollinear = ab.Cross(ac).LengthSq() < 1e-4f; // Manually test that we don't try to fit sphere to three collinear points. bool success = !areCollinear && FitSphereThroughPoints(ab, ac, s, t); if (!success || Abs(s) > 10000.f || Abs(t) > 10000.f) // If s and t are very far from the triangle, do a manual box fitting for numerical stability. { vec minPt = Min(a, b, c); vec maxPt = Max(a, b, c); sphere.pos = (minPt + maxPt) * 0.5f; sphere.r = sphere.pos.Distance(minPt); } else if (s < 0.f) { sphere.pos = (a + c) * 0.5f; sphere.r = a.Distance(c) * 0.5f; sphere.r = Max(sphere.r, b.Distance(sphere.pos)); // For numerical stability, expand the radius of the sphere so it certainly contains the third point. } else if (t < 0.f) { sphere.pos = (a + b) * 0.5f; sphere.r = a.Distance(b) * 0.5f; sphere.r = Max(sphere.r, c.Distance(sphere.pos)); // For numerical stability, expand the radius of the sphere so it certainly contains the third point. } else if (s+t > 1.f) { sphere.pos = (b + c) * 0.5f; sphere.r = b.Distance(c) * 0.5f; sphere.r = Max(sphere.r, a.Distance(sphere.pos)); // For numerical stability, expand the radius of the sphere so it certainly contains the third point. } else { const vec center = s*ab + t*ac; sphere.pos = a + center; // Mathematically, the following would be correct, but it suffers from floating point inaccuracies, // since it only tests distance against one point. //sphere.r = center.Length(); // For robustness, take the radius to be the distance to the farthest point (though the distance are all // equal). sphere.r = Sqrt(Max(sphere.pos.DistanceSq(a), sphere.pos.DistanceSq(b), sphere.pos.DistanceSq(c))); } // Allow floating point inconsistency and expand the radius by a small epsilon so that the containment tests // really contain the points (note that the points must be sufficiently near enough to the origin) sphere.r += 2.f * sEpsilon; // We test against one epsilon, so expand by two epsilons. #ifdef MATH_ASSERT_CORRECTNESS if (!sphere.Contains(a, sEpsilon) || !sphere.Contains(b, sEpsilon) || !sphere.Contains(c, sEpsilon)) { LOGE("Pos: %s, r: %f", sphere.pos.ToString().c_str(), sphere.r); LOGE("A: %s, dist: %f", a.ToString().c_str(), a.Distance(sphere.pos)); LOGE("B: %s, dist: %f", b.ToString().c_str(), b.Distance(sphere.pos)); LOGE("C: %s, dist: %f", c.ToString().c_str(), c.Distance(sphere.pos)); mathassert(false); } #endif return sphere; }