Хотя это древняя нить, я подумал, что для потомков было бы неплохо немного упомянуть. Источник формулы взят из « Геометрических инструментов для компьютерной графики » Филиппа Шнайдера и Дэвида Х. Эберли. Что-то отметить, согласно тексту
Тетраэдр V0, V1, V2, V3 упорядочен так, что он изоморфен каноническому (0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1 )
Как я понимаю, изоморфизм может иметь несколько различных значений при использовании в геометрии. Если он имеет в виду изоморфность в отношении теории графов, то следующий код должен вести себя корректно, поскольку топология любого тетраэдра одинакова (K4, полный граф). Я проверил результаты функции против альфа-вольфрама, используя различные перестановки в упорядочении канонических вершин, и я не увидел различий в результате. Если упорядочение оказывается проблемой, я предлагаю исследовать нормаль треугольника, образованного вершинами 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);
}