forked from mirrors/gecko-dev
		
	 126bd9e1a4
			
		
	
	
		126bd9e1a4
		
	
	
	
	
		
			
			This patch was generated automatically by the "modeline.py" script, available here: https://github.com/amccreight/moz-source-tools/blob/master/modeline.py For every file that is modified in this patch, the changes are as follows: (1) The patch changes the file to use the exact C++ mode lines from the Mozilla coding style guide, available here: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style#Mode_Line (2) The patch deletes any blank lines between the mode line & the MPL boilerplate comment. (3) If the file previously had the mode lines and MPL boilerplate in a single contiguous C++ comment, then the patch splits them into separate C++ comments, to match the boilerplate in the coding style. MozReview-Commit-ID: 77D61xpSmIl --HG-- extra : rebase_source : c6162fa3cf539a07177a19838324bf368faa162b
		
			
				
	
	
		
			342 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "BezierUtils.h"
 | |
| 
 | |
| #include "PathHelpers.h"
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace gfx {
 | |
| 
 | |
| Point
 | |
| GetBezierPoint(const Bezier& aBezier, Float t)
 | |
| {
 | |
|   Float s = 1.0f - t;
 | |
| 
 | |
|   return Point(
 | |
|     aBezier.mPoints[0].x * s * s * s +
 | |
|     3.0f * aBezier.mPoints[1].x * t * s * s +
 | |
|     3.0f * aBezier.mPoints[2].x * t * t * s +
 | |
|     aBezier.mPoints[3].x * t * t * t,
 | |
|     aBezier.mPoints[0].y * s * s * s +
 | |
|     3.0f * aBezier.mPoints[1].y * t * s * s +
 | |
|     3.0f * aBezier.mPoints[2].y * t * t * s +
 | |
|     aBezier.mPoints[3].y * t * t * t
 | |
|     );
 | |
| }
 | |
| 
 | |
| Point
 | |
| GetBezierDifferential(const Bezier& aBezier, Float t)
 | |
| {
 | |
|   // Return P'(t).
 | |
| 
 | |
|   Float s = 1.0f - t;
 | |
| 
 | |
|   return Point(
 | |
|     -3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
 | |
|              2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
 | |
|              (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
 | |
|     -3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
 | |
|              2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
 | |
|              (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
 | |
|     );
 | |
| }
 | |
| 
 | |
| Point
 | |
| GetBezierDifferential2(const Bezier& aBezier, Float t)
 | |
| {
 | |
|   // Return P''(t).
 | |
| 
 | |
|   Float s = 1.0f - t;
 | |
| 
 | |
|   return Point(
 | |
|     6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
 | |
|             (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
 | |
|             (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
 | |
|     6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
 | |
|             (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
 | |
|             (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
 | |
|     );
 | |
| }
 | |
| 
 | |
| Float
 | |
| GetBezierLength(const Bezier& aBezier, Float a, Float b)
 | |
| {
 | |
|   if (a < 0.5f && b > 0.5f) {
 | |
|     // To increase the accuracy, split into two parts.
 | |
|     return GetBezierLength(aBezier, a, 0.5f) +
 | |
|            GetBezierLength(aBezier, 0.5f, b);
 | |
|   }
 | |
| 
 | |
|   // Calculate length of simple bezier curve with Simpson's rule.
 | |
|   //            _
 | |
|   //           /  b
 | |
|   // length =  |    |P'(x)| dx
 | |
|   //          _/  a
 | |
|   //
 | |
|   //          b - a                   a + b
 | |
|   //        = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
 | |
|   //            6                       2
 | |
| 
 | |
|   Float fa = GetBezierDifferential(aBezier, a).Length();
 | |
|   Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
 | |
|   Float fb = GetBezierDifferential(aBezier, b).Length();
 | |
| 
 | |
|   return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
 | |
| }
 | |
| 
 | |
| static void
 | |
| SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
 | |
| {
 | |
|   // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
 | |
| 
 | |
|   Float s = 1.0f - t;
 | |
| 
 | |
|   Point tmp1;
 | |
|   Point tmp2;
 | |
| 
 | |
|   aSubBezier->mPoints[0] = aBezier.mPoints[0];
 | |
| 
 | |
|   aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
 | |
|   tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
 | |
|   tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
 | |
| 
 | |
|   aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
 | |
|   tmp1 = tmp1 * s + tmp2 * t;
 | |
| 
 | |
|   aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
 | |
| }
 | |
| 
 | |
| static void
 | |
| SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
 | |
| {
 | |
|   // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
 | |
| 
 | |
|   Float s = 1.0f - t;
 | |
| 
 | |
|   Point tmp1;
 | |
|   Point tmp2;
 | |
| 
 | |
|   aSubBezier->mPoints[3] = aBezier.mPoints[3];
 | |
| 
 | |
|   aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
 | |
|   tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
 | |
|   tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
 | |
| 
 | |
|   aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
 | |
|   tmp1 = tmp2 * s + tmp1 * t;
 | |
| 
 | |
|   aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
 | |
| }
 | |
| 
 | |
| void
 | |
| GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
 | |
| {
 | |
|   Bezier tmp;
 | |
|   SplitBezierB(&tmp, aBezier, t1);
 | |
| 
 | |
|   Float range = 1.0f - t1;
 | |
|   if (range == 0.0f) {
 | |
|     *aSubBezier = tmp;
 | |
|   } else {
 | |
|     SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static Point
 | |
| BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
 | |
|                          Float* aT)
 | |
| {
 | |
|   // Find a nearest point on bezier curve with Binary search.
 | |
|   // Called from FindBezierNearestPoint.
 | |
| 
 | |
|   Float lower = 0.0f;
 | |
|   Float upper = 1.0f;
 | |
|   Float t;
 | |
| 
 | |
|   Point P, lastP;
 | |
|   const size_t MAX_LOOP = 32;
 | |
|   const Float DIST_MARGIN = 0.1f;
 | |
|   const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
 | |
|   const Float DIFF = 0.0001f;
 | |
|   for (size_t i = 0; i < MAX_LOOP; i++) {
 | |
|     t = (upper + lower) / 2.0f;
 | |
|     P = GetBezierPoint(aBezier, t);
 | |
| 
 | |
|     // Check if it converged.
 | |
|     if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     Float distSquare = (P - aTarget).LengthSquare();
 | |
|     if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
 | |
|         distSquare) {
 | |
|       lower = t;
 | |
|     } else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
 | |
|                distSquare) {
 | |
|       upper = t;
 | |
|     } else {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     lastP = P;
 | |
|   }
 | |
| 
 | |
|   if (aT) {
 | |
|     *aT = t;
 | |
|   }
 | |
| 
 | |
|   return P;
 | |
| }
 | |
| 
 | |
| Point
 | |
| FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
 | |
|                        Float aInitialT, Float* aT)
 | |
| {
 | |
|   // Find a nearest point on bezier curve with Newton's method.
 | |
|   // It converges within 4 iterations in most cases.
 | |
|   //
 | |
|   //                   f(t_n)
 | |
|   //  t_{n+1} = t_n - ---------
 | |
|   //                   f'(t_n)
 | |
|   //
 | |
|   //             d                     2
 | |
|   //     f(t) = ---- | P(t) - aTarget |
 | |
|   //             dt
 | |
| 
 | |
|   Float t = aInitialT;
 | |
|   Point P;
 | |
|   Point lastP = GetBezierPoint(aBezier, t);
 | |
| 
 | |
|   const size_t MAX_LOOP = 4;
 | |
|   const Float DIST_MARGIN = 0.1f;
 | |
|   const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
 | |
|   for (size_t i = 0; i <= MAX_LOOP; i++) {
 | |
|     Point dP = GetBezierDifferential(aBezier, t);
 | |
|     Point ddP = GetBezierDifferential2(aBezier, t);
 | |
|     Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
 | |
|     Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
 | |
|                        aTarget.DotProduct(ddP));
 | |
|     t = t - f / df;
 | |
|     P = GetBezierPoint(aBezier, t);
 | |
|     if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
 | |
|       break;
 | |
|     }
 | |
|     lastP = P;
 | |
| 
 | |
|     if (i == MAX_LOOP) {
 | |
|       // If aInitialT is too bad, it won't converge in a few iterations,
 | |
|       // fallback to binary search.
 | |
|       return BisectBezierNearestPoint(aBezier, aTarget, aT);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aT) {
 | |
|     *aT = t;
 | |
|   }
 | |
| 
 | |
|   return P;
 | |
| }
 | |
| 
 | |
| void
 | |
| GetBezierPointsForCorner(Bezier* aBezier, Corner aCorner,
 | |
|                          const Point& aCornerPoint, const Size& aCornerSize)
 | |
| {
 | |
|   // Calculate bezier control points for elliptic arc.
 | |
| 
 | |
|   const Float signsList[4][2] = {
 | |
|     { +1.0f, +1.0f },
 | |
|     { -1.0f, +1.0f },
 | |
|     { -1.0f, -1.0f },
 | |
|     { +1.0f, -1.0f }
 | |
|   };
 | |
|   const Float (& signs)[2] = signsList[aCorner];
 | |
| 
 | |
|   aBezier->mPoints[0] = aCornerPoint;
 | |
|   aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
 | |
| 
 | |
|   aBezier->mPoints[1] = aBezier->mPoints[0];
 | |
|   aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
 | |
| 
 | |
|   aBezier->mPoints[3] = aCornerPoint;
 | |
|   aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
 | |
| 
 | |
|   aBezier->mPoints[2] = aBezier->mPoints[3];
 | |
|   aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
 | |
| }
 | |
| 
 | |
| Float
 | |
| GetQuarterEllipticArcLength(Float a, Float b)
 | |
| {
 | |
|   // Calculate the approximate length of a quarter elliptic arc formed by radii
 | |
|   // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
 | |
|   //           _                                                     _
 | |
|   //          |                                      2                |
 | |
|   //          |                           3 * (a - b)                 |
 | |
|   //  p =  PI | (a + b) + ------------------------------------------- |
 | |
|   //          |                                 2                 2   |
 | |
|   //          |_           10 * (a + b) + sqrt(a  + 14 * a * b + b ) _|
 | |
|   //
 | |
|   //           _                                                            _
 | |
|   //          |                                         2                    |
 | |
|   //          |                              3 * (a - b)                     |
 | |
|   //    =  PI | (a + b) + -------------------------------------------------- |
 | |
|   //          |                                           2              2   |
 | |
|   //          |_           10 * (a + b) + sqrt(4 * (a + b)  - 3 * (a - b) ) _|
 | |
|   //
 | |
|   //           _                                          _
 | |
|   //          |                          2                 |
 | |
|   //          |                     3 * S                  |
 | |
|   //    =  PI | A + -------------------------------------- |
 | |
|   //          |                               2        2   |
 | |
|   //          |_           10 * A + sqrt(4 * A  - 3 * S ) _|
 | |
|   //
 | |
|   // where A = a + b, S = a - b
 | |
| 
 | |
|   Float A = a + b, S = a - b;
 | |
|   Float A2 = A * A, S2 = S * S;
 | |
|   Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
 | |
|   return p / 4.0f;
 | |
| }
 | |
| 
 | |
| Float
 | |
| CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
 | |
|                                const Point& origin, Float width, Float height)
 | |
| {
 | |
|   // Solve following equations with n and return smaller n.
 | |
|   //
 | |
|   // /  (x, y) = P + n * normal
 | |
|   // |
 | |
|   // <   _            _ 2   _            _ 2
 | |
|   // |  | x - origin.x |   | y - origin.y |
 | |
|   // |  | ------------ | + | ------------ | = 1
 | |
|   // \  |_   width    _|   |_   height   _|
 | |
| 
 | |
|   Float a = (P.x - origin.x) / width;
 | |
|   Float b = normal.x / width;
 | |
|   Float c = (P.y - origin.y) / height;
 | |
|   Float d = normal.y / height;
 | |
| 
 | |
|   Float A = b * b + d * d;
 | |
|   Float B = a * b + c * d;
 | |
|   Float C = a * a + c * c - 1;
 | |
| 
 | |
|   Float S = sqrt(B * B - A * C);
 | |
| 
 | |
|   Float n1 = - B + S;
 | |
|   Float n2 = - B - S;
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   Float epsilon = (Float) 0.001;
 | |
|   MOZ_ASSERT(n1 >= -epsilon);
 | |
|   MOZ_ASSERT(n2 >= -epsilon);
 | |
| #endif
 | |
| 
 | |
|   return std::max((n1 < n2 ? n1 : n2) / A, (Float) 0.0);
 | |
| }
 | |
| 
 | |
| } // namespace gfx
 | |
| } // namespace mozilla
 |