caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 1 | #include "CurveIntersection.h" |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 2 | #include "CubicUtilities.h" |
| 3 | #include "Intersections.h" |
| 4 | #include "LineUtilities.h" |
| 5 | |
| 6 | /* |
| 7 | Find the interection of a line and cubic by solving for valid t values. |
| 8 | |
| 9 | Analogous to line-quadratic intersection, solve line-cubic intersection by |
| 10 | representing the cubic as: |
| 11 | x = a(1-t)^3 + 2b(1-t)^2t + c(1-t)t^2 + dt^3 |
| 12 | y = e(1-t)^3 + 2f(1-t)^2t + g(1-t)t^2 + ht^3 |
| 13 | and the line as: |
| 14 | y = i*x + j (if the line is more horizontal) |
| 15 | or: |
| 16 | x = i*y + j (if the line is more vertical) |
| 17 | |
| 18 | Then using Mathematica, solve for the values of t where the cubic intersects the |
| 19 | line: |
| 20 | |
| 21 | (in) Resultant[ |
| 22 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x, |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 23 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - i*x - j, x] |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 24 | (out) -e + j + |
| 25 | 3 e t - 3 f t - |
| 26 | 3 e t^2 + 6 f t^2 - 3 g t^2 + |
| 27 | e t^3 - 3 f t^3 + 3 g t^3 - h t^3 + |
| 28 | i ( a - |
| 29 | 3 a t + 3 b t + |
| 30 | 3 a t^2 - 6 b t^2 + 3 c t^2 - |
| 31 | a t^3 + 3 b t^3 - 3 c t^3 + d t^3 ) |
| 32 | |
| 33 | if i goes to infinity, we can rewrite the line in terms of x. Mathematica: |
| 34 | |
| 35 | (in) Resultant[ |
| 36 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - i*y - j, |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 37 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y] |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 38 | (out) a - j - |
| 39 | 3 a t + 3 b t + |
| 40 | 3 a t^2 - 6 b t^2 + 3 c t^2 - |
| 41 | a t^3 + 3 b t^3 - 3 c t^3 + d t^3 - |
| 42 | i ( e - |
| 43 | 3 e t + 3 f t + |
| 44 | 3 e t^2 - 6 f t^2 + 3 g t^2 - |
| 45 | e t^3 + 3 f t^3 - 3 g t^3 + h t^3 ) |
| 46 | |
| 47 | Solving this with Mathematica produces an expression with hundreds of terms; |
| 48 | instead, use Numeric Solutions recipe to solve the cubic. |
| 49 | |
| 50 | The near-horizontal case, in terms of: Ax^3 + Bx^2 + Cx + D == 0 |
| 51 | A = (-(-e + 3*f - 3*g + h) + i*(-a + 3*b - 3*c + d) ) |
| 52 | B = 3*(-( e - 2*f + g ) + i*( a - 2*b + c ) ) |
| 53 | C = 3*(-(-e + f ) + i*(-a + b ) ) |
| 54 | D = (-( e ) + i*( a ) + j ) |
| 55 | |
| 56 | The near-vertical case, in terms of: Ax^3 + Bx^2 + Cx + D == 0 |
| 57 | A = ( (-a + 3*b - 3*c + d) - i*(-e + 3*f - 3*g + h) ) |
| 58 | B = 3*( ( a - 2*b + c ) - i*( e - 2*f + g ) ) |
| 59 | C = 3*( (-a + b ) - i*(-e + f ) ) |
| 60 | D = ( ( a ) - i*( e ) - j ) |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 61 | |
| 62 | For horizontal lines: |
| 63 | (in) Resultant[ |
| 64 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j, |
| 65 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y] |
| 66 | (out) e - j - |
| 67 | 3 e t + 3 f t + |
| 68 | 3 e t^2 - 6 f t^2 + 3 g t^2 - |
| 69 | e t^3 + 3 f t^3 - 3 g t^3 + h t^3 |
| 70 | So the cubic coefficients are: |
| 71 | |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 72 | */ |
| 73 | |
| 74 | class LineCubicIntersections : public Intersections { |
| 75 | public: |
| 76 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 77 | LineCubicIntersections(const Cubic& c, const _Line& l, double r[3]) |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 78 | : cubic(c) |
| 79 | , line(l) |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 80 | , range(r) { |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 81 | } |
| 82 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 83 | int intersect() { |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 84 | double slope; |
| 85 | double axisIntercept; |
| 86 | moreHorizontal = implicitLine(line, slope, axisIntercept); |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 87 | double A, B, C, D; |
| 88 | coefficients(&cubic[0].x, A, B, C, D); |
| 89 | double E, F, G, H; |
| 90 | coefficients(&cubic[0].y, E, F, G, H); |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 91 | if (moreHorizontal) { |
| 92 | A = A * slope - E; |
| 93 | B = B * slope - F; |
| 94 | C = C * slope - G; |
| 95 | D = D * slope - H + axisIntercept; |
| 96 | } else { |
| 97 | A = A - E * slope; |
| 98 | B = B - F * slope; |
| 99 | C = C - G * slope; |
| 100 | D = D - H * slope - axisIntercept; |
| 101 | } |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 102 | return cubicRoots(A, B, C, D, range); |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 103 | } |
| 104 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 105 | int horizontalIntersect(double axisIntercept) { |
| 106 | double A, B, C, D; |
| 107 | coefficients(&cubic[0].y, A, B, C, D); |
| 108 | D -= axisIntercept; |
| 109 | return cubicRoots(A, B, C, D, range); |
| 110 | } |
| 111 | |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 112 | double findLineT(double t) { |
| 113 | const double* cPtr; |
| 114 | const double* lPtr; |
| 115 | if (moreHorizontal) { |
| 116 | cPtr = &cubic[0].x; |
| 117 | lPtr = &line[0].x; |
| 118 | } else { |
| 119 | cPtr = &cubic[0].y; |
| 120 | lPtr = &line[0].y; |
| 121 | } |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 122 | // FIXME: should fold the following in with TestUtilities.cpp xy_at_t() |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 123 | double s = 1 - t; |
| 124 | double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t |
| 125 | + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t; |
| 126 | return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]); |
| 127 | } |
| 128 | |
| 129 | private: |
| 130 | |
| 131 | const Cubic& cubic; |
| 132 | const _Line& line; |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 133 | double* range; |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 134 | bool moreHorizontal; |
| 135 | |
| 136 | }; |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 137 | |
| 138 | int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) { |
| 139 | LineCubicIntersections c(cubic, *((_Line*) 0), tRange); |
| 140 | return c.horizontalIntersect(y); |
| 141 | } |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 142 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame^] | 143 | int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) { |
| 144 | LineCubicIntersections c(cubic, line, cRange); |
| 145 | int roots; |
| 146 | if (approximately_equal(line[0].y, line[1].y)) { |
| 147 | roots = c.horizontalIntersect(line[0].y); |
| 148 | } else { |
| 149 | roots = c.intersect(); |
| 150 | } |
| 151 | for (int index = 0; index < roots; ++index) { |
| 152 | lRange[index] = c.findLineT(cRange[index]); |
| 153 | } |
| 154 | return roots; |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 155 | } |