1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "BezierUtils.h"
9 #include "PathHelpers.h"
14 Point
GetBezierPoint(const Bezier
& aBezier
, Float t
) {
17 return Point(aBezier
.mPoints
[0].x
* s
* s
* s
+
18 3.0f
* aBezier
.mPoints
[1].x
* t
* s
* s
+
19 3.0f
* aBezier
.mPoints
[2].x
* t
* t
* s
+
20 aBezier
.mPoints
[3].x
* t
* t
* t
,
21 aBezier
.mPoints
[0].y
* s
* s
* s
+
22 3.0f
* aBezier
.mPoints
[1].y
* t
* s
* s
+
23 3.0f
* aBezier
.mPoints
[2].y
* t
* t
* s
+
24 aBezier
.mPoints
[3].y
* t
* t
* t
);
27 Point
GetBezierDifferential(const Bezier
& aBezier
, Float t
) {
33 -3.0f
* ((aBezier
.mPoints
[0].x
- aBezier
.mPoints
[1].x
) * s
* s
+
34 2.0f
* (aBezier
.mPoints
[1].x
- aBezier
.mPoints
[2].x
) * t
* s
+
35 (aBezier
.mPoints
[2].x
- aBezier
.mPoints
[3].x
) * t
* t
),
36 -3.0f
* ((aBezier
.mPoints
[0].y
- aBezier
.mPoints
[1].y
) * s
* s
+
37 2.0f
* (aBezier
.mPoints
[1].y
- aBezier
.mPoints
[2].y
) * t
* s
+
38 (aBezier
.mPoints
[2].y
- aBezier
.mPoints
[3].y
) * t
* t
));
41 Point
GetBezierDifferential2(const Bezier
& aBezier
, Float t
) {
46 return Point(6.0f
* ((aBezier
.mPoints
[0].x
- aBezier
.mPoints
[1].x
) * s
-
47 (aBezier
.mPoints
[1].x
- aBezier
.mPoints
[2].x
) * (s
- t
) -
48 (aBezier
.mPoints
[2].x
- aBezier
.mPoints
[3].x
) * t
),
49 6.0f
* ((aBezier
.mPoints
[0].y
- aBezier
.mPoints
[1].y
) * s
-
50 (aBezier
.mPoints
[1].y
- aBezier
.mPoints
[2].y
) * (s
- t
) -
51 (aBezier
.mPoints
[2].y
- aBezier
.mPoints
[3].y
) * t
));
54 Float
GetBezierLength(const Bezier
& aBezier
, Float a
, Float b
) {
55 if (a
< 0.5f
&& b
> 0.5f
) {
56 // To increase the accuracy, split into two parts.
57 return GetBezierLength(aBezier
, a
, 0.5f
) +
58 GetBezierLength(aBezier
, 0.5f
, b
);
61 // Calculate length of simple bezier curve with Simpson's rule.
64 // length = | |P'(x)| dx
68 // = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
71 Float fa
= GetBezierDifferential(aBezier
, a
).Length();
72 Float fab
= GetBezierDifferential(aBezier
, (a
+ b
) / 2.0f
).Length();
73 Float fb
= GetBezierDifferential(aBezier
, b
).Length();
75 return (b
- a
) / 6.0f
* (fa
+ 4.0f
* fab
+ fb
);
78 static void SplitBezierA(Bezier
* aSubBezier
, const Bezier
& aBezier
, Float t
) {
79 // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
86 aSubBezier
->mPoints
[0] = aBezier
.mPoints
[0];
88 aSubBezier
->mPoints
[1] = aBezier
.mPoints
[0] * s
+ aBezier
.mPoints
[1] * t
;
89 tmp1
= aBezier
.mPoints
[1] * s
+ aBezier
.mPoints
[2] * t
;
90 tmp2
= aBezier
.mPoints
[2] * s
+ aBezier
.mPoints
[3] * t
;
92 aSubBezier
->mPoints
[2] = aSubBezier
->mPoints
[1] * s
+ tmp1
* t
;
93 tmp1
= tmp1
* s
+ tmp2
* t
;
95 aSubBezier
->mPoints
[3] = aSubBezier
->mPoints
[2] * s
+ tmp1
* t
;
98 static void SplitBezierB(Bezier
* aSubBezier
, const Bezier
& aBezier
, Float t
) {
99 // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
106 aSubBezier
->mPoints
[3] = aBezier
.mPoints
[3];
108 aSubBezier
->mPoints
[2] = aBezier
.mPoints
[2] * s
+ aBezier
.mPoints
[3] * t
;
109 tmp1
= aBezier
.mPoints
[1] * s
+ aBezier
.mPoints
[2] * t
;
110 tmp2
= aBezier
.mPoints
[0] * s
+ aBezier
.mPoints
[1] * t
;
112 aSubBezier
->mPoints
[1] = tmp1
* s
+ aSubBezier
->mPoints
[2] * t
;
113 tmp1
= tmp2
* s
+ tmp1
* t
;
115 aSubBezier
->mPoints
[0] = tmp1
* s
+ aSubBezier
->mPoints
[1] * t
;
118 void GetSubBezier(Bezier
* aSubBezier
, const Bezier
& aBezier
, Float t1
,
121 SplitBezierB(&tmp
, aBezier
, t1
);
123 Float range
= 1.0f
- t1
;
127 SplitBezierA(aSubBezier
, tmp
, (t2
- t1
) / range
);
131 static Point
BisectBezierNearestPoint(const Bezier
& aBezier
,
132 const Point
& aTarget
, Float
* aT
) {
133 // Find a nearest point on bezier curve with Binary search.
134 // Called from FindBezierNearestPoint.
141 const size_t MAX_LOOP
= 32;
142 const Float DIST_MARGIN
= 0.1f
;
143 const Float DIST_MARGIN_SQUARE
= DIST_MARGIN
* DIST_MARGIN
;
144 const Float DIFF
= 0.0001f
;
145 for (size_t i
= 0; i
< MAX_LOOP
; i
++) {
146 t
= (upper
+ lower
) / 2.0f
;
147 P
= GetBezierPoint(aBezier
, t
);
149 // Check if it converged.
150 if (i
> 0 && (lastP
- P
).LengthSquare() < DIST_MARGIN_SQUARE
) {
154 Float distSquare
= (P
- aTarget
).LengthSquare();
155 if ((GetBezierPoint(aBezier
, t
+ DIFF
) - aTarget
).LengthSquare() <
158 } else if ((GetBezierPoint(aBezier
, t
- DIFF
) - aTarget
).LengthSquare() <
175 Point
FindBezierNearestPoint(const Bezier
& aBezier
, const Point
& aTarget
,
176 Float aInitialT
, Float
* aT
) {
177 // Find a nearest point on bezier curve with Newton's method.
178 // It converges within 4 iterations in most cases.
181 // t_{n+1} = t_n - ---------
185 // f(t) = ---- | P(t) - aTarget |
190 Point lastP
= GetBezierPoint(aBezier
, t
);
192 const size_t MAX_LOOP
= 4;
193 const Float DIST_MARGIN
= 0.1f
;
194 const Float DIST_MARGIN_SQUARE
= DIST_MARGIN
* DIST_MARGIN
;
195 for (size_t i
= 0; i
<= MAX_LOOP
; i
++) {
196 Point dP
= GetBezierDifferential(aBezier
, t
);
197 Point ddP
= GetBezierDifferential2(aBezier
, t
);
198 Float f
= 2.0f
* (lastP
.DotProduct(dP
) - aTarget
.DotProduct(dP
));
199 Float df
= 2.0f
* (dP
.DotProduct(dP
) + lastP
.DotProduct(ddP
) -
200 aTarget
.DotProduct(ddP
));
202 P
= GetBezierPoint(aBezier
, t
);
203 if ((P
- lastP
).LengthSquare() < DIST_MARGIN_SQUARE
) {
209 // If aInitialT is too bad, it won't converge in a few iterations,
210 // fallback to binary search.
211 return BisectBezierNearestPoint(aBezier
, aTarget
, aT
);
222 void GetBezierPointsForCorner(Bezier
* aBezier
, Corner aCorner
,
223 const Point
& aCornerPoint
,
224 const Size
& aCornerSize
) {
225 // Calculate bezier control points for elliptic arc.
227 const Float signsList
[4][2] = {
228 {+1.0f
, +1.0f
}, {-1.0f
, +1.0f
}, {-1.0f
, -1.0f
}, {+1.0f
, -1.0f
}};
229 const Float(&signs
)[2] = signsList
[aCorner
];
231 aBezier
->mPoints
[0] = aCornerPoint
;
232 aBezier
->mPoints
[0].x
+= signs
[0] * aCornerSize
.width
;
234 aBezier
->mPoints
[1] = aBezier
->mPoints
[0];
235 aBezier
->mPoints
[1].x
-= signs
[0] * aCornerSize
.width
* kKappaFactor
;
237 aBezier
->mPoints
[3] = aCornerPoint
;
238 aBezier
->mPoints
[3].y
+= signs
[1] * aCornerSize
.height
;
240 aBezier
->mPoints
[2] = aBezier
->mPoints
[3];
241 aBezier
->mPoints
[2].y
-= signs
[1] * aCornerSize
.height
* kKappaFactor
;
244 Float
GetQuarterEllipticArcLength(Float a
, Float b
) {
245 // Calculate the approximate length of a quarter elliptic arc formed by radii
246 // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
250 // p = PI | (a + b) + ------------------------------------------- |
252 // |_ 10 * (a + b) + sqrt(a + 14 * a * b + b ) _|
257 // = PI | (a + b) + -------------------------------------------------- |
259 // |_ 10 * (a + b) + sqrt(4 * (a + b) - 3 * (a - b) ) _|
264 // = PI | A + -------------------------------------- |
266 // |_ 10 * A + sqrt(4 * A - 3 * S ) _|
268 // where A = a + b, S = a - b
270 Float A
= a
+ b
, S
= a
- b
;
271 Float A2
= A
* A
, S2
= S
* S
;
272 Float p
= M_PI
* (A
+ 3.0f
* S2
/ (10.0f
* A
+ sqrt(4.0f
* A2
- 3.0f
* S2
)));
276 Float
CalculateDistanceToEllipticArc(const Point
& P
, const Point
& normal
,
277 const Point
& origin
, Float width
,
279 // Solve following equations with n and return smaller n.
281 // / (x, y) = P + n * normal
284 // | | x - origin.x | | y - origin.y |
285 // | | ------------ | + | ------------ | = 1
286 // \ |_ width _| |_ height _|
288 Float a
= (P
.x
- origin
.x
) / width
;
289 Float b
= normal
.x
/ width
;
290 Float c
= (P
.y
- origin
.y
) / height
;
291 Float d
= normal
.y
/ height
;
293 Float A
= b
* b
+ d
* d
;
294 // In the quadratic formulat B would be 2*(a*b+c*d), however we factor the 2
295 // out Here which cancels out later.
296 Float B
= a
* b
+ c
* d
;
297 Float C
= a
* a
+ c
* c
- 1.0;
304 // 2nd degree polynomials are typically computed using the formulae
305 // r1 = -(B - sqrt(delta)) / (2 * A)
306 // r2 = -(B + sqrt(delta)) / (2 * A)
307 // However B - sqrt(delta) can be an inportant source of precision loss for
308 // one of the roots when computing the difference between two similar and
309 // large numbers. To avoid that we pick the root with no precision loss in r1
310 // and compute r2 using the Citardauq formula.
311 // Factoring out 2 from B earlier let
312 Float S
= B
+ signB
* sqrt(B
* B
- A
* C
);
317 Float epsilon
= (Float
)0.001;
318 MOZ_ASSERT(r1
>= -epsilon
);
319 MOZ_ASSERT(r2
>= -epsilon
);
322 return std::max((r1
< r2
? r1
: r2
), (Float
)0.0);
326 } // namespace mozilla