BSplineSurfaceFit<Real>::BSplineSurfaceFit (int iDegree0, int iControlQuantity0, int iSampleQuantity0, int iDegree1, int iControlQuantity1, int iSampleQuantity1, Vector3<Real>** aakSamplePoint) { assert(1 <= iDegree0 && iDegree0 < iControlQuantity0); assert(iControlQuantity0 <= iSampleQuantity0); assert(1 <= iDegree1 && iDegree1 < iControlQuantity1); assert(iControlQuantity1 <= iSampleQuantity1); m_aiDegree[0] = iDegree0; m_aiSampleQuantity[0] = iSampleQuantity0; m_aiControlQuantity[0] = iControlQuantity0; m_aiDegree[1] = iDegree1; m_aiSampleQuantity[1] = iSampleQuantity1; m_aiControlQuantity[1] = iControlQuantity1; m_aakSamplePoint = aakSamplePoint; Allocate(iControlQuantity0,iControlQuantity1,m_aakControlPoint); // The double-precision basis functions are used to help with the // numerical round-off errors. BSplineFitBasisd* apkDBasis[2]; double adTMultiplier[2]; int iDim; for (iDim = 0; iDim < 2; iDim++) { m_apkBasis[iDim] = WM4_NEW BSplineFitBasis<Real>( m_aiControlQuantity[iDim],m_aiDegree[iDim]); apkDBasis[iDim] = WM4_NEW BSplineFitBasisd( m_aiControlQuantity[iDim],m_aiDegree[iDim]); adTMultiplier[iDim] = 1.0/(double)(m_aiSampleQuantity[iDim] - 1); } // Fit the data points with a B-spline surface using a least-squares error // metric. The problem is of the form A0^T*A0*Q*A1^T*A1 = A0^T*P*A1, where // A0^T*A0 and A1^T*A1 are banded matrices, P contains the sample data, and // Q is the unknown matrix of control points. double dT; int i0, i1, i2, iMin, iMax; // Construct the matrices A0^T*A0 and A1^T*A1. BandedMatrixd* apkATAMat[2]; for (iDim = 0; iDim < 2; iDim++) { apkATAMat[iDim] = WM4_NEW BandedMatrixd(m_aiControlQuantity[iDim], m_aiDegree[iDim]+1,m_aiDegree[iDim]+1); for (i0 = 0; i0 < m_aiControlQuantity[iDim]; i0++) { for (i1 = 0; i1 < i0; i1++) { (*apkATAMat[iDim])(i0,i1) = (*apkATAMat[iDim])(i1,i0); } int i1Max = i0 + m_aiDegree[iDim]; if (i1Max >= m_aiControlQuantity[iDim]) { i1Max = m_aiControlQuantity[iDim] - 1; } for (i1 = i0; i1 <= i1Max; i1++) { double dValue = 0.0; for (i2 = 0; i2 < m_aiSampleQuantity[iDim]; i2++) { dT = adTMultiplier[iDim]*(double)i2; apkDBasis[iDim]->Compute(dT,iMin,iMax); if (iMin <= i0 && i0 <= iMax && iMin <= i1 && i1 <= iMax) { double dB0 = apkDBasis[iDim]->GetValue(i0 - iMin); double dB1 = apkDBasis[iDim]->GetValue(i1 - iMin); dValue += dB0*dB1; } } (*apkATAMat[iDim])(i0,i1) = dValue; } } } // Construct the matrices A0^T and A1^T. double** aaadATMat[2]; for (iDim = 0; iDim < 2; iDim++) { Allocate(m_aiSampleQuantity[iDim],m_aiControlQuantity[iDim], aaadATMat[iDim]); memset(aaadATMat[iDim][0],0,m_aiControlQuantity[iDim] *m_aiSampleQuantity[iDim]*sizeof(double)); for (i0 = 0; i0 < m_aiControlQuantity[iDim]; i0++) { for (i1 = 0; i1 < m_aiSampleQuantity[iDim]; i1++) { dT = adTMultiplier[iDim]*(double)i1; apkDBasis[iDim]->Compute(dT,iMin,iMax); if (iMin <= i0 && i0 <= iMax) { aaadATMat[iDim][i0][i1] = apkDBasis[iDim]->GetValue(i0 - iMin); } } } } // Compute X0 = (A0^T*A0)^{-1}*A0^T and X1 = (A1^T*A1)^{-1}*A1^T by // solving the linear systems A0^T*A0*X0 = A0^T and A1^T*A1*X1 = A1^T. for (iDim = 0; iDim < 2; iDim++) { bool bSolved = apkATAMat[iDim]->SolveSystem(aaadATMat[iDim], m_aiSampleQuantity[iDim]); assert(bSolved); (void)bSolved; // Avoid warning in release build. } // The control points for the fitted surface are stored in the matrix // Q = X0*P*X1^T, where P is the matrix of sample data. for (i0 = 0; i0 < m_aiControlQuantity[0]; i0++) { for (i1 = 0; i1 < m_aiControlQuantity[1]; i1++) { Vector3<Real> kSum = Vector3<Real>::ZERO; for (int j0 = 0; j0 < m_aiSampleQuantity[0]; j0++) { Real fX0Value = (Real)aaadATMat[0][i0][j0]; for (int j1 = 0; j1 < m_aiSampleQuantity[1]; j1++) { Real fX1Value = (Real)aaadATMat[1][i1][j1]; kSum += (fX0Value*fX1Value)*m_aakSamplePoint[j0][j1]; } } m_aakControlPoint[i0][i1] = kSum; } } for (iDim = 0; iDim < 2; iDim++) { WM4_DELETE apkDBasis[iDim]; WM4_DELETE apkATAMat[iDim]; Deallocate(aaadATMat[iDim]); } }
BSplineCurveFit<Real>::BSplineCurveFit (int dimension, int numSamples, const Real* sampleData, int degree, int numControls) : mBasis(numControls, degree) { assertion(dimension >= 1, "Invalid input\n"); assertion(1 <= degree && degree < numControls, "Invalid input\n"); assertion(numControls <= numSamples, "Invalid input\n"); mDimension = dimension; mNumSamples = numSamples; mSampleData = sampleData; mDegree = degree; mNumControls = numControls; mControlData = new1<Real>(mDimension*numControls); // The double-precision basis functions are used to help with the // numerical round-off errors. BSplineFitBasisd dBasis(mNumControls,mDegree); double tMultiplier = 1.0/(double)(mNumSamples - 1); // Fit the data points with a B-spline curve using a least-squares error // metric. The problem is of the form A^T*A*Q = A^T*P, where A^T*A is a // banded matrix, P contains the sample data, and Q is the unknown vector // of control points. double t; int i0, i1, i2, imin, imax, j; // Construct the matrix A^T*A. BandedMatrixd* ATAMat = new0 BandedMatrixd(mNumControls, mDegree + 1, mDegree + 1); for (i0 = 0; i0 < mNumControls; ++i0) { for (i1 = 0; i1 < i0; ++i1) { (*ATAMat)(i0, i1) = (*ATAMat)(i1, i0); } int i1Max = i0 + mDegree; if (i1Max >= mNumControls) { i1Max = mNumControls - 1; } for (i1 = i0; i1 <= i1Max; ++i1) { double value = 0.0; for (i2 = 0; i2 < mNumSamples; ++i2) { t = tMultiplier*(double)i2; dBasis.Compute(t, imin, imax); if (imin <= i0 && i0 <= imax && imin <= i1 && i1 <= imax) { double dB0 = dBasis.GetValue(i0 - imin); double dB1 = dBasis.GetValue(i1 - imin); value += dB0*dB1; } } (*ATAMat)(i0, i1) = value; } } // Construct the matrix A^T. double** ATMat = new2<double>(mNumSamples, mNumControls); memset(ATMat[0], 0, mNumControls*mNumSamples*sizeof(double)); for (i0 = 0; i0 < mNumControls; ++i0) { for (i1 = 0; i1 < mNumSamples; ++i1) { t = tMultiplier*(double)i1; dBasis.Compute(t, imin, imax); if (imin <= i0 && i0 <= imax) { ATMat[i0][i1] = dBasis.GetValue(i0 - imin); } } } // Compute X0 = (A^T*A)^{-1}*A^T by solving the linear system // A^T*A*X = A^T. bool solved = ATAMat->SolveSystem(ATMat,mNumSamples); assertion(solved, "Failed to solve linear system\n"); WM5_UNUSED(solved); // The control points for the fitted curve are stored in the vector // Q = X0*P, where P is the vector of sample data. memset(mControlData, 0, mNumControls*mDimension*sizeof(Real)); for (i0 = 0; i0 < mNumControls; ++i0) { Real* Q = mControlData + i0*mDimension; for (i1 = 0; i1 < mNumSamples; ++i1) { const Real* P = mSampleData + i1*mDimension; Real xValue = (Real)ATMat[i0][i1]; for (j = 0; j < mDimension; j++) { Q[j] += xValue*P[j]; } } } // Set the first and last output control points to match the first and // last input samples. This supports the application of fitting keyframe // data with B-spline curves. The user expects that the curve passes // through the first and last positions in order to support matching two // consecutive keyframe sequences. Real* cEnd0 = mControlData; const Real* sEnd0 = mSampleData; Real* cEnd1 = &mControlData[mDimension*(mNumControls-1)]; const Real* sEnd1 = &mSampleData[mDimension*(mNumSamples-1)]; for (j = 0; j < mDimension; j++) { *cEnd0++ = *sEnd0++; *cEnd1++ = *sEnd1++; } delete2(ATMat); delete0(ATAMat); }
BSplineSurfaceFit<Real>::BSplineSurfaceFit (int degree0, int numControls0, int numSamples0, int degree1, int numControls1, int numSamples1, Vector3<Real>** samples) { assertion(1 <= degree0 && degree0 + 1 < numControls0, "Invalid input\n"); assertion(numControls0 <= numSamples0, "Invalid input\n"); assertion(1 <= degree1 && degree1 + 1 < numControls1, "Invalid input\n"); assertion(numControls1 <= numSamples1, "Invalid input\n"); mDegree[0] = degree0; mNumSamples[0] = numSamples0; mNumControls[0] = numControls0; mDegree[1] = degree1; mNumSamples[1] = numSamples1; mNumControls[1] = numControls1; mSamples = samples; mControls = new2<Vector3<Real> >(numControls0, numControls1); // The double-precision basis functions are used to help with the // numerical round-off errors. BSplineFitBasisd* dBasis[2]; double tMultiplier[2]; int dim; for (dim = 0; dim < 2; ++dim) { mBasis[dim] = new0 BSplineFitBasis<Real>(mNumControls[dim], mDegree[dim]); dBasis[dim] = new0 BSplineFitBasisd(mNumControls[dim], mDegree[dim]); tMultiplier[dim] = 1.0/(double)(mNumSamples[dim] - 1); } // Fit the data points with a B-spline surface using a least-squares error // metric. The problem is of the form A0^T*A0*Q*A1^T*A1 = A0^T*P*A1, where // A0^T*A0 and A1^T*A1 are banded matrices, P contains the sample data, and // Q is the unknown matrix of control points. double t; int i0, i1, i2, imin, imax; // Construct the matrices A0^T*A0 and A1^T*A1. BandedMatrixd* ATAMat[2]; for (dim = 0; dim < 2; ++dim) { ATAMat[dim] = new0 BandedMatrixd(mNumControls[dim], mDegree[dim] + 1, mDegree[dim] + 1); for (i0 = 0; i0 < mNumControls[dim]; ++i0) { for (i1 = 0; i1 < i0; ++i1) { (*ATAMat[dim])(i0, i1) = (*ATAMat[dim])(i1, i0); } int i1Max = i0 + mDegree[dim]; if (i1Max >= mNumControls[dim]) { i1Max = mNumControls[dim] - 1; } for (i1 = i0; i1 <= i1Max; ++i1) { double value = 0.0; for (i2 = 0; i2 < mNumSamples[dim]; ++i2) { t = tMultiplier[dim]*(double)i2; dBasis[dim]->Compute(t, imin, imax); if (imin <= i0 && i0 <= imax && imin <= i1 && i1 <= imax) { double b0 = dBasis[dim]->GetValue(i0 - imin); double b1 = dBasis[dim]->GetValue(i1 - imin); value += b0*b1; } } (*ATAMat[dim])(i0, i1) = value; } } } // Construct the matrices A0^T and A1^T. A[d]^T has mNumControls[d] // rows and mNumSamples[d] columns. double** ATMat[2]; for (dim = 0; dim < 2; dim++) { ATMat[dim] = new2<double>(mNumSamples[dim], mNumControls[dim]); memset(ATMat[dim][0], 0, mNumControls[dim]*mNumSamples[dim]*sizeof(double)); for (i0 = 0; i0 < mNumControls[dim]; ++i0) { for (i1 = 0; i1 < mNumSamples[dim]; ++i1) { t = tMultiplier[dim]*(double)i1; dBasis[dim]->Compute(t, imin, imax); if (imin <= i0 && i0 <= imax) { ATMat[dim][i0][i1] = dBasis[dim]->GetValue(i0 - imin); } } } } // Compute X0 = (A0^T*A0)^{-1}*A0^T and X1 = (A1^T*A1)^{-1}*A1^T by // solving the linear systems A0^T*A0*X0 = A0^T and A1^T*A1*X1 = A1^T. for (dim = 0; dim < 2; ++dim) { bool solved = ATAMat[dim]->SolveSystem(ATMat[dim], mNumSamples[dim]); assertion(solved, "Failed to solve linear system\n"); WM5_UNUSED(solved); } // The control points for the fitted surface are stored in the matrix // Q = X0*P*X1^T, where P is the matrix of sample data. for (i1 = 0; i1 < mNumControls[1]; ++i1) { for (i0 = 0; i0 < mNumControls[0]; ++i0) { Vector3<Real> sum = Vector3<Real>::ZERO; for (int j1 = 0; j1 < mNumSamples[1]; ++j1) { Real x1Value = (Real)ATMat[1][i1][j1]; for (int j0 = 0; j0 < mNumSamples[0]; ++j0) { Real x0Value = (Real)ATMat[0][i0][j0]; sum += (x0Value*x1Value)*mSamples[j1][j0]; } } mControls[i1][i0] = sum; } } for (dim = 0; dim < 2; ++dim) { delete0(dBasis[dim]); delete0(ATAMat[dim]); delete2(ATMat[dim]); } }