189 lines
4.5 KiB
C++
189 lines
4.5 KiB
C++
/*
|
|
* Copyright (C) 2012 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 "gazebo/math/RotationSpline.hh"
|
|
|
|
using namespace gazebo;
|
|
using namespace math;
|
|
|
|
RotationSpline::RotationSpline()
|
|
: autoCalc(true)
|
|
{
|
|
}
|
|
|
|
RotationSpline::~RotationSpline()
|
|
{
|
|
}
|
|
|
|
void RotationSpline::AddPoint(const Quaternion &_p)
|
|
{
|
|
this->points.push_back(_p);
|
|
if (this->autoCalc)
|
|
this->RecalcTangents();
|
|
}
|
|
|
|
Quaternion RotationSpline::Interpolate(double _t, bool _useShortestPath)
|
|
{
|
|
// Work out which segment this is in
|
|
double fSeg = _t * (this->points.size() - 1);
|
|
unsigned int segIdx = (unsigned int)fSeg;
|
|
|
|
// Apportion t
|
|
_t = fSeg - segIdx;
|
|
|
|
return this->Interpolate(segIdx, _t, _useShortestPath);
|
|
}
|
|
|
|
Quaternion RotationSpline::Interpolate(unsigned int _fromIndex, double _t,
|
|
bool _useShortestPath)
|
|
{
|
|
// Bounds check
|
|
assert (_fromIndex < this->points.size() && "fromIndex out of bounds");
|
|
|
|
if ((_fromIndex + 1) == this->points.size())
|
|
{
|
|
// Duff request, cannot blend to nothing
|
|
// Just return source
|
|
return this->points[_fromIndex];
|
|
}
|
|
|
|
// Fast special cases
|
|
if (math::equal(_t, 0.0))
|
|
return this->points[_fromIndex];
|
|
else if (math::equal(_t, 1.0))
|
|
return this->points[_fromIndex + 1];
|
|
|
|
// double interpolation
|
|
// Use squad using tangents we've already set up
|
|
Quaternion &p = this->points[_fromIndex];
|
|
Quaternion &q = this->points[_fromIndex+1];
|
|
Quaternion &a = this->tangents[_fromIndex];
|
|
Quaternion &b = this->tangents[_fromIndex+1];
|
|
|
|
// NB interpolate to nearest rotation
|
|
return Quaternion::Squad(_t, p, a, b, q, _useShortestPath);
|
|
}
|
|
|
|
void RotationSpline::RecalcTangents()
|
|
{
|
|
// ShoeMake (1987) approach
|
|
// Just like Catmull-Rom really, just more gnarly
|
|
// And no, I don't understand how to derive this!
|
|
//
|
|
// let p = point[i], pInv = p.Inverse
|
|
// tangent[i] = p * exp(-0.25 *
|
|
// (log(pInv * point[i+1]) + log(pInv * point[i-1])))
|
|
//
|
|
// Assume endpoint tangents are parallel with line with neighbour
|
|
|
|
unsigned int i, numPoints;
|
|
bool isClosed;
|
|
|
|
numPoints = this->points.size();
|
|
|
|
if (numPoints < 2)
|
|
{
|
|
// Can't do anything yet
|
|
return;
|
|
}
|
|
|
|
this->tangents.resize(numPoints);
|
|
|
|
if (this->points[0] == this->points[numPoints-1])
|
|
isClosed = true;
|
|
else
|
|
isClosed = false;
|
|
|
|
Quaternion invp, part1, part2, preExp;
|
|
for (i = 0; i < numPoints; ++i)
|
|
{
|
|
Quaternion &p = this->points[i];
|
|
invp = p.GetInverse();
|
|
|
|
if (i == 0)
|
|
{
|
|
// special case start
|
|
part1 = (invp * this->points[i+1]).GetLog();
|
|
if (isClosed)
|
|
{
|
|
// Use numPoints-2 since numPoints-1 == end == start == this one
|
|
part2 = (invp * this->points[numPoints-2]).GetLog();
|
|
}
|
|
else
|
|
{
|
|
part2 = (invp * p).GetLog();
|
|
}
|
|
}
|
|
else if (i == numPoints-1)
|
|
{
|
|
// special case end
|
|
if (isClosed)
|
|
{
|
|
// Wrap to [1] (not [0], this is the same as end == this one)
|
|
part1 = (invp * this->points[1]).GetLog();
|
|
}
|
|
else
|
|
{
|
|
part1 = (invp * p).GetLog();
|
|
}
|
|
part2 = (invp * this->points[i-1]).GetLog();
|
|
}
|
|
else
|
|
{
|
|
part1 = (invp * this->points[i+1]).GetLog();
|
|
part2 = (invp * this->points[i-1]).GetLog();
|
|
}
|
|
|
|
preExp = (part1 + part2) * -0.25;
|
|
this->tangents[i] = p * preExp.GetExp();
|
|
}
|
|
}
|
|
|
|
const Quaternion& RotationSpline::GetPoint(unsigned int _index) const
|
|
{
|
|
assert (_index < this->points.size() && "Point index is out of bounds!!");
|
|
|
|
return this->points[_index];
|
|
}
|
|
|
|
unsigned int RotationSpline::GetNumPoints() const
|
|
{
|
|
return this->points.size();
|
|
}
|
|
|
|
void RotationSpline::Clear()
|
|
{
|
|
this->points.clear();
|
|
this->tangents.clear();
|
|
}
|
|
|
|
void RotationSpline::UpdatePoint(unsigned int _index,
|
|
const Quaternion &_value)
|
|
{
|
|
assert (_index < this->points.size() && "Point index is out of bounds!!");
|
|
|
|
this->points[_index] = _value;
|
|
if (this->autoCalc)
|
|
this->RecalcTangents();
|
|
}
|
|
|
|
void RotationSpline::SetAutoCalculate(bool _autoCalc)
|
|
{
|
|
this->autoCalc = _autoCalc;
|
|
}
|
|
|
|
|