/* -*- 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 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);
}
staticvoid 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;
}
staticvoid 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;
}
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; constFloat DIST_MARGIN = 0.1f; constFloat DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN; constFloat 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;
}
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; constFloat DIST_MARGIN = 0.1f; constFloat 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.
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; // In the quadratic formulat B would be 2*(a*b+c*d), however we factor the 2 // out Here which cancels out later. Float B = a * b + c * d; Float C = a * a + c * c - 1.0;
Float signB = 1.0; if (B < 0.0) {
signB = -1.0;
}
// 2nd degree polynomials are typically computed using the formulae // r1 = -(B - sqrt(delta)) / (2 * A) // r2 = -(B + sqrt(delta)) / (2 * A) // However B - sqrt(delta) can be an inportant source of precision loss for // one of the roots when computing the difference between two similar and // large numbers. To avoid that we pick the root with no precision loss in r1 // and compute r2 using the Citardauq formula. // Factoring out 2 from B earlier let Float S = B + signB * sqrt(B * B - A * C); Float r1 = -S / A; Float r2 = -C / S;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.