/*
* Copyright (C) 2017 Open Source Robotics Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include "gz/math/Matrix4.hh"
#include "SplinePrivate.hh"
namespace ignition
{
namespace math
{
inline namespace IGNITION_MATH_VERSION_NAMESPACE
{
///////////////////////////////////////////////////////////
Vector4d PolynomialPowers(const unsigned int _order,
const double _t)
{
// It is much faster to go over this table than
// delving into factorials and power computations.
double t2 = _t * _t;
double t3 = t2 * _t;
switch (_order) {
case 0:
return Vector4d(t3, t2, _t, 1.0);
case 1:
return Vector4d(3*t2, 2*_t, 1.0, 0.0);
case 2:
return Vector4d(6*_t, 2.0, 0.0, 0.0);
case 3:
return Vector4d(6.0, 0.0, 0.0, 0.0);
default:
return Vector4d(0.0, 0.0, 0.0, 0.0);
}
}
///////////////////////////////////////////////////////////
void ComputeCubicBernsteinHermiteCoeff(const ControlPoint &_startPoint,
const ControlPoint &_endPoint,
Matrix4d &_coeffs)
{
// Get values and tangents
const Vector3d &point0 = _startPoint.MthDerivative(0);
const Vector3d &point1 = _endPoint.MthDerivative(0);
const Vector3d &tan0 = _startPoint.MthDerivative(1);
const Vector3d &tan1 = _endPoint.MthDerivative(1);
// Hermite basis matrix
const Matrix4d bmatrix(2.0, -2.0, 1.0, 1.0,
-3.0, 3.0, -2.0, -1.0,
0.0, 0.0, 1.0, 0.0,
1.0, 0.0, 0.0, 0.0);
// Control vectors matrix
Matrix4d cmatrix(point0.X(), point0.Y(), point0.Z(), 1.0,
point1.X(), point1.Y(), point1.Z(), 1.0,
tan0.X(), tan0.Y(), tan0.Z(), 1.0,
tan1.X(), tan1.Y(), tan1.Z(), 1.0);
// Compute coefficients
_coeffs = bmatrix * cmatrix;
}
///////////////////////////////////////////////////////////
IntervalCubicSpline::IntervalCubicSpline()
: startPoint({Vector3d::Zero, Vector3d::Zero}),
endPoint({Vector3d::Zero, Vector3d::Zero}),
coeffs(Matrix4d::Zero),
arcLength(0.0)
{
}
///////////////////////////////////////////////////////////
void IntervalCubicSpline::SetPoints(const ControlPoint &_startPoint,
const ControlPoint &_endPoint)
{
this->startPoint = _startPoint;
this->endPoint = _endPoint;
ComputeCubicBernsteinHermiteCoeff(
this->startPoint, this->endPoint, this->coeffs);
this->startPoint.MthDerivative(2) = this->DoInterpolateMthDerivative(2, 0.0);
this->startPoint.MthDerivative(3) = this->DoInterpolateMthDerivative(3, 0.0);
this->endPoint.MthDerivative(2) = this->DoInterpolateMthDerivative(2, 1.0);
this->endPoint.MthDerivative(3) = this->DoInterpolateMthDerivative(3, 1.0);
this->arcLength = this->ArcLength(1.0);
}
///////////////////////////////////////////////////////////
double IntervalCubicSpline::ArcLength(const double _t) const
{
// Bound check
if (_t < 0.0 || _t > 1.0)
return INF_D;
// 5 Point Gauss-Legendre quadrature rule for numerical path integration
// TODO(anyone): generalize into a numerical integration toolkit ?
double w1 = 0.28444444444444444 * _t;
double w23 = 0.23931433524968326 * _t;
double w45 = 0.11846344252809456 * _t;
double x1 = 0.5 * _t;
double x2 = 0.23076534494715845 * _t;
double x3 = 0.7692346550528415 * _t;
double x4 = 0.0469100770306680 * _t;
double x5 = 0.9530899229693319 * _t;
double arc_length = w1 * this->InterpolateMthDerivative(1, x1).Length();
arc_length += w23 * this->InterpolateMthDerivative(1, x2).Length();
arc_length += w23 * this->InterpolateMthDerivative(1, x3).Length();
arc_length += w45 * this->InterpolateMthDerivative(1, x4).Length();
arc_length += w45 * this->InterpolateMthDerivative(1, x5).Length();
return arc_length;
}
///////////////////////////////////////////////////////////
Vector3d IntervalCubicSpline::DoInterpolateMthDerivative(
const unsigned int _mth, const double _t) const
{
Vector4d powers = PolynomialPowers(_mth, _t);
Vector4d interpolation = powers * this->coeffs;
return Vector3d(interpolation.X(), interpolation.Y(), interpolation.Z());
}
///////////////////////////////////////////////////////////
Vector3d IntervalCubicSpline::InterpolateMthDerivative(
const unsigned int _mth, const double _t) const
{
// Bound check
if (_t < 0.0 || _t > 1.0)
return Vector3d(INF_D, INF_D, INF_D);
if (equal(_t, 0.0))
return this->startPoint.MthDerivative(_mth);
else if (equal(_t, 1.0))
return this->endPoint.MthDerivative(_mth);
return this->DoInterpolateMthDerivative(_mth, _t);
}
}
}
}