blob: 91a0b7df922b1bb43c35a0b0f69c1bf1e26c9861 [file] [log] [blame]
caryclark@google.com9e49fb62012-08-27 14:11:33 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
caryclark@google.comc6825902012-02-03 22:07:47 +00007#include "CurveIntersection.h"
caryclark@google.com27accef2012-01-25 18:57:23 +00008#include "CubicUtilities.h"
9#include "Intersections.h"
10#include "LineUtilities.h"
11
12/*
13Find the interection of a line and cubic by solving for valid t values.
14
15Analogous to line-quadratic intersection, solve line-cubic intersection by
16representing the cubic as:
17 x = a(1-t)^3 + 2b(1-t)^2t + c(1-t)t^2 + dt^3
18 y = e(1-t)^3 + 2f(1-t)^2t + g(1-t)t^2 + ht^3
19and the line as:
20 y = i*x + j (if the line is more horizontal)
21or:
22 x = i*y + j (if the line is more vertical)
23
24Then using Mathematica, solve for the values of t where the cubic intersects the
25line:
26
27 (in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000028 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 +000029 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 +000030 (out) -e + j +
31 3 e t - 3 f t -
32 3 e t^2 + 6 f t^2 - 3 g t^2 +
rmistry@google.comd6176b02012-08-23 18:14:13 +000033 e t^3 - 3 f t^3 + 3 g t^3 - h t^3 +
caryclark@google.com27accef2012-01-25 18:57:23 +000034 i ( a -
35 3 a t + 3 b t +
36 3 a t^2 - 6 b t^2 + 3 c t^2 -
37 a t^3 + 3 b t^3 - 3 c t^3 + d t^3 )
38
39if i goes to infinity, we can rewrite the line in terms of x. Mathematica:
40
41 (in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000042 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 +000043 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 +000044 (out) a - j -
45 3 a t + 3 b t +
caryclark@google.com27accef2012-01-25 18:57:23 +000046 3 a t^2 - 6 b t^2 + 3 c t^2 -
rmistry@google.comd6176b02012-08-23 18:14:13 +000047 a t^3 + 3 b t^3 - 3 c t^3 + d t^3 -
48 i ( e -
49 3 e t + 3 f t +
caryclark@google.com27accef2012-01-25 18:57:23 +000050 3 e t^2 - 6 f t^2 + 3 g t^2 -
51 e t^3 + 3 f t^3 - 3 g t^3 + h t^3 )
52
53Solving this with Mathematica produces an expression with hundreds of terms;
54instead, use Numeric Solutions recipe to solve the cubic.
55
56The near-horizontal case, in terms of: Ax^3 + Bx^2 + Cx + D == 0
57 A = (-(-e + 3*f - 3*g + h) + i*(-a + 3*b - 3*c + d) )
58 B = 3*(-( e - 2*f + g ) + i*( a - 2*b + c ) )
59 C = 3*(-(-e + f ) + i*(-a + b ) )
60 D = (-( e ) + i*( a ) + j )
61
62The near-vertical case, in terms of: Ax^3 + Bx^2 + Cx + D == 0
63 A = ( (-a + 3*b - 3*c + d) - i*(-e + 3*f - 3*g + h) )
64 B = 3*( ( a - 2*b + c ) - i*( e - 2*f + g ) )
65 C = 3*( (-a + b ) - i*(-e + f ) )
66 D = ( ( a ) - i*( e ) - j )
rmistry@google.comd6176b02012-08-23 18:14:13 +000067
caryclark@google.comc6825902012-02-03 22:07:47 +000068For horizontal lines:
69(in) Resultant[
rmistry@google.comd6176b02012-08-23 18:14:13 +000070 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 +000071 e*(1 - t)^3 + 3*f*(1 - t)^2*t + 3*g*(1 - t)*t^2 + h*t^3 - y, y]
72(out) e - j -
rmistry@google.comd6176b02012-08-23 18:14:13 +000073 3 e t + 3 f t +
caryclark@google.comc6825902012-02-03 22:07:47 +000074 3 e t^2 - 6 f t^2 + 3 g t^2 -
75 e t^3 + 3 f t^3 - 3 g t^3 + h t^3
76So the cubic coefficients are:
77
caryclark@google.com27accef2012-01-25 18:57:23 +000078 */
79
80class LineCubicIntersections : public Intersections {
81public:
82
caryclark@google.comc6825902012-02-03 22:07:47 +000083LineCubicIntersections(const Cubic& c, const _Line& l, double r[3])
caryclark@google.com27accef2012-01-25 18:57:23 +000084 : cubic(c)
85 , line(l)
caryclark@google.comc6825902012-02-03 22:07:47 +000086 , range(r) {
caryclark@google.com27accef2012-01-25 18:57:23 +000087}
88
caryclark@google.comc6825902012-02-03 22:07:47 +000089int intersect() {
caryclark@google.com27accef2012-01-25 18:57:23 +000090 double slope;
91 double axisIntercept;
92 moreHorizontal = implicitLine(line, slope, axisIntercept);
caryclark@google.comc6825902012-02-03 22:07:47 +000093 double A, B, C, D;
94 coefficients(&cubic[0].x, A, B, C, D);
95 double E, F, G, H;
96 coefficients(&cubic[0].y, E, F, G, H);
caryclark@google.com27accef2012-01-25 18:57:23 +000097 if (moreHorizontal) {
98 A = A * slope - E;
99 B = B * slope - F;
100 C = C * slope - G;
101 D = D * slope - H + axisIntercept;
102 } else {
103 A = A - E * slope;
104 B = B - F * slope;
105 C = C - G * slope;
106 D = D - H * slope - axisIntercept;
107 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000108 return cubicRoots(A, B, C, D, range);
caryclark@google.com27accef2012-01-25 18:57:23 +0000109}
110
caryclark@google.comc6825902012-02-03 22:07:47 +0000111int horizontalIntersect(double axisIntercept) {
112 double A, B, C, D;
113 coefficients(&cubic[0].y, A, B, C, D);
114 D -= axisIntercept;
115 return cubicRoots(A, B, C, D, range);
116}
117
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000118int verticalIntersect(double axisIntercept) {
119 double A, B, C, D;
120 coefficients(&cubic[0].x, A, B, C, D);
121 D -= axisIntercept;
122 return cubicRoots(A, B, C, D, range);
123}
124
caryclark@google.com27accef2012-01-25 18:57:23 +0000125double findLineT(double t) {
126 const double* cPtr;
127 const double* lPtr;
128 if (moreHorizontal) {
129 cPtr = &cubic[0].x;
130 lPtr = &line[0].x;
131 } else {
132 cPtr = &cubic[0].y;
133 lPtr = &line[0].y;
134 }
caryclark@google.comc6825902012-02-03 22:07:47 +0000135 // FIXME: should fold the following in with TestUtilities.cpp xy_at_t()
caryclark@google.com27accef2012-01-25 18:57:23 +0000136 double s = 1 - t;
137 double cubicVal = cPtr[0] * s * s * s + 3 * cPtr[2] * s * s * t
138 + 3 * cPtr[4] * s * t * t + cPtr[6] * t * t * t;
139 return (cubicVal - lPtr[0]) / (lPtr[2] - lPtr[0]);
140}
141
142private:
143
144const Cubic& cubic;
145const _Line& line;
caryclark@google.comc6825902012-02-03 22:07:47 +0000146double* range;
caryclark@google.com27accef2012-01-25 18:57:23 +0000147bool moreHorizontal;
148
149};
caryclark@google.comc6825902012-02-03 22:07:47 +0000150
151int horizontalIntersect(const Cubic& cubic, double y, double tRange[3]) {
152 LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
153 return c.horizontalIntersect(y);
154}
caryclark@google.com198e0542012-03-30 18:47:02 +0000155
156int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
157 double tRange[3]) {
158 LineCubicIntersections c(cubic, *((_Line*) 0), tRange);
159 int result = c.horizontalIntersect(y);
160 for (int index = 0; index < result; ) {
161 double x, y;
162 xy_at_t(cubic, tRange[index], x, y);
163 if (x < left || x > right) {
164 if (--result > index) {
165 tRange[index] = tRange[result];
166 }
167 continue;
168 }
169 ++index;
170 }
171 return result;
172}
173
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000174int horizontalIntersect(const Cubic& cubic, double left, double right, double y,
175 bool flipped, Intersections& intersections) {
176 LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
177 int result = c.horizontalIntersect(y);
178 for (int index = 0; index < result; ) {
179 double x, y;
180 xy_at_t(cubic, intersections.fT[0][index], x, y);
181 if (x < left || x > right) {
182 if (--result > index) {
183 intersections.fT[0][index] = intersections.fT[0][result];
184 }
185 continue;
186 }
caryclark@google.com24bec792012-08-20 12:43:57 +0000187 intersections.fT[1][index] = (x - left) / (right - left);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000188 ++index;
189 }
190 if (flipped) {
191 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
192 for (int index = 0; index < result; ++index) {
193 intersections.fT[1][index] = 1 - intersections.fT[1][index];
194 }
195 }
196 return result;
197}
198
199int verticalIntersect(const Cubic& cubic, double top, double bottom, double x,
200 bool flipped, Intersections& intersections) {
201 LineCubicIntersections c(cubic, *((_Line*) 0), intersections.fT[0]);
202 int result = c.verticalIntersect(x);
203 for (int index = 0; index < result; ) {
204 double x, y;
205 xy_at_t(cubic, intersections.fT[0][index], x, y);
206 if (y < top || y > bottom) {
207 if (--result > index) {
caryclark@google.com24bec792012-08-20 12:43:57 +0000208 intersections.fT[1][index] = intersections.fT[0][result];
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000209 }
210 continue;
211 }
212 intersections.fT[0][index] = (y - top) / (bottom - top);
213 ++index;
214 }
215 if (flipped) {
216 // OPTIMIZATION: instead of swapping, pass original line, use [1].x - [0].x
217 for (int index = 0; index < result; ++index) {
218 intersections.fT[1][index] = 1 - intersections.fT[1][index];
219 }
220 }
221 return result;
222}
223
caryclark@google.comc6825902012-02-03 22:07:47 +0000224int intersect(const Cubic& cubic, const _Line& line, double cRange[3], double lRange[3]) {
225 LineCubicIntersections c(cubic, line, cRange);
226 int roots;
caryclark@google.com6d0032a2013-01-04 19:41:13 +0000227 if (AlmostEqualUlps(line[0].y, line[1].y)) {
caryclark@google.comc6825902012-02-03 22:07:47 +0000228 roots = c.horizontalIntersect(line[0].y);
229 } else {
230 roots = c.intersect();
231 }
232 for (int index = 0; index < roots; ++index) {
233 lRange[index] = c.findLineT(cRange[index]);
234 }
235 return roots;
caryclark@google.com27accef2012-01-25 18:57:23 +0000236}