Program Listing for File wrapped_float.cpp
↰ Return to documentation for file (src/tap/algorithms/wrapped_float.cpp
)
/*
* Copyright (c) 2020-2021 Advanced Robotics at the University of Washington <robomstr@uw.edu>
*
* This file is part of Taproot.
*
* Taproot is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Taproot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Taproot. If not, see <https://www.gnu.org/licenses/>.
*/
#include "wrapped_float.hpp"
namespace tap
{
namespace algorithms
{
WrappedFloat::WrappedFloat(const float value, const float lowerBound, const float upperBound)
: wrapped(value),
lowerBound(lowerBound),
upperBound(upperBound)
{
assert(upperBound > lowerBound);
wrapValue();
}
bool WrappedFloat::operator==(const WrappedFloat& other) const
{
assertBoundsEqual(other);
return this->wrapped == other.wrapped;
}
void WrappedFloat::operator+=(const WrappedFloat& other)
{
assertBoundsEqual(other);
this->wrapped += other.wrapped;
wrapValue();
this->revolutions += other.revolutions;
}
void WrappedFloat::operator-=(const WrappedFloat& other)
{
assertBoundsEqual(other);
this->wrapped -= other.wrapped;
wrapValue();
this->revolutions -= other.revolutions;
}
WrappedFloat WrappedFloat::operator+(const WrappedFloat& other) const
{
assertBoundsEqual(other);
WrappedFloat temp(*this);
temp += other;
return temp;
}
WrappedFloat WrappedFloat::operator-(const WrappedFloat& other) const
{
assertBoundsEqual(other);
WrappedFloat temp = *this;
temp -= other;
return temp;
}
void WrappedFloat::operator+=(float value) { *this += this->withSameBounds(value); }
void WrappedFloat::operator-=(float value) { *this -= this->withSameBounds(value); }
WrappedFloat WrappedFloat::operator+(float value) const
{
return *this + this->withSameBounds(value);
}
WrappedFloat WrappedFloat::operator-(float value) const
{
return *this - this->withSameBounds(value);
}
float WrappedFloat::minDifference(const WrappedFloat& other) const
{
assertBoundsEqual(other);
float interval = this->getUpperBound() - this->getLowerBound();
float difference_between = other.getWrappedValue() - this->getWrappedValue();
float difference_around =
difference_between + ((difference_between < 0) ? interval : -interval);
return (abs(difference_between) < abs(difference_around)) ? difference_between
: difference_around;
}
float WrappedFloat::minDifference(const float& unwrappedValue) const
{
return minDifference(this->withSameBounds(unwrappedValue));
}
WrappedFloat WrappedFloat::minInterpolate(const WrappedFloat& other, const float alpha) const
{
assertBoundsEqual(other);
return *this + (minDifference(other) * alpha);
}
void WrappedFloat::shiftBounds(const float shiftMagnitude)
{
upperBound += shiftMagnitude;
lowerBound += shiftMagnitude;
wrapValue();
}
void WrappedFloat::wrapValue()
{
float oldValue = wrapped;
if (oldValue < lowerBound)
{
this->wrapped = upperBound + fmodf(oldValue - upperBound, upperBound - lowerBound);
}
else if (oldValue >= upperBound)
{
this->wrapped = lowerBound + fmodf(oldValue - lowerBound, upperBound - lowerBound);
}
this->revolutions += floor((oldValue - lowerBound) / (upperBound - lowerBound));
}
float WrappedFloat::limitValue(
const WrappedFloat& valueToLimit,
const float min,
const float max,
int* status)
{
WrappedFloat minWrapped = valueToLimit.withSameBounds(min);
WrappedFloat maxWrapped = valueToLimit.withSameBounds(max);
return limitValue(valueToLimit, minWrapped, maxWrapped, status);
}
float WrappedFloat::limitValue(
const WrappedFloat& valueToLimit,
const WrappedFloat& min,
const WrappedFloat& max,
int* status)
{
WrappedFloat::assertBoundsEqual(min, max);
WrappedFloat::assertBoundsEqual(valueToLimit, min);
if (min.getWrappedValue() == max.getWrappedValue())
{
return valueToLimit.getWrappedValue();
}
if (!valueToLimit.withinRange(min, max))
{
// valueToLimit is not "within" min and max
float targetMinDifference = valueToLimit.minDifference(min);
float targetMaxDifference = valueToLimit.minDifference(max);
if (fabs(targetMinDifference) < fabs(targetMaxDifference))
{
*status = 1;
return min.getWrappedValue();
}
else
{
*status = 2;
return max.getWrappedValue();
}
}
else
{
*status = 0;
return valueToLimit.getWrappedValue();
}
}
bool WrappedFloat::withinRange(const WrappedFloat& lowerBound, const WrappedFloat& upperBound) const
{
return (lowerBound.getWrappedValue() < upperBound.getWrappedValue() &&
(this->getWrappedValue() > lowerBound.getWrappedValue() &&
this->getWrappedValue() < upperBound.getWrappedValue())) ||
(lowerBound.getWrappedValue() > upperBound.getWrappedValue() &&
(this->getWrappedValue() > lowerBound.getWrappedValue() ||
this->getWrappedValue() < upperBound.getWrappedValue()));
}
float WrappedFloat::rangeOverlap(
const WrappedFloat& lowerA,
const WrappedFloat& upperA,
const WrappedFloat& lowerB,
const WrappedFloat& upperB)
{
assertBoundsEqual(lowerA, upperA);
assertBoundsEqual(upperA, lowerB);
assertBoundsEqual(lowerB, upperB);
float origin = lowerA.getLowerBound();
float offset = lowerA.getWrappedValue() - origin;
float upperAShifted = (upperA - offset).getWrappedValue();
float lowerBShifted = (lowerB - offset).getWrappedValue();
float upperBShifted = (upperB - offset).getWrappedValue();
if (upperBShifted < lowerBShifted)
{
float leftRange = std::min(upperBShifted, upperAShifted);
float rightRange = std::max(origin, upperAShifted - lowerBShifted);
return leftRange + rightRange;
}
return std::max(0.0f, std::min(upperAShifted, upperBShifted) - std::max(origin, lowerBShifted));
}
} // namespace algorithms
} // namespace tap