| // Copyright 2016 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. |
| |
| #include "cobalt/renderer/rasterizer/common/streaming_best_fit_line.h" |
| |
| #include <math.h> |
| |
| #include <algorithm> |
| |
| namespace cobalt { |
| namespace renderer { |
| namespace rasterizer { |
| namespace common { |
| |
| namespace { |
| const int kMaxPointWindowSize = 4; |
| |
| Line FindBestFitLineFromPoints(const std::vector<math::PointF>& points) { |
| DCHECK_LT(1, points.size()); |
| |
| // Find the best fit least squares line through our set of |points|. |
| // http://mathworld.wolfram.com/LeastSquaresFitting.html |
| float sum_x = 0; |
| float sum_xx = 0; |
| float sum_y = 0; |
| float sum_xy = 0; |
| for (size_t i = 0; i < points.size(); ++i) { |
| const math::PointF& point = points[i]; |
| sum_x += point.x(); |
| sum_y += point.y(); |
| sum_xx += point.x() * point.x(); |
| sum_xy += point.x() * point.y(); |
| } |
| float denom = (points.size() * sum_xx - sum_x * sum_x); |
| DCHECK_NE(0.0f, denom) << "It must be guaranteed that the points do not all " |
| "form a vertical line."; |
| float inv_denom = 1.0f / denom; |
| float best_fit_y_intercept = (sum_y * sum_xx - sum_x * sum_xy) * inv_denom; |
| float best_fit_slope = (points.size() * sum_xy - sum_x * sum_y) * inv_denom; |
| |
| return Line(best_fit_y_intercept, best_fit_slope); |
| } |
| } // namespace |
| |
| void StreamingBestFitLine::AddValue(const math::PointF& point) { |
| // First add the point to our point set. |
| InsertPoint(point); |
| |
| DCHECK(!points_.empty()); |
| if (points_.size() > 1) { |
| // Determine the best fit line based only on the points in our point set. |
| Line best_fit_line = FindBestFitLineFromPoints(points_); |
| |
| // Possibly clamp the best fit line's y intercept and slope to non-negative |
| // values. |
| float new_y_intercept = force_non_negative_y_intercept_ |
| ? std::max(0.0f, best_fit_line.y_intercept()) |
| : best_fit_line.y_intercept(); |
| float new_slope = force_non_negative_slope_ |
| ? std::max(0.0f, best_fit_line.slope()) |
| : best_fit_line.slope(); |
| |
| // Update our best estimate line's parameters. Note that slope is |
| // internally stored as an angle since linear combination is more stable |
| // in that space. |
| y_intercept_.AddValue(new_y_intercept); |
| line_angle_radians_.AddValue(atan(new_slope)); |
| } |
| } |
| |
| void StreamingBestFitLine::InsertPoint(const math::PointF& point) { |
| // Our sliding window of points isn't exactly a simple sliding window. In |
| // order to ensure a diverse set of points in our point set, we first check |
| // if an existing point shares our x coordinate and if so, we simply replace |
| // that point. |
| for (size_t i = 0; i < points_.size(); ++i) { |
| if (points_[i].x() == point.x()) { |
| // Shuffle all points down. |
| size_t shuffle_to = i; |
| size_t shuffle_from = (i + 1) % kMaxPointWindowSize; |
| while (shuffle_from != next_point_position_) { |
| points_[shuffle_to] = points_[shuffle_from]; |
| shuffle_to = shuffle_from; |
| shuffle_from = (shuffle_from + 1) % kMaxPointWindowSize; |
| } |
| // Add our new point. |
| points_[shuffle_to] = point; |
| return; |
| } |
| } |
| |
| // If we get here, we have a point with a unique x-coordinate, so add it to |
| // the end of our point window. |
| if (points_.size() < kMaxPointWindowSize) { |
| points_.push_back(point); |
| } else { |
| points_[next_point_position_] = point; |
| next_point_position_ = (next_point_position_ + 1) % kMaxPointWindowSize; |
| } |
| } |
| |
| } // namespace common |
| } // namespace rasterizer |
| } // namespace renderer |
| } // namespace cobalt |