| /* |
| * Copyright 2017 Google Inc. All Rights Reserved. |
| * |
| * 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. |
| */ |
| |
| #ifndef COBALT_MATH_LINEAR_INTERPOLATOR_H_ |
| #define COBALT_MATH_LINEAR_INTERPOLATOR_H_ |
| |
| #include <iterator> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/logging.h" |
| #include "starboard/types.h" |
| |
| namespace cobalt { |
| namespace math { |
| |
| // LinearInterpolator effectively generates outputs based on a table. This can |
| // be used for function approximation (ie. sine) or arbitrary functions that |
| // need specific values at specific inputs, and interpolated values otherwise. |
| // KeyT and ValueT can be any builtin integer and float point type. |
| // It's recommended that double types are used unless speed is required, then |
| // use other types. |
| // |
| // Performance: |
| // This table increases the computation on the order of O(n) of the number |
| // of interpolation points. However in practice the number of interpolation |
| // points is small enough (<20) where this linear search is faster than binary |
| // lookup. |
| // |
| // Discontinuous values: |
| // This table supports discontinuous values. To enable this feature, insert a |
| // pair of duplicate keys, each with a different value. See example below. |
| // |
| // Example (continuous): |
| // LinearInterpolator<float, float> interp; |
| // interp.Add(0, 0); |
| // interp.Add(1, 2); |
| // EXPECT_FLOAT_EQ(0.f, interp.Map(0.0f)); |
| // EXPECT_FLOAT_EQ(1.f, interp.Map(0.5f)); |
| // EXPECT_FLOAT_EQ(2.f, interp.Map(1.0f)); |
| // |
| // Example (discontinuous): |
| // LinearInterpolator<double, float> interp; |
| // interp.Add(0, 0); |
| // interp.Add(1, 1); // Discontinuity at input = 1. |
| // interp.Add(1, 3); |
| // interp.Add(2, 4); |
| // static const double kErrorThreshold = .1f; |
| // static const double kEpsilon = std::numeric_limits<double>::epsilon()*4.0; |
| // EXPECT_NEAR(1.0, interp.Map(1.f - kEpsilon), kErrorThreshold); |
| // EXPECT_FLOAT_EQ(3, interp.Map(1.f)); |
| // EXPECT_NEAR(3.0, interp.Map(1.f + kEpsilon), kErrorThreshold); |
| template <typename KeyT, typename ValueT> |
| class LinearInterpolator { |
| public: |
| ValueT Map(const KeyT& key) const { |
| return Interp(key); |
| } |
| |
| // Adds a key to the table. For a continuous piecewise line-curve each key |
| // added must be greater in value than the last key added. |
| // If duplicate keys are entered sequentially then this will introduce |
| // a discontinuity - a big jump in the returned value from Map() when the |
| // input crosses the discontinuity point. |
| void Add(const KeyT& key, const ValueT& val) { |
| if (!table_.empty()) { |
| DCHECK_LE(table_.back().first, key) << "Keys are not in order."; |
| } |
| table_.push_back(std::pair<KeyT, ValueT>(key, val)); |
| } |
| void Clear() { |
| table_.clear(); |
| } |
| |
| private: |
| ValueT Interp(const KeyT& k) const { |
| if (table_.empty()) { |
| return ValueT(0); |
| } |
| |
| // Contains the indices which reference keys before and after the key. |
| std::pair<size_t, size_t> indices = SelectInterpPoints(k); |
| |
| // Singular value, no interpolation needed. |
| if (indices.first == indices.second) { |
| return table_[indices.first].second; |
| } |
| |
| // Perform the interpolation with the given key & value lines. |
| const std::pair<KeyT, ValueT>& curr = table_[indices.first]; |
| const std::pair<KeyT, ValueT>& next = table_[indices.second]; |
| |
| // Duplicate keys, select the second value. This prevents |
| // LinearInterpolation() from dividing by 0 for duplicate keys. |
| if (curr.first == next.first) { |
| return next.second; |
| } |
| |
| return LinearInterpolation(k, curr.first, next.first, |
| curr.second, next.second); |
| } |
| |
| // Linearly walk through the table looking for a pair of interpolation |
| // points. Linear walking must be used because this table uses duplicate |
| // keys generate discontinuous outputs. |
| std::pair<size_t, size_t> SelectInterpPoints(const KeyT& k) const { |
| DCHECK(!table_.empty()); |
| if (k < table_[0].first) { |
| return std::pair<size_t, size_t>(0, 0); |
| } |
| |
| for (size_t i = 0; i < table_.size() - 1; ++i) { |
| const std::pair<KeyT, ValueT>& curr = table_[i]; |
| const std::pair<KeyT, ValueT>& next = table_[i+1]; |
| |
| if (curr.first <= k && k < next.first) { |
| return std::pair<size_t, size_t>(i, i+1); |
| } |
| } |
| |
| return std::pair<size_t, size_t>(table_.size() - 1, table_.size() - 1); |
| } |
| |
| // Maps a value in the range [x1, x2] to the range [y1, y2]. |
| static const ValueT LinearInterpolation(const KeyT& t, |
| const KeyT& x1, const KeyT& x2, |
| const ValueT& y1, const ValueT& y2) { |
| ValueT return_val = |
| static_cast<ValueT>((t - x1) * (y2 - y1) / (x2 - x1) + y1); |
| return return_val; |
| } |
| |
| typedef std::vector<std::pair<KeyT, ValueT> > VectorType; |
| VectorType table_; |
| }; |
| |
| } // namespace math |
| } // namespace cobalt |
| |
| #endif // COBALT_MATH_LINEAR_INTERPOLATOR_H_ |