// David Eberly, Geometric Tools, Redmond WA 98052 // Copyright (c) 1998-2020 // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt // Version: 4.0.2019.08.13 #pragma once #include #include #include // Least-squares fit of a paraboloid to a set of point. The paraboloid is // of the form z = c0*x^2+c1*x*y+c2*y^2+c3*x+c4*y+c5. A successful fit is // indicated by return value of 'true'. // // Given a set of samples (x_i,y_i,z_i) for 0 <= i < N, and assuming // that the true values lie on a paraboloid // z = p0*x*x + p1*x*y + p2*y*y + p3*x + p4*y + p5 = Dot(P,Q(x,y)) // where P = (p0,p1,p2,p3,p4,p5) and Q(x,y) = (x*x,x*y,y*y,x,y,1), // select P to minimize the sum of squared errors // E(P) = sum_{i=0}^{N-1} [Dot(P,Q_i)-z_i]^2 // where Q_i = Q(x_i,y_i). // // The minimum occurs when the gradient of E is the zero vector, // grad(E) = 2 sum_{i=0}^{N-1} [Dot(P,Q_i)-z_i] Q_i = 0 // Some algebra converts this to a system of 6 equations in 6 unknowns: // [(sum_{i=0}^{N-1} Q_i Q_i^t] P = sum_{i=0}^{N-1} z_i Q_i // The product Q_i Q_i^t is a product of the 6x1 matrix Q_i with the // 1x6 matrix Q_i^t, the result being a 6x6 matrix. // // Define the 6x6 symmetric matrix A = sum_{i=0}^{N-1} Q_i Q_i^t and the 6x1 // vector B = sum_{i=0}^{N-1} z_i Q_i. The choice for P is the solution to // the linear system of equations A*P = B. The entries of A and B indicate // summations over the appropriate product of variables. For example, // s(x^3 y) = sum_{i=0}^{N-1} x_i^3 y_i. // // +- -++ + +- -+ // | s(x^4) s(x^3 y) s(x^2 y^2) s(x^3) s(x^2 y) s(x^2) ||p0| |s(z x^2)| // | s(x^2 y^2) s(x y^3) s(x^2 y) s(x y^2) s(x y) ||p1| |s(z x y)| // | s(y^4) s(x y^2) s(y^3) s(y^2) ||p2| = |s(z y^2)| // | s(x^2) s(x y) s(x) ||p3| |s(z x) | // | s(y^2) s(y) ||p4| |s(z y) | // | s(1) ||p5| |s(z) | // +- -++ + +- -+ namespace WwiseGTE { template class ApprParaboloid3 { public: bool operator()(int numPoints, Vector3 const* points, Real coefficients[6]) const { Matrix<6, 6, Real> A; Vector<6, Real> B; B.MakeZero(); for (int i = 0; i < numPoints; i++) { Real x2 = points[i][0] * points[i][0]; Real xy = points[i][0] * points[i][1]; Real y2 = points[i][1] * points[i][1]; Real zx = points[i][2] * points[i][0]; Real zy = points[i][2] * points[i][1]; Real x3 = points[i][0] * x2; Real x2y = x2 * points[i][1]; Real xy2 = points[i][0] * y2; Real y3 = points[i][1] * y2; Real zx2 = points[i][2] * x2; Real zxy = points[i][2] * xy; Real zy2 = points[i][2] * y2; Real x4 = x2 * x2; Real x3y = x3 * points[i][1]; Real x2y2 = x2 * y2; Real xy3 = points[i][0] * y3; Real y4 = y2 * y2; A(0, 0) += x4; A(0, 1) += x3y; A(0, 2) += x2y2; A(0, 3) += x3; A(0, 4) += x2y; A(0, 5) += x2; A(1, 2) += xy3; A(1, 4) += xy2; A(1, 5) += xy; A(2, 2) += y4; A(2, 4) += y3; A(2, 5) += y2; A(3, 3) += x2; A(3, 5) += points[i][0]; A(4, 5) += points[i][1]; B[0] += zx2; B[1] += zxy; B[2] += zy2; B[3] += zx; B[4] += zy; B[5] += points[i][2]; } A(1, 0) = A(0, 1); A(1, 1) = A(0, 2); A(1, 3) = A(0, 4); A(2, 0) = A(0, 2); A(2, 1) = A(1, 2); A(2, 3) = A(1, 4); A(3, 0) = A(0, 3); A(3, 1) = A(1, 3); A(3, 2) = A(2, 3); A(3, 4) = A(1, 5); A(4, 0) = A(0, 4); A(4, 1) = A(1, 4); A(4, 2) = A(2, 4); A(4, 3) = A(3, 4); A(4, 4) = A(2, 5); A(5, 0) = A(0, 5); A(5, 1) = A(1, 5); A(5, 2) = A(2, 5); A(5, 3) = A(3, 5); A(5, 4) = A(4, 5); A(5, 5) = static_cast(numPoints); return LinearSystem().Solve(6, &A[0], &B[0], &coefficients[0]); } }; }