blob: a210fb3603ede821701a0b9b304bac5f43477169 [file] [log] [blame]
caryclark@google.comc6825902012-02-03 22:07:47 +00001#include "CurveIntersection.h"
caryclark@google.com27accef2012-01-25 18:57:23 +00002#include "CubicUtilities.h"
3#include "Intersections.h"
4#include "LineUtilities.h"
5
6/*
7Find the interection of a line and cubic by solving for valid t values.
8
9Analogous to line-quadratic intersection, solve line-cubic intersection by
10representing 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
13and the line as:
14 y = i*x + j (if the line is more horizontal)
15or:
16 x = i*y + j (if the line is more vertical)
17
18Then using Mathematica, solve for the values of t where the cubic intersects the
19line:
20
21 (in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000022 a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - x,
caryclark@google.comc6825902012-02-03 22:07:47 +000023 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.com27accef2012-01-25 18:57:23 +000024 (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.comd6176b02012-08-23 18:14:13 +000027 e t^3 - 3 f t^3 + 3 g t^3 - h t^3 +
caryclark@google.com27accef2012-01-25 18:57:23 +000028 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
33if i goes to infinity, we can rewrite the line in terms of x. Mathematica:
34
35 (in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000036 a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - i*y - j,
caryclark@google.comc6825902012-02-03 22:07:47 +000037 e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y]
rmistry@google.comd6176b02012-08-23 18:14:13 +000038 (out) a - j -
39 3 a t + 3 b t +
caryclark@google.com27accef2012-01-25 18:57:23 +000040 3 a t^2 - 6 b t^2 + 3 c t^2 -
rmistry@google.comd6176b02012-08-23 18:14:13 +000041 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.com27accef2012-01-25 18:57:23 +000044 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
47Solving this with Mathematica produces an expression with hundreds of terms;
48instead, use Numeric Solutions recipe to solve the cubic.
49
50The 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
56The 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.comd6176b02012-08-23 18:14:13 +000061
caryclark@google.comc6825902012-02-03 22:07:47 +000062For horizontal lines:
63(in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000064 a*(1 - t)^3 + 3*b*(1 - t)^2*t + 3*c*(1 - t)*t^2 + d*t^3 - j,
caryclark@google.comc6825902012-02-03 22:07:47 +000065 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.comd6176b02012-08-23 18:14:13 +000067 3 e t + 3 f t +
caryclark@google.comc6825902012-02-03 22:07:47 +000068 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
70So the cubic coefficients are:
71
caryclark@google.com27accef2012-01-25 18:57:23 +000072 */
73
74class LineCubicIntersections : public Intersections {
75public:
76
caryclark@google.comc6825902012-02-03 22:07:47 +000077LineCubicIntersections(const Cubic& c, const _Line& l, double r[3])
caryclark@google.com27accef2012-01-25 18:57:23 +000078 : cubic(c)
79 , line(l)
caryclark@google.comc6825902012-02-03 22:07:47 +000080 , range(r) {
caryclark@google.com27accef2012-01-25 18:57:23 +000081}
82
caryclark@google.comc6825902012-02-03 22:07:47 +000083int intersect() {
caryclark@google.com27accef2012-01-25 18:57:23 +000084 double slope;
85 double axisIntercept;
86 moreHorizontal = implicitLine(line, slope, axisIntercept);
caryclark@google.comc6825902012-02-03 22:07:47 +000087 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.com27accef2012-01-25 18:57:23 +000091 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.comc6825902012-02-03 22:07:47 +0000102 return cubicRoots(A, B, C, D, range);
caryclark@google.com27accef2012-01-25 18:57:23 +0000103}
104
caryclark@google.comc6825902012-02-03 22:07:47 +0000105int 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.comfa0588f2012-04-26 21:01:06 +0000112int 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.com27accef2012-01-25 18:57:23 +0000119double 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.comc6825902012-02-03 22:07:47 +0000129 // FIXME: should fold the following in with TestUtilities.cpp xy_at_t()
caryclark@google.com27accef2012-01-25 18:57:23 +0000130 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
136private:
137
138const Cubic& cubic;
139const _Line& line;
caryclark@google.comc6825902012-02-03 22:07:47 +0000140double* range;
caryclark@google.com27accef2012-01-25 18:57:23 +0000141bool moreHorizontal;
142
143};
caryclark@google.comc6825902012-02-03 22:07:47 +0000144
145int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) {
146 LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
147 return c.horizontalIntersect(y);
148}
caryclark@google.com198e0542012-03-30 18:47:02 +0000149
150int 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.comfa0588f2012-04-26 21:01:06 +0000168int 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.com24bec792012-08-20 12:43:57 +0000181 intersections.fT[1][index] = (x - left) / (right - left);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000182 ++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
193int 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.com24bec792012-08-20 12:43:57 +0000202 intersections.fT[1][index] = intersections.fT[0][result];
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000203 }
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.comc6825902012-02-03 22:07:47 +0000218int 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.com27accef2012-01-25 18:57:23 +0000230}