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[ |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 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 + |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 27 | e t^3 - 3 f t^3 + 3 g t^3 - h t^3 + |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 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[ |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 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] |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 38 | (out) a - j - |
| 39 | 3 a t + 3 b t + |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 40 | 3 a t^2 - 6 b t^2 + 3 c t^2 - |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 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 + |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 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 ) |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 61 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 62 | For horizontal lines: |
| 63 | (in) Resultant[ |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 64 | a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j, |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 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 - |
rmistry@google.com | d6176b0 | 2012-08-23 18:14:13 +0000 | [diff] [blame^] | 67 | 3 e t + 3 f t + |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 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 | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame] | 112 | int verticalIntersect(double axisIntercept) { |
| 113 | double A, B, C, D; |
| 114 | coefficients(&cubic[0].x, A, B, C, D); |
| 115 | D -= axisIntercept; |
| 116 | return cubicRoots(A, B, C, D, range); |
| 117 | } |
| 118 | |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 119 | double findLineT(double t) { |
| 120 | const double* cPtr; |
| 121 | const double* lPtr; |
| 122 | if (moreHorizontal) { |
| 123 | cPtr = &cubic[0].x; |
| 124 | lPtr = &line[0].x; |
| 125 | } else { |
| 126 | cPtr = &cubic[0].y; |
| 127 | lPtr = &line[0].y; |
| 128 | } |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 129 | // 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] | 130 | double s = 1 - t; |
| 131 | double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t |
| 132 | + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t; |
| 133 | return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]); |
| 134 | } |
| 135 | |
| 136 | private: |
| 137 | |
| 138 | const Cubic& cubic; |
| 139 | const _Line& line; |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 140 | double* range; |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 141 | bool moreHorizontal; |
| 142 | |
| 143 | }; |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 144 | |
| 145 | int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) { |
| 146 | LineCubicIntersections c(cubic, *((_Line*) 0), tRange); |
| 147 | return c.horizontalIntersect(y); |
| 148 | } |
caryclark@google.com | 198e054 | 2012-03-30 18:47:02 +0000 | [diff] [blame] | 149 | |
| 150 | int horizontalIntersect(const Cubic& cubic, double left, double right, double y, |
| 151 | double tRange[3]) { |
| 152 | LineCubicIntersections c(cubic, *((_Line*) 0), tRange); |
| 153 | int result = c.horizontalIntersect(y); |
| 154 | for (int index = 0; index < result; ) { |
| 155 | double x, y; |
| 156 | xy_at_t(cubic, tRange[index], x, y); |
| 157 | if (x < left || x > right) { |
| 158 | if (--result > index) { |
| 159 | tRange[index] = tRange[result]; |
| 160 | } |
| 161 | continue; |
| 162 | } |
| 163 | ++index; |
| 164 | } |
| 165 | return result; |
| 166 | } |
| 167 | |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame] | 168 | int horizontalIntersect(const Cubic& cubic, double left, double right, double y, |
| 169 | bool flipped, Intersections& intersections) { |
| 170 | LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]); |
| 171 | int result = c.horizontalIntersect(y); |
| 172 | for (int index = 0; index < result; ) { |
| 173 | double x, y; |
| 174 | xy_at_t(cubic, intersections.fT[0][index], x, y); |
| 175 | if (x < left || x > right) { |
| 176 | if (--result > index) { |
| 177 | intersections.fT[0][index] = intersections.fT[0][result]; |
| 178 | } |
| 179 | continue; |
| 180 | } |
caryclark@google.com | 24bec79 | 2012-08-20 12:43:57 +0000 | [diff] [blame] | 181 | intersections.fT[1][index] = (x - left) / (right - left); |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame] | 182 | ++index; |
| 183 | } |
| 184 | if (flipped) { |
| 185 | // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x |
| 186 | for (int index = 0; index < result; ++index) { |
| 187 | intersections.fT[1][index] = 1 - intersections.fT[1][index]; |
| 188 | } |
| 189 | } |
| 190 | return result; |
| 191 | } |
| 192 | |
| 193 | int verticalIntersect(const Cubic& cubic, double top, double bottom, double x, |
| 194 | bool flipped, Intersections& intersections) { |
| 195 | LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]); |
| 196 | int result = c.verticalIntersect(x); |
| 197 | for (int index = 0; index < result; ) { |
| 198 | double x, y; |
| 199 | xy_at_t(cubic, intersections.fT[0][index], x, y); |
| 200 | if (y < top || y > bottom) { |
| 201 | if (--result > index) { |
caryclark@google.com | 24bec79 | 2012-08-20 12:43:57 +0000 | [diff] [blame] | 202 | intersections.fT[1][index] = intersections.fT[0][result]; |
caryclark@google.com | fa0588f | 2012-04-26 21:01:06 +0000 | [diff] [blame] | 203 | } |
| 204 | continue; |
| 205 | } |
| 206 | intersections.fT[0][index] = (y - top) / (bottom - top); |
| 207 | ++index; |
| 208 | } |
| 209 | if (flipped) { |
| 210 | // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x |
| 211 | for (int index = 0; index < result; ++index) { |
| 212 | intersections.fT[1][index] = 1 - intersections.fT[1][index]; |
| 213 | } |
| 214 | } |
| 215 | return result; |
| 216 | } |
| 217 | |
caryclark@google.com | c682590 | 2012-02-03 22:07:47 +0000 | [diff] [blame] | 218 | int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) { |
| 219 | LineCubicIntersections c(cubic, line, cRange); |
| 220 | int roots; |
| 221 | if (approximately_equal(line[0].y, line[1].y)) { |
| 222 | roots = c.horizontalIntersect(line[0].y); |
| 223 | } else { |
| 224 | roots = c.intersect(); |
| 225 | } |
| 226 | for (int index = 0; index < roots; ++index) { |
| 227 | lRange[index] = c.findLineT(cRange[index]); |
| 228 | } |
| 229 | return roots; |
caryclark@google.com | 27accef | 2012-01-25 18:57:23 +0000 | [diff] [blame] | 230 | } |