caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 1 | #include "DataTypes.h" |
| 2 | #include "Extrema.h" |
| 3 | |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 4 | static int validUnitDivide(double numer, double denom, double* ratio) |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 5 | { |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 6 | if (numer < 0) { |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 7 | numer = -numer; |
| 8 | denom = -denom; |
| 9 | } |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 10 | if (denom == 0 || numer == 0 || numer >= denom) |
| 11 | return 0; |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 12 | double r = numer / denom; |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 13 | if (r == 0) { // catch underflow if numer <<<< denom |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 14 | return 0; |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 15 | } |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 16 | *ratio = r; |
| 17 | return 1; |
| 18 | } |
| 19 | |
| 20 | /** From Numerical Recipes in C. |
| 21 | |
| 22 | Q = -1/2 (B + sign(B) sqrt[B*B - 4*A*C]) |
| 23 | x1 = Q / A |
| 24 | x2 = C / Q |
| 25 | */ |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 26 | static int findUnitQuadRoots(double A, double B, double C, double roots[2]) |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 27 | { |
| 28 | if (A == 0) |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 29 | return validUnitDivide(-C, B, roots); |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 30 | |
| 31 | double* r = roots; |
| 32 | |
| 33 | double R = B*B - 4*A*C; |
| 34 | if (R < 0) { // complex roots |
| 35 | return 0; |
| 36 | } |
| 37 | R = sqrt(R); |
| 38 | |
| 39 | double Q = (B < 0) ? -(B-R)/2 : -(B+R)/2; |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 40 | r += validUnitDivide(Q, A, r); |
| 41 | r += validUnitDivide(C, Q, r); |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 42 | if (r - roots == 2 && approximately_equal(roots[0], roots[1])) { // nearly-equal? |
| 43 | r -= 1; // skip the double root |
| 44 | } |
| 45 | return (int)(r - roots); |
| 46 | } |
| 47 | |
| 48 | /** Cubic'(t) = At^2 + Bt + C, where |
| 49 | A = 3(-a + 3(b - c) + d) |
| 50 | B = 6(a - 2b + c) |
| 51 | C = 3(b - a) |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 52 | Solve for t, keeping only those that fit between 0 < t < 1 |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 53 | */ |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 54 | int findExtrema(double a, double b, double c, double d, double tValues[2]) |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 55 | { |
| 56 | // we divide A,B,C by 3 to simplify |
| 57 | double A = d - a + 3*(b - c); |
| 58 | double B = 2*(a - b - b + c); |
| 59 | double C = b - a; |
| 60 | |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 61 | return findUnitQuadRoots(A, B, C, tValues); |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 62 | } |
| 63 | |
| 64 | /** Quad'(t) = At + B, where |
| 65 | A = 2(a - 2b + c) |
| 66 | B = 2(b - a) |
| 67 | Solve for t, only if it fits between 0 < t < 1 |
| 68 | */ |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 69 | int findExtrema(double a, double b, double c, double tValue[1]) |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 70 | { |
| 71 | /* At + B == 0 |
| 72 | t = -B / A |
| 73 | */ |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame^] | 74 | return validUnitDivide(a - b, a - b - b + c, tValue); |
caryclark@google.com | 639df89 | 2012-01-10 21:46:10 +0000 | [diff] [blame] | 75 | } |