blob: 475ec13b7d3ad0c5c12f9349138b117efd9bcd9c [file] [log] [blame]
caryclark@google.com9e49fb62012-08-27 14:11:33 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.com27accef2012-01-25 18:57:23 +00007#include "QuadraticUtilities.h"
caryclark@google.com9f602912013-01-24 21:47:16 +00008#include "SkTypes.h"
caryclark@google.comd88e0892012-03-27 13:23:51 +00009#include <math.h>
caryclark@google.com27accef2012-01-25 18:57:23 +000010
caryclark@google.com03f97062012-08-21 13:13:52 +000011/*
12
13Numeric Solutions (5.6) suggests to solve the quadratic by computing
14
15 Q = -1/2(B + sgn(B)Sqrt(B^2 - 4 A C))
16
17and using the roots
18
19 t1 = Q / A
20 t2 = C / Q
rmistry@google.comd6176b02012-08-23 18:14:13 +000021
caryclark@google.com03f97062012-08-21 13:13:52 +000022*/
23
caryclark@google.com9f602912013-01-24 21:47:16 +000024
25int add_valid_ts(double s[], int realRoots, double* t) {
26 int foundRoots = 0;
27 for (int index = 0; index < realRoots; ++index) {
28 double tValue = s[index];
29 if (approximately_zero_or_more(tValue) && approximately_one_or_less(tValue)) {
30 if (approximately_less_than_zero(tValue)) {
31 tValue = 0;
32 } else if (approximately_greater_than_one(tValue)) {
33 tValue = 1;
34 }
35 for (int idx2 = 0; idx2 < foundRoots; ++idx2) {
36 if (approximately_equal(t[idx2], tValue)) {
37 goto nextRoot;
38 }
39 }
40 t[foundRoots++] = tValue;
41 }
42nextRoot:
43 ;
44 }
45 return foundRoots;
46}
47
caryclark@google.coma7e483d2012-08-28 20:44:43 +000048// note: caller expects multiple results to be sorted smaller first
49// note: http://en.wikipedia.org/wiki/Loss_of_significance has an interesting
50// analysis of the quadratic equation, suggesting why the following looks at
51// the sign of B -- and further suggesting that the greatest loss of precision
52// is in b squared less two a c
caryclark@google.com9f602912013-01-24 21:47:16 +000053int quadraticRootsValidT(double A, double B, double C, double t[2]) {
54#if 0
caryclark@google.com27accef2012-01-25 18:57:23 +000055 B *= 2;
56 double square = B * B - 4 * A * C;
caryclark@google.com235f56a2012-09-14 14:19:30 +000057 if (approximately_negative(square)) {
58 if (!approximately_positive(square)) {
59 return 0;
60 }
61 square = 0;
caryclark@google.com27accef2012-01-25 18:57:23 +000062 }
63 double squareRt = sqrt(square);
64 double Q = (B + (B < 0 ? -squareRt : squareRt)) / -2;
65 int foundRoots = 0;
caryclark@google.com03f97062012-08-21 13:13:52 +000066 double ratio = Q / A;
caryclark@google.coma7e483d2012-08-28 20:44:43 +000067 if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
68 if (approximately_less_than_zero(ratio)) {
caryclark@google.com03f97062012-08-21 13:13:52 +000069 ratio = 0;
caryclark@google.coma7e483d2012-08-28 20:44:43 +000070 } else if (approximately_greater_than_one(ratio)) {
caryclark@google.com03f97062012-08-21 13:13:52 +000071 ratio = 1;
caryclark@google.com78e17132012-04-17 11:40:34 +000072 }
caryclark@google.coma7e483d2012-08-28 20:44:43 +000073 t[0] = ratio;
74 ++foundRoots;
caryclark@google.com27accef2012-01-25 18:57:23 +000075 }
caryclark@google.com03f97062012-08-21 13:13:52 +000076 ratio = C / Q;
caryclark@google.coma7e483d2012-08-28 20:44:43 +000077 if (approximately_zero_or_more(ratio) && approximately_one_or_less(ratio)) {
78 if (approximately_less_than_zero(ratio)) {
caryclark@google.com03f97062012-08-21 13:13:52 +000079 ratio = 0;
caryclark@google.coma7e483d2012-08-28 20:44:43 +000080 } else if (approximately_greater_than_one(ratio)) {
caryclark@google.com03f97062012-08-21 13:13:52 +000081 ratio = 1;
caryclark@google.com78e17132012-04-17 11:40:34 +000082 }
caryclark@google.coma7e483d2012-08-28 20:44:43 +000083 if (foundRoots == 0 || !approximately_negative(ratio - t[0])) {
caryclark@google.comc899ad92012-08-23 15:24:42 +000084 t[foundRoots++] = ratio;
caryclark@google.coma7e483d2012-08-28 20:44:43 +000085 } else if (!approximately_negative(t[0] - ratio)) {
86 t[foundRoots++] = t[0];
87 t[0] = ratio;
caryclark@google.comc899ad92012-08-23 15:24:42 +000088 }
caryclark@google.com27accef2012-01-25 18:57:23 +000089 }
caryclark@google.com9f602912013-01-24 21:47:16 +000090#else
91 double s[2];
92 int realRoots = quadraticRootsReal(A, B, C, s);
93 int foundRoots = add_valid_ts(s, realRoots, t);
94#endif
caryclark@google.com27accef2012-01-25 18:57:23 +000095 return foundRoots;
96}
caryclark@google.com8dcf1142012-07-02 20:27:02 +000097
caryclark@google.com9f602912013-01-24 21:47:16 +000098// unlike quadratic roots, this does not discard real roots <= 0 or >= 1
99int quadraticRootsReal(const double A, const double B, const double C, double s[2]) {
100 if (approximately_zero(A)) {
101 if (approximately_zero(B)) {
102 s[0] = 0;
103 return C == 0;
104 }
105 s[0] = -C / B;
106 return 1;
107 }
108 /* normal form: x^2 + px + q = 0 */
109 const double p = B / (2 * A);
110 const double q = C / A;
111 const double p2 = p * p;
112#if 0
113 double D = AlmostEqualUlps(p2, q) ? 0 : p2 - q;
114 if (D <= 0) {
115 if (D < 0) {
116 return 0;
117 }
118 s[0] = -p;
119 SkDebugf("[%d] %1.9g\n", 1, s[0]);
120 return 1;
121 }
122 double sqrt_D = sqrt(D);
123 s[0] = sqrt_D - p;
124 s[1] = -sqrt_D - p;
125 SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
126 return 2;
127#else
128 if (!AlmostEqualUlps(p2, q) && p2 < q) {
129 return 0;
130 }
131 double sqrt_D = 0;
132 if (p2 > q) {
133 sqrt_D = sqrt(p2 - q);
134 }
135 s[0] = sqrt_D - p;
136 s[1] = -sqrt_D - p;
137#if 0
138 if (AlmostEqualUlps(s[0], s[1])) {
139 SkDebugf("[%d] %1.9g\n", 1, s[0]);
140 } else {
141 SkDebugf("[%d] %1.9g %1.9g\n", 2, s[0], s[1]);
142 }
143#endif
144 return 1 + !AlmostEqualUlps(s[0], s[1]);
145#endif
146}
147
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000148static double derivativeAtT(const double* quad, double t) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000149 double a = t - 1;
150 double b = 1 - 2 * t;
151 double c = t;
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000152 return a * quad[0] + b * quad[2] + c * quad[4];
153}
154
155double dx_at_t(const Quadratic& quad, double t) {
156 return derivativeAtT(&quad[0].x, t);
157}
158
159double dy_at_t(const Quadratic& quad, double t) {
160 return derivativeAtT(&quad[0].y, t);
161}
162
163void dxdy_at_t(const Quadratic& quad, double t, _Point& dxy) {
164 double a = t - 1;
165 double b = 1 - 2 * t;
166 double c = t;
167 dxy.x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
168 dxy.y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000169}
170
171void xy_at_t(const Quadratic& quad, double t, double& x, double& y) {
172 double one_t = 1 - t;
173 double a = one_t * one_t;
174 double b = 2 * one_t * t;
175 double c = t * t;
176 if (&x) {
177 x = a * quad[0].x + b * quad[1].x + c * quad[2].x;
178 }
179 if (&y) {
180 y = a * quad[0].y + b * quad[1].y + c * quad[2].y;
181 }
182}