2012-08-27 14:11:33 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2012 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2012-01-25 18:57:23 +00:00
|
|
|
#include "QuadraticUtilities.h"
|
2012-03-27 13:23:51 +00:00
|
|
|
#include <math.h>
|
2012-01-25 18:57:23 +00:00
|
|
|
|
2012-08-21 13:13:52 +00:00
|
|
|
/*
|
|
|
|
|
|
|
|
Numeric Solutions (5.6) suggests to solve the quadratic by computing
|
|
|
|
|
|
|
|
Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
|
|
|
|
|
|
|
|
and using the roots
|
|
|
|
|
|
|
|
t1 = Q / A
|
|
|
|
t2 = C / Q
|
2012-08-23 18:14:13 +00:00
|
|
|
|
2012-08-21 13:13:52 +00:00
|
|
|
*/
|
|
|
|
|
2013-01-25 07:06:46 +00:00
|
|
|
|
2013-01-24 21:47:16 +00:00
|
|
|
int add_valid_ts(double s[], int realRoots, double* t) {
|
|
|
|
int foundRoots = 0;
|
|
|
|
for (int index = 0; index < realRoots; ++index) {
|
|
|
|
double tValue = s[index];
|
|
|
|
if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
|
|
|
|
if (approximately_less_than_zero(tValue)) {
|
|
|
|
tValue = 0;
|
|
|
|
} else if (approximately_greater_than_one(tValue)) {
|
|
|
|
tValue = 1;
|
|
|
|
}
|
|
|
|
for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
|
|
|
|
if (approximately_equal(t[idx2], tValue)) {
|
|
|
|
goto nextRoot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
t[foundRoots++] = tValue;
|
|
|
|
}
|
|
|
|
nextRoot:
|
|
|
|
;
|
|
|
|
}
|
|
|
|
return foundRoots;
|
|
|
|
}
|
|
|
|
|
2012-08-28 20:44:43 +00:00
|
|
|
// note: caller expects multiple results to be sorted smaller first
|
|
|
|
// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
|
|
|
|
// analysis of the quadratic equation, suggesting why the following looks at
|
|
|
|
// the sign of B -- and further suggesting that the greatest loss of precision
|
|
|
|
// is in b squared less two a c
|
2013-01-24 21:47:16 +00:00
|
|
|
int quadraticRootsValidT(double A, double B, double C, double t[2]) {
|
|
|
|
#if 0
|
2012-01-25 18:57:23 +00:00
|
|
|
B *= 2;
|
|
|
|
double square = B * B - 4 * A * C;
|
2012-09-14 14:19:30 +00:00
|
|
|
if (approximately_negative(square)) {
|
|
|
|
if (!approximately_positive(square)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
square = 0;
|
2012-01-25 18:57:23 +00:00
|
|
|
}
|
|
|
|
double squareRt = sqrt(square);
|
|
|
|
double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2;
|
|
|
|
int foundRoots = 0;
|
2012-08-21 13:13:52 +00:00
|
|
|
double ratio = Q / A;
|
2012-08-28 20:44:43 +00:00
|
|
|
if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
|
|
|
|
if (approximately_less_than_zero(ratio)) {
|
2012-08-21 13:13:52 +00:00
|
|
|
ratio = 0;
|
2012-08-28 20:44:43 +00:00
|
|
|
} else if (approximately_greater_than_one(ratio)) {
|
2012-08-21 13:13:52 +00:00
|
|
|
ratio = 1;
|
2012-04-17 11:40:34 +00:00
|
|
|
}
|
2012-08-28 20:44:43 +00:00
|
|
|
t[0] = ratio;
|
|
|
|
++foundRoots;
|
2012-01-25 18:57:23 +00:00
|
|
|
}
|
2012-08-21 13:13:52 +00:00
|
|
|
ratio = C / Q;
|
2012-08-28 20:44:43 +00:00
|
|
|
if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
|
|
|
|
if (approximately_less_than_zero(ratio)) {
|
2012-08-21 13:13:52 +00:00
|
|
|
ratio = 0;
|
2012-08-28 20:44:43 +00:00
|
|
|
} else if (approximately_greater_than_one(ratio)) {
|
2012-08-21 13:13:52 +00:00
|
|
|
ratio = 1;
|
2012-04-17 11:40:34 +00:00
|
|
|
}
|
2012-08-28 20:44:43 +00:00
|
|
|
if (foundRoots == 0 || !approximately_negative(ratio - t[0])) {
|
2012-08-23 15:24:42 +00:00
|
|
|
t[foundRoots++] = ratio;
|
2012-08-28 20:44:43 +00:00
|
|
|
} else if (!approximately_negative(t[0] - ratio)) {
|
|
|
|
t[foundRoots++] = t[0];
|
|
|
|
t[0] = ratio;
|
2012-08-23 15:24:42 +00:00
|
|
|
}
|
2012-01-25 18:57:23 +00:00
|
|
|
}
|
2013-01-24 21:47:16 +00:00
|
|
|
#else
|
|
|
|
double s[2];
|
|
|
|
int realRoots = quadraticRootsReal(A, B, C, s);
|
|
|
|
int foundRoots = add_valid_ts(s, realRoots, t);
|
|
|
|
#endif
|
2012-01-25 18:57:23 +00:00
|
|
|
return foundRoots;
|
|
|
|
}
|
2012-07-02 20:27:02 +00:00
|
|
|
|
2013-01-24 21:47:16 +00:00
|
|
|
// unlike quadratic roots, this does not discard real roots <= 0 or >= 1
|
|
|
|
int quadraticRootsReal(const double A, const double B, const double C, double s[2]) {
|
2013-01-28 19:25:51 +00:00
|
|
|
const double p = B / (2 * A);
|
|
|
|
const double q = C / A;
|
|
|
|
if (approximately_zero(A) && (approximately_zero_inverse(p) || approximately_zero_inverse(q))) {
|
2013-01-24 21:47:16 +00:00
|
|
|
if (approximately_zero(B)) {
|
|
|
|
s[0] = 0;
|
|
|
|
return C == 0;
|
|
|
|
}
|
|
|
|
s[0] = -C / B;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* normal form: x^2 + px + q = 0 */
|
|
|
|
const double p2 = p * p;
|
|
|
|
#if 0
|
|
|
|
double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q;
|
|
|
|
if (D <= 0) {
|
|
|
|
if (D < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
s[0] = -p;
|
|
|
|
SkDebugf("[%d] %1.9g\n", 1, s[0]);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
double sqrt_D = sqrt(D);
|
|
|
|
s[0] = sqrt_D - p;
|
|
|
|
s[1] = -sqrt_D - p;
|
|
|
|
SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
|
|
|
|
return 2;
|
|
|
|
#else
|
|
|
|
if (!AlmostEqualUlps(p2, q) && p2 < q) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
double sqrt_D = 0;
|
|
|
|
if (p2 > q) {
|
|
|
|
sqrt_D = sqrt(p2 - q);
|
|
|
|
}
|
|
|
|
s[0] = sqrt_D - p;
|
|
|
|
s[1] = -sqrt_D - p;
|
|
|
|
#if 0
|
|
|
|
if (AlmostEqualUlps(s[0], s[1])) {
|
|
|
|
SkDebugf("[%d] %1.9g\n", 1, s[0]);
|
|
|
|
} else {
|
|
|
|
SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 1 + !AlmostEqualUlps(s[0], s[1]);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-01-19 13:22:39 +00:00
|
|
|
static double derivativeAtT(const double* quad, double t) {
|
2012-07-02 20:27:02 +00:00
|
|
|
double a = t - 1;
|
|
|
|
double b = 1 - 2 * t;
|
|
|
|
double c = t;
|
2013-01-19 13:22:39 +00:00
|
|
|
return a * quad[0] + b * quad[2] + c * quad[4];
|
|
|
|
}
|
|
|
|
|
|
|
|
double dx_at_t(const Quadratic& quad, double t) {
|
|
|
|
return derivativeAtT(&quad[0].x, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
double dy_at_t(const Quadratic& quad, double t) {
|
|
|
|
return derivativeAtT(&quad[0].y, t);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dxdy_at_t(const Quadratic& quad, double t, _Point& dxy) {
|
|
|
|
double a = t - 1;
|
|
|
|
double b = 1 - 2 * t;
|
|
|
|
double c = t;
|
|
|
|
dxy.x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
|
|
|
|
dxy.y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
|
2012-07-02 20:27:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
|
|
|
|
double one_t = 1 - t;
|
|
|
|
double a = one_t * one_t;
|
|
|
|
double b = 2 * one_t * t;
|
|
|
|
double c = t * t;
|
|
|
|
if (&x) {
|
|
|
|
x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
|
|
|
|
}
|
|
|
|
if (&y) {
|
|
|
|
y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
|
|
|
|
}
|
|
|
|
}
|