/*========================================================================= Program: Visualization Toolkit Module: vtkKochanekSpline.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkKochanekSpline.h" #include "vtkObjectFactory.h" #include "vtkPiecewiseFunction.h" #include vtkStandardNewMacro(vtkKochanekSpline); //------------------------------------------------------------------------------ // Construct a KochanekSpline with the following defaults: // DefaultBias = 0, // DefaultTension = 0, // DefaultContinuity = 0. vtkKochanekSpline::vtkKochanekSpline() { this->DefaultBias = 0.0; this->DefaultTension = 0.0; this->DefaultContinuity = 0.0; } //------------------------------------------------------------------------------ // Evaluate a 1D Spline double vtkKochanekSpline::Evaluate(double t) { int index = 0; double* intervals; double* coefficients; // check to see if we need to recompute the spline if (this->ComputeTime < this->GetMTime()) { this->Compute(); } // make sure we have at least 2 points int size = this->PiecewiseFunction->GetSize(); if (size < 2) { return 0.0; } intervals = this->Intervals; coefficients = this->Coefficients; if (this->Closed) { size = size + 1; } // clamp the function at both ends if (t < intervals[0]) { t = intervals[0]; } if (t > intervals[size - 1]) { t = intervals[size - 1]; } // find pointer to cubic spline coefficient index = this->FindIndex(size, t); // calculate offset within interval t = (t - intervals[index]) / (intervals[index + 1] - intervals[index]); // evaluate y return (t * (t * (t * *(coefficients + index * 4 + 3) + *(coefficients + index * 4 + 2)) + *(coefficients + index * 4 + 1)) + *(coefficients + index * 4)); } //------------------------------------------------------------------------------ // Compute Kochanek Spline coefficients. void vtkKochanekSpline::Compute() { double *ts, *xs; double* coefficients; std::vector dependent; int size; int i; // get the size of the independent variables size = this->PiecewiseFunction->GetSize(); if (size < 2) { vtkErrorMacro("Spline requires at least 2 points. # of points is: " << size); return; } if (!this->Closed) { // copy the independent variables delete[] this->Intervals; this->Intervals = new double[size]; ts = this->PiecewiseFunction->GetDataPointer(); for (i = 0; i < size; i++) { this->Intervals[i] = *(ts + 2 * i); } // allocate memory for coefficients delete[] this->Coefficients; this->Coefficients = new double[4 * size]; // allocate memory for dependent variables dependent.resize(size); // get start of coefficients for this dependent variable coefficients = this->Coefficients; // get the dependent variable values xs = this->PiecewiseFunction->GetDataPointer() + 1; for (int j = 0; j < size; j++) { dependent[j] = xs[2 * j]; } } else // spline is closed, create extra "fictitious" point { size = size + 1; // copy the independent variables delete[] this->Intervals; this->Intervals = new double[size]; ts = this->PiecewiseFunction->GetDataPointer(); for (i = 0; i < size - 1; i++) { this->Intervals[i] = *(ts + 2 * i); } if (this->ParametricRange[0] != this->ParametricRange[1]) { this->Intervals[size - 1] = this->ParametricRange[1]; } else { this->Intervals[size - 1] = this->Intervals[size - 2] + 1.0; } // allocate memory for coefficients delete[] this->Coefficients; this->Coefficients = new double[4 * size]; // allocate memory for dependent variables dependent.resize(size); // get start of coefficients for this dependent variable coefficients = this->Coefficients; // get the dependent variable values xs = this->PiecewiseFunction->GetDataPointer() + 1; for (int j = 0; j < size - 1; j++) { dependent[j] = xs[2 * j]; } dependent[size - 1] = xs[0]; } this->Fit1D(size, this->Intervals, dependent.data(), this->DefaultTension, this->DefaultBias, this->DefaultContinuity, (double(*)[4])coefficients, this->LeftConstraint, this->LeftValue, this->RightConstraint, this->RightValue); // update compute time this->ComputeTime = this->GetMTime(); } #define VTK_EPSILON .0001 //------------------------------------------------------------------------------ // Compute the coefficients for a 1D spline void vtkKochanekSpline::Fit1D(int size, double* x, double* y, double tension, double bias, double continuity, double coefficients[][4], int leftConstraint, double leftValue, int rightConstraint, double rightValue) { double cs; /* source chord */ double cd; /* destination chord */ double ds; /* source deriviative */ double dd; /* destination deriviative */ double n0, n1; /* number of frames btwn nodes */ int N; /* top point number */ int i; N = size - 1; for (i = 1; i < N; i++) { cs = y[i] - y[i - 1]; cd = y[i + 1] - y[i]; ds = cs * ((1 - tension) * (1 - continuity) * (1 + bias)) / 2.0 + cd * ((1 - tension) * (1 + continuity) * (1 - bias)) / 2.0; dd = cs * ((1 - tension) * (1 + continuity) * (1 + bias)) / 2.0 + cd * ((1 - tension) * (1 - continuity) * (1 - bias)) / 2.0; // adjust deriviatives for non uniform spacing between nodes n1 = x[i + 1] - x[i]; n0 = x[i] - x[i - 1]; ds *= (2 * n0 / (n0 + n1)); dd *= (2 * n1 / (n0 + n1)); coefficients[i][0] = y[i]; coefficients[i][1] = dd; coefficients[i][2] = ds; } // Calculate the deriviatives at the end points coefficients[0][0] = y[0]; coefficients[N][0] = y[N]; coefficients[N][1] = 0.0; coefficients[N][2] = 0.0; coefficients[N][3] = 0.0; if (this->Closed) // the curve is continuous and closed at P0=Pn { cs = y[N] - y[N - 1]; cd = y[1] - y[0]; ds = cs * ((1 - tension) * (1 - continuity) * (1 + bias)) / 2.0 + cd * ((1 - tension) * (1 + continuity) * (1 - bias)) / 2.0; dd = cs * ((1 - tension) * (1 + continuity) * (1 + bias)) / 2.0 + cd * ((1 - tension) * (1 - continuity) * (1 - bias)) / 2.0; // adjust deriviatives for non uniform spacing between nodes n1 = x[1] - x[0]; n0 = x[N] - x[N - 1]; ds *= (2 * n0 / (n0 + n1)); dd *= (2 * n1 / (n0 + n1)); coefficients[0][1] = dd; coefficients[0][2] = ds; coefficients[N][1] = dd; coefficients[N][2] = ds; } else // curve is open { switch (leftConstraint) { case 0: // desired slope at leftmost point is leftValue coefficients[0][1] = this->ComputeLeftDerivative(); break; case 1: // desired slope at leftmost point is leftValue coefficients[0][1] = leftValue; break; case 2: // desired second derivative at leftmost point is leftValue coefficients[0][1] = (6 * (y[1] - y[0]) - 2 * coefficients[1][2] - leftValue) / 4.0; break; case 3: // desired second derivative at leftmost point is leftValue // times second derivative at first interior point if ((leftValue > (-2.0 + VTK_EPSILON)) || (leftValue < (-2.0 - VTK_EPSILON))) { coefficients[0][1] = (3 * (1 + leftValue) * (y[1] - y[0]) - (1 + 2 * leftValue) * coefficients[1][2]) / (2 + leftValue); } else { coefficients[0][1] = 0.0; } break; } switch (rightConstraint) { case 0: // desired slope at rightmost point is rightValue coefficients[N][2] = this->ComputeRightDerivative(); break; case 1: // desired slope at rightmost point is rightValue coefficients[N][2] = rightValue; break; case 2: // desired second derivative at rightmost point is rightValue coefficients[N][2] = (6 * (y[N] - y[N - 1]) - 2 * coefficients[N - 1][1] + rightValue) / 4.0; break; case 3: // desired second derivative at rightmost point is rightValue // times second derivative at last interior point if ((rightValue > (-2.0 + VTK_EPSILON)) || (rightValue < (-2.0 - VTK_EPSILON))) { coefficients[N][2] = (3 * (1 + rightValue) * (y[N] - y[N - 1]) - (1 + 2 * rightValue) * coefficients[N - 1][1]) / (2 + rightValue); } else { coefficients[N][2] = 0.0; } break; } } // curve is open // Compute the Coefficients for (i = 0; i < N; i++) { // // c0 = P ; c1 = DD ; // i i i i // // c1 = P ; c2 = DS ; // i+1 i+1 i+1 i+1 // // c2 = -3P + 3P - 2DD - DS ; // i i i+1 i i+1 // // c3 = 2P - 2P + DD + DS ; // i i i+1 i i+1 // coefficients[i][2] = (-3 * y[i]) + (3 * y[i + 1]) + (-2 * coefficients[i][1]) + (-1 * coefficients[i + 1][2]); coefficients[i][3] = (2 * y[i]) + (-2 * y[i + 1]) + (1 * coefficients[i][1]) + (1 * coefficients[i + 1][2]); } } //------------------------------------------------------------------------------ void vtkKochanekSpline::DeepCopy(vtkSpline* s) { vtkKochanekSpline* spline = vtkKochanekSpline::SafeDownCast(s); if (spline != nullptr) { this->DefaultBias = spline->DefaultBias; this->DefaultTension = spline->DefaultTension; this->DefaultContinuity = spline->DefaultContinuity; } // Now do superclass this->vtkSpline::DeepCopy(s); } //------------------------------------------------------------------------------ void vtkKochanekSpline::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "DefaultBias: " << this->DefaultBias << "\n"; os << indent << "DefaultTension: " << this->DefaultTension << "\n"; os << indent << "DefaultContinuity: " << this->DefaultContinuity << "\n"; }