caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame^] | 1 | #include "CubicIntersection.h" |
| 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, |
| 23 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - i*x - j, x] |
| 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, |
| 37 | e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y] |
| 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 ) |
| 61 | */ |
| 62 | |
| 63 | class LineCubicIntersections : public Intersections { |
| 64 | public: |
| 65 | |
| 66 | LineCubicIntersections(const Cubic& c, const _Line& l, Intersections& i) |
| 67 | : cubic(c) |
| 68 | , line(l) |
| 69 | , intersections(i) { |
| 70 | } |
| 71 | |
| 72 | bool intersect() { |
| 73 | double slope; |
| 74 | double axisIntercept; |
| 75 | moreHorizontal = implicitLine(line, slope, axisIntercept); |
| 76 | double A = cubic[3].x; // d |
| 77 | double B = cubic[2].x * 3; // 3*c |
| 78 | double C = cubic[1].x * 3; // 3*b |
| 79 | double D = cubic[0].x; // a |
| 80 | A -= D - C + B; // A = -a + 3*b - 3*c + d |
| 81 | B += 3 * D - 2 * C; // B = 3*a - 6*b + 3*c |
| 82 | C -= 3 * D; // C = -3*a + 3*b |
| 83 | double E = cubic[3].y; // h |
| 84 | double F = cubic[2].y * 3; // 3*g |
| 85 | double G = cubic[1].y * 3; // 3*f |
| 86 | double H = cubic[0].y; // e |
| 87 | E -= H - G + F; // E = -e + 3*f - 3*g + h |
| 88 | F += 3 * H - 2 * G; // F = 3*e - 6*f + 3*g |
| 89 | G -= 3 * H; // G = -3*e + 3*f |
| 90 | if (moreHorizontal) { |
| 91 | A = A * slope - E; |
| 92 | B = B * slope - F; |
| 93 | C = C * slope - G; |
| 94 | D = D * slope - H + axisIntercept; |
| 95 | } else { |
| 96 | A = A - E * slope; |
| 97 | B = B - F * slope; |
| 98 | C = C - G * slope; |
| 99 | D = D - H * slope - axisIntercept; |
| 100 | } |
| 101 | double t[3]; |
| 102 | int roots = cubicRoots(A, B, C, D, t); |
| 103 | for (int x = 0; x < roots; ++x) { |
| 104 | intersections.add(t[x], findLineT(t[x])); |
| 105 | } |
| 106 | return roots > 0; |
| 107 | } |
| 108 | |
| 109 | protected: |
| 110 | |
| 111 | double findLineT(double t) { |
| 112 | const double* cPtr; |
| 113 | const double* lPtr; |
| 114 | if (moreHorizontal) { |
| 115 | cPtr = &cubic[0].x; |
| 116 | lPtr = &line[0].x; |
| 117 | } else { |
| 118 | cPtr = &cubic[0].y; |
| 119 | lPtr = &line[0].y; |
| 120 | } |
| 121 | double s = 1 - t; |
| 122 | double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t |
| 123 | + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t; |
| 124 | return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]); |
| 125 | } |
| 126 | |
| 127 | private: |
| 128 | |
| 129 | const Cubic& cubic; |
| 130 | const _Line& line; |
| 131 | Intersections& intersections; |
| 132 | bool moreHorizontal; |
| 133 | |
| 134 | }; |
| 135 | |
| 136 | bool intersectStart(const Cubic& cubic, const _Line& line, Intersections& i) { |
| 137 | LineCubicIntersections c(cubic, line, i); |
| 138 | return c.intersect(); |
| 139 | } |