public static Output <double, double, Vector, Vector> PrincipalCurvatureAtParameter(this NurbsSurface surface, double u, double v) { // Vector entries for a "Hessian" with regard to u and v Vector dU2 = DerivativeAtParameter(surface, u, v, 2, 0); Vector dUV = DerivativeAtParameter(surface, u, v, 1, 1); Vector dV2 = DerivativeAtParameter(surface, u, v, 0, 2); // Get the local space, dU and dV are the basis vectors for the Hessian above. Vector dU = DerivativeAtParameter(surface, u, v, 1, 0); Vector dV = DerivativeAtParameter(surface, u, v, 0, 1); Vector normal = dU.CrossProduct(dV).Normalise(); // We reduce the vector valued Hessian to a scalar valued Hessian, where the value (of the imagined original function) is the distance from the point we evaluate along the normal // | a b | // | c d | double a = dU2.DotProduct(normal); double b = dUV.DotProduct(normal); double c = b; double d = dV2.DotProduct(normal); // That's the Hessian for a system where the derivative vectors are the local coordinate systems xy-axis, i.e very poorly scaled and skewed for most purposes double phi = dU.SignedAngle(dV, normal); // Change of basis matrix, to get the transform for the Hessian to the world space // | e f | // | g h | double e = 1 / dU.Length(); double g = 0; double h = 1 / (Math.Sin(phi) * dV.Length()); double f = -(Math.Cos(phi) * dV.Length()) * h * e; // A^T * Hess(x) * A gives the Hessian in the world space scaled/skewed system, i.e a system we care about double a1 = a * e * e + b * g * e + c * e * g + d * g * g; double b1 = e * a * f + e * b * h + g * c * f + g * d * h; double c1 = f * a * e + b * g * f + c * e * h + d * g * h; double d1 = a * f * f + b * h * f + c * f * h + d * h * h; // Eigenvalues of the Hessian gives the min and max curvature of the function double p = (a1 + d1) * 0.5; double sqrt = Math.Sqrt((a1 + d1) * (a1 + d1) * 0.25 - (a1 * d1 - b1 * c1)); if (double.IsNaN(sqrt)) { sqrt = 0; } double eigenMin = (p - sqrt); double eigenMax = (p + sqrt); // The eigenvectors of the Hessian are the principled directions Vector eigMin = new Vector(); Vector eigMax = new Vector(); if (Math.Abs(c1) > Tolerance.Distance) { eigMin.X = eigenMin - d1; eigMin.Y = c1; eigMax.X = eigenMax - d1; eigMax.Y = c1; } else if (Math.Abs(b1) > Tolerance.Distance) { eigMin.X = b1; eigMin.Y = eigenMin - a1; eigMax.X = b1; eigMax.Y = eigenMax - a1; } else { eigMin.X = 1; eigMax.Y = 1; } // Orient to world space, as in we've been working on the origin with the normal as z-axis, this places the vectors back to the surface. // would be nice to suppress the warning in some manner as it is very much intended. TransformMatrix matrix = Create.OrientationMatrixGlobalToLocal(Create.CartesianCoordinateSystem(new Point(), dU, dV)); return(new Output <double, double, Vector, Vector>() { Item1 = eigenMin, Item2 = eigenMax, Item3 = eigMin.Transform(matrix).Normalise(), Item4 = eigMax.Transform(matrix).Normalise(), }); }