| OLD | NEW | 
|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "ui/gfx/geometry/cubic_bezier.h" | 5 #include "ui/gfx/geometry/cubic_bezier.h" | 
| 6 | 6 | 
| 7 #include <algorithm> |  | 
| 8 #include <cmath> |  | 
| 9 |  | 
| 10 #include "base/logging.h" |  | 
| 11 |  | 
| 12 namespace gfx { | 7 namespace gfx { | 
| 13 | 8 | 
| 14 namespace { | 9 static const double kBezierEpsilon = 1e-7; | 
| 15 | 10 | 
| 16 static const double kBezierEpsilon = 1e-7; | 11 double CubicBezier::GetDefaultEpsilon() { | 
| 17 static const int MAX_STEPS = 30; | 12   return kBezierEpsilon; | 
| 18 |  | 
| 19 static double eval_bezier(double p1, double p2, double t) { |  | 
| 20   const double p1_times_3 = 3.0 * p1; |  | 
| 21   const double p2_times_3 = 3.0 * p2; |  | 
| 22   const double h3 = p1_times_3; |  | 
| 23   const double h1 = p1_times_3 - p2_times_3 + 1.0; |  | 
| 24   const double h2 = p2_times_3 - 6.0 * p1; |  | 
| 25   return t * (t * (t * h1 + h2) + h3); |  | 
| 26 } | 13 } | 
| 27 | 14 | 
| 28 static double eval_bezier_derivative(double p1, double p2, double t) { | 15 CubicBezier::CubicBezier(double p1x, double p1y, double p2x, double p2y) { | 
| 29   const double h1 = 9.0 * p1 - 9.0 * p2 + 3.0; | 16   InitCoefficients(p1x, p1y, p2x, p2y); | 
| 30   const double h2 = 6.0 * p2 - 12.0 * p1; | 17   InitGradients(p1x, p1y, p2x, p2y); | 
| 31   const double h3 = 3.0 * p1; | 18   InitRange(p1y, p2y); | 
| 32   return t * (t * h1 + h2) + h3; |  | 
| 33 } | 19 } | 
| 34 | 20 | 
| 35 // Finds t such that eval_bezier(x1, x2, t) = x. | 21 void CubicBezier::InitCoefficients(double p1x, | 
| 36 // There is a unique solution if x1 and x2 lie within (0, 1). | 22                                    double p1y, | 
| 37 static double bezier_interp(double x1, | 23                                    double p2x, | 
| 38                             double x2, | 24                                    double p2y) { | 
| 39                             double x) { | 25   // Calculate the polynomial coefficients, implicit first and last control | 
| 40   DCHECK_GE(1.0, x1); | 26   // points are (0,0) and (1,1). | 
| 41   DCHECK_LE(0.0, x1); | 27   cx = 3.0 * p1x; | 
| 42   DCHECK_GE(1.0, x2); | 28   bx = 3.0 * (p2x - p1x) - cx; | 
| 43   DCHECK_LE(0.0, x2); | 29   ax = 1.0 - cx - bx; | 
| 44 | 30 | 
| 45   x1 = std::min(std::max(x1, 0.0), 1.0); | 31   cy = 3.0 * p1y; | 
| 46   x2 = std::min(std::max(x2, 0.0), 1.0); | 32   by = 3.0 * (p2y - p1y) - cy; | 
| 47   x = std::min(std::max(x, 0.0), 1.0); | 33   ay = 1.0 - cy - by; | 
| 48 |  | 
| 49   // We're just going to do bisection for now (for simplicity), but we could |  | 
| 50   // easily do some newton steps if this turns out to be a bottleneck. |  | 
| 51   double t = 0.0; |  | 
| 52   double step = 1.0; |  | 
| 53   for (int i = 0; i < MAX_STEPS; ++i, step *= 0.5) { |  | 
| 54     const double error = eval_bezier(x1, x2, t) - x; |  | 
| 55     if (std::abs(error) < kBezierEpsilon) |  | 
| 56       break; |  | 
| 57     t += error > 0.0 ? -step : step; |  | 
| 58   } |  | 
| 59 |  | 
| 60   // We should have terminated the above loop because we got close to x, not |  | 
| 61   // because we exceeded MAX_STEPS. Do a DCHECK here to confirm. |  | 
| 62   DCHECK_GT(kBezierEpsilon, std::abs(eval_bezier(x1, x2, t) - x)); |  | 
| 63 |  | 
| 64   return t; |  | 
| 65 } | 34 } | 
| 66 | 35 | 
| 67 }  // namespace | 36 void CubicBezier::InitGradients(double p1x, | 
|  | 37                                 double p1y, | 
|  | 38                                 double p2x, | 
|  | 39                                 double p2y) { | 
|  | 40   // End-point gradients are used to calculate timing function results | 
|  | 41   // outside the range [0, 1]. | 
|  | 42   // | 
|  | 43   // There are three possibilities for the gradient at each end: | 
|  | 44   // (1) the closest control point is not horizontally coincident with regard to | 
|  | 45   //     (0, 0) or (1, 1). In this case the line between the end point and | 
|  | 46   //     the control point is tangent to the bezier at the end point. | 
|  | 47   // (2) the closest control point is coincident with the end point. In | 
|  | 48   //     this case the line between the end point and the far control | 
|  | 49   //     point is tangent to the bezier at the end point. | 
|  | 50   // (3) the closest control point is horizontally coincident with the end | 
|  | 51   //     point, but vertically distinct. In this case the gradient at the | 
|  | 52   //     end point is Infinite. However, this causes issues when | 
|  | 53   //     interpolating. As a result, we break down to a simple case of | 
|  | 54   //     0 gradient under these conditions. | 
| 68 | 55 | 
| 69 CubicBezier::CubicBezier(double x1, double y1, double x2, double y2) | 56   if (p1x > 0) | 
| 70     : x1_(x1), | 57     start_gradient_ = p1y / p1x; | 
| 71       y1_(y1), | 58   else if (!p1y && p2x > 0) | 
| 72       x2_(x2), | 59     start_gradient_ = p2y / p2x; | 
| 73       y2_(y2) { |  | 
| 74   InitGradients(); |  | 
| 75 } |  | 
| 76 |  | 
| 77 CubicBezier::~CubicBezier() { |  | 
| 78 } |  | 
| 79 |  | 
| 80 void CubicBezier::InitGradients() { |  | 
| 81   if (x1_ > 0) |  | 
| 82     start_gradient_ = y1_ / x1_; |  | 
| 83   else if (!y1_ && x2_ > 0) |  | 
| 84     start_gradient_ = y2_ / x2_; |  | 
| 85   else | 60   else | 
| 86     start_gradient_ = 0; | 61     start_gradient_ = 0; | 
| 87 | 62 | 
| 88   if (x2_ < 1) | 63   if (p2x < 1) | 
| 89     end_gradient_ = (y2_ - 1) / (x2_ - 1); | 64     end_gradient_ = (p2y - 1) / (p2x - 1); | 
| 90   else if (x2_ == 1 && x1_ < 1) | 65   else if (p2x == 1 && p1x < 1) | 
| 91     end_gradient_ = (y1_ - 1) / (x1_ - 1); | 66     end_gradient_ = (p1y - 1) / (p1x - 1); | 
| 92   else | 67   else | 
| 93     end_gradient_ = 0; | 68     end_gradient_ = 0; | 
| 94 } | 69 } | 
| 95 | 70 | 
| 96 double CubicBezier::Solve(double x) const { | 71 void CubicBezier::InitRange(double p1y, double p2y) { | 
| 97   if (x < 0) | 72   range_min_ = 0; | 
| 98     return start_gradient_ * x; | 73   range_max_ = 1; | 
| 99   if (x > 1) | 74   if (0 <= p1y && p1y < 1 && 0 <= p2y && p2y <= 1) | 
| 100     return 1.0 + end_gradient_ * (x - 1.0); |  | 
| 101 |  | 
| 102   return eval_bezier(y1_, y2_, bezier_interp(x1_, x2_, x)); |  | 
| 103 } |  | 
| 104 |  | 
| 105 double CubicBezier::Slope(double x) const { |  | 
| 106   double t = bezier_interp(x1_, x2_, x); |  | 
| 107   double dx_dt = eval_bezier_derivative(x1_, x2_, t); |  | 
| 108   double dy_dt = eval_bezier_derivative(y1_, y2_, t); |  | 
| 109   return dy_dt / dx_dt; |  | 
| 110 } |  | 
| 111 |  | 
| 112 void CubicBezier::Range(double* min, double* max) const { |  | 
| 113   *min = 0; |  | 
| 114   *max = 1; |  | 
| 115   if (0 <= y1_ && y1_ < 1 && 0 <= y2_ && y2_ <= 1) |  | 
| 116     return; | 75     return; | 
| 117 | 76 | 
| 118   // Represent the function's derivative in the form at^2 + bt + c. | 77   // Represent the function's derivative in the form at^2 + bt + c | 
|  | 78   // as in sampleCurveDerivativeY. | 
| 119   // (Technically this is (dy/dt)*(1/3), which is suitable for finding zeros | 79   // (Technically this is (dy/dt)*(1/3), which is suitable for finding zeros | 
| 120   // but does not actually give the slope of the curve.) | 80   // but does not actually give the slope of the curve.) | 
| 121   double a = 3 * (y1_ - y2_) + 1; | 81   const double a = 3.0 * ay; | 
| 122   double b = 2 * (y2_ - 2 * y1_); | 82   const double b = 2.0 * by; | 
| 123   double c = y1_; | 83   const double c = cy; | 
| 124 | 84 | 
| 125   // Check if the derivative is constant. | 85   // Check if the derivative is constant. | 
| 126   if (std::abs(a) < kBezierEpsilon && | 86   if (std::abs(a) < kBezierEpsilon && std::abs(b) < kBezierEpsilon) | 
| 127       std::abs(b) < kBezierEpsilon) |  | 
| 128     return; | 87     return; | 
| 129 | 88 | 
| 130   // Zeros of the function's derivative. | 89   // Zeros of the function's derivative. | 
| 131   double t_1 = 0; | 90   double t1 = 0; | 
| 132   double t_2 = 0; | 91   double t2 = 0; | 
| 133 | 92 | 
| 134   if (std::abs(a) < kBezierEpsilon) { | 93   if (std::abs(a) < kBezierEpsilon) { | 
| 135     // The function's derivative is linear. | 94     // The function's derivative is linear. | 
| 136     t_1 = -c / b; | 95     t1 = -c / b; | 
| 137   } else { | 96   } else { | 
| 138     // The function's derivative is a quadratic. We find the zeros of this | 97     // The function's derivative is a quadratic. We find the zeros of this | 
| 139     // quadratic using the quadratic formula. | 98     // quadratic using the quadratic formula. | 
| 140     double discriminant = b * b - 4 * a * c; | 99     double discriminant = b * b - 4 * a * c; | 
| 141     if (discriminant < 0) | 100     if (discriminant < 0) | 
| 142       return; | 101       return; | 
| 143     double discriminant_sqrt = sqrt(discriminant); | 102     double discriminant_sqrt = sqrt(discriminant); | 
| 144     t_1 = (-b + discriminant_sqrt) / (2 * a); | 103     t1 = (-b + discriminant_sqrt) / (2 * a); | 
| 145     t_2 = (-b - discriminant_sqrt) / (2 * a); | 104     t2 = (-b - discriminant_sqrt) / (2 * a); | 
| 146   } | 105   } | 
| 147 | 106 | 
| 148   double sol_1 = 0; | 107   double sol1 = 0; | 
| 149   double sol_2 = 0; | 108   double sol2 = 0; | 
| 150 | 109 | 
| 151   if (0 < t_1 && t_1 < 1) | 110   if (0 < t1 && t1 < 1) | 
| 152     sol_1 = eval_bezier(y1_, y2_, t_1); | 111     sol1 = SampleCurveY(t1); | 
| 153 | 112 | 
| 154   if (0 < t_2 && t_2 < 1) | 113   if (0 < t2 && t2 < 1) | 
| 155     sol_2 = eval_bezier(y1_, y2_, t_2); | 114     sol2 = SampleCurveY(t2); | 
| 156 | 115 | 
| 157   *min = std::min(std::min(*min, sol_1), sol_2); | 116   range_min_ = std::min(std::min(range_min_, sol1), sol2); | 
| 158   *max = std::max(std::max(*max, sol_1), sol_2); | 117   range_max_ = std::max(std::max(range_max_, sol1), sol2); | 
| 159 } | 118 } | 
| 160 | 119 | 
| 161 }  // namespace gfx | 120 }  // namespace gfx | 
| OLD | NEW | 
|---|