Хоча це давня нитка, я подумав, що для нащадків може бути непогано мати трохи посилання. Джерело формули - з " Геометричних інструментів комп'ютерної графіки " Філіпа Дж. Шнайдера та Девіда Х. Еберлі. Щось зауважити, згідно тексту
Тетраедр V0, V1, V2, V3 впорядкований так, щоб він був ізоморфним канонічному (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1 ).
Як я розумію ізоморфізм , у геометрії може бути кілька різних значень. Якщо він означає ізоморфний щодо теорії графів, то наступний код повинен вести себе правильно, оскільки топологія будь-якого тетраедра однакова (К4, повний графік). Я перевіряв результати функції проти вольфрам-альфа, використовуючи різні перестановки в упорядкуванні канонічних вершин, і не бачив різниці в результаті. Якщо впорядкування виявляється проблемою, я пропоную вивчити норму трикутника, утвореного вершинами V1, V2, V3 при введенні в цю функцію, і обробляти точки, як напівпростір, за допомогою тесту крапкового добутку, щоб з'ясувати якщо цей трикутник звернений у потрібний бік. Якщо ні, простийstd::swap
будь-яких двох вершин трикутника буде змінювати напрямок нормального, і ви можете продовжувати. Але, як я вже говорив, я не бачив різниці в різних перестановках.
Ось перекладений код без використання матриць, щоб уникнути будь-якої плутанини в реалізації, це досить прямо;
void Circumsphere(const Vec3& v0, const Vec3& v1, const Vec3& v2, const Vec3& v3, Vec3* center, float* radius)
{
//Create the rows of our "unrolled" 3x3 matrix
Vec3 Row1 = v1 - v0;
float sqLength1 = length2(Row1);
Vec3 Row2 = v2 - v0;
float sqLength2 = length2(Row2);
Vec3 Row3 = v3 - v0;
float sqLength3 = length2(Row3);
//Compute the determinant of said matrix
const float determinant = Row1.x * (Row2.y * Row3.z - Row3.y * Row2.z)
- Row2.x * (Row1.y * Row3.z - Row3.y * Row1.z)
+ Row3.x * (Row1.y * Row2.z - Row2.y * Row1.z);
// Compute the volume of the tetrahedron, and precompute a scalar quantity for re-use in the formula
const float volume = determinant / 6.f;
const float iTwelveVolume = 1.f / (volume * 12.f);
center->x = v0.x + iTwelveVolume * ( ( Row2.y * Row3.z - Row3.y * Row2.z) * sqLength1 - (Row1.y * Row3.z - Row3.y * Row1.z) * sqLength2 + (Row1.y * Row2.z - Row2.y * Row1.z) * sqLength3 );
center->y = v0.y + iTwelveVolume * (-( Row2.x * Row3.z - Row3.x * Row2.z) * sqLength1 + (Row1.x * Row3.z - Row3.x * Row1.z) * sqLength2 - (Row1.x * Row2.z - Row2.x * Row1.z) * sqLength3 );
center->z = v0.z + iTwelveVolume * ( ( Row2.x * Row3.y - Row3.x * Row2.y) * sqLength1 - (Row1.x * Row3.y - Row3.x * Row1.y) * sqLength2 + (Row1.x * Row2.y - Row2.x * Row1.y) * sqLength3 );
//Once we know the center, the radius is clearly the distance to any vertex
*radius = length(*center - v0);
}