1/*
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 */
7#include "CurveIntersection.h"
8#include "Intersections.h"
9#include "LineUtilities.h"
10#include "QuadraticUtilities.h"
11
12/*
13Find the interection of a line and quadratic by solving for valid t values.
14
15From http://stackoverflow.com/questions/1853637/how-to-find-the-mathematical-function-defining-a-bezier-curve
16
17"A Bezier curve is a parametric function. A quadratic Bezier curve (i.e. three
18control points) can be expressed as: F(t) = A(1 - t)^2 + B(1 - t)t + Ct^2 where
19A, B and C are points and t goes from zero to one.
20
21This will give you two equations:
22
23  x = a(1 - t)^2 + b(1 - t)t + ct^2
24  y = d(1 - t)^2 + e(1 - t)t + ft^2
25
26If you add for instance the line equation (y = kx + m) to that, you'll end up
27with three equations and three unknowns (x, y and t)."
28
29Similar to above, the quadratic is represented as
30  x = a(1-t)^2 + 2b(1-t)t + ct^2
31  y = d(1-t)^2 + 2e(1-t)t + ft^2
32and the line as
33  y = g*x + h
34
35Using Mathematica, solve for the values of t where the quadratic intersects the
36line:
37
38  (in)  t1 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - x,
39                       d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - g*x - h, x]
40  (out) -d + h + 2 d t - 2 e t - d t^2 + 2 e t^2 - f t^2 +
41         g  (a - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2)
42  (in)  Solve[t1 == 0, t]
43  (out) {
44    {t -> (-2 d + 2 e +   2 a g - 2 b g    -
45      Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
46          4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
47         (2 (-d + 2 e - f + a g - 2 b g    + c g))
48         },
49    {t -> (-2 d + 2 e +   2 a g - 2 b g    +
50      Sqrt[(2 d - 2 e -   2 a g + 2 b g)^2 -
51          4 (-d + 2 e - f + a g - 2 b g    + c g) (-d + a g + h)]) /
52         (2 (-d + 2 e - f + a g - 2 b g    + c g))
53         }
54        }
55
56Using the results above (when the line tends towards horizontal)
57       A =   (-(d - 2*e + f) + g*(a - 2*b + c)     )
58       B = 2*( (d -   e    ) - g*(a -   b    )     )
59       C =   (-(d          ) + g*(a          ) + h )
60
61If g goes to infinity, we can rewrite the line in terms of x.
62  x = g'*y + h'
63
64And solve accordingly in Mathematica:
65
66  (in)  t2 = Resultant[a*(1 - t)^2 + 2*b*(1 - t)*t + c*t^2 - g'*y - h',
67                       d*(1 - t)^2 + 2*e*(1 - t)*t  + f*t^2 - y, y]
68  (out)  a - h' - 2 a t + 2 b t + a t^2 - 2 b t^2 + c t^2 -
69         g'  (d - 2 d t + 2 e t + d t^2 - 2 e t^2 + f t^2)
70  (in)  Solve[t2 == 0, t]
71  (out) {
72    {t -> (2 a - 2 b -   2 d g' + 2 e g'    -
73    Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
74          4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')]) /
75         (2 (a - 2 b + c - d g' + 2 e g' - f g'))
76         },
77    {t -> (2 a - 2 b -   2 d g' + 2 e g'    +
78    Sqrt[(-2 a + 2 b +   2 d g' - 2 e g')^2 -
79          4 (a - 2 b + c - d g' + 2 e g' - f g') (a - d g' - h')])/
80         (2 (a - 2 b + c - d g' + 2 e g' - f g'))
81         }
82        }
83
84Thus, if the slope of the line tends towards vertical, we use:
85       A =   ( (a - 2*b + c) - g'*(d  - 2*e + f)      )
86       B = 2*(-(a -   b    ) + g'*(d  -   e    )      )
87       C =   ( (a          ) - g'*(d           ) - h' )
88 */
89
90
91class LineQuadraticIntersections {
92public:
93
94LineQuadraticIntersections(const Quadratic& q, const _Line& l, Intersections& i)
95    : quad(q)
96    , line(l)
97    , intersections(i) {
98}
99
100int intersectRay(double roots[2]) {
101/*
102    solve by rotating line+quad so line is horizontal, then finding the roots
103    set up matrix to rotate quad to x-axis
104    |cos(a) -sin(a)|
105    |sin(a)  cos(a)|
106    note that cos(a) = A(djacent) / Hypoteneuse
107              sin(a) = O(pposite) / Hypoteneuse
108    since we are computing Ts, we can ignore hypoteneuse, the scale factor:
109    |  A     -O    |
110    |  O      A    |
111    A = line[1].x - line[0].x (adjacent side of the right triangle)
112    O = line[1].y - line[0].y (opposite side of the right triangle)
113    for each of the three points (e.g. n = 0 to 2)
114    quad[n].y' = (quad[n].y - line[0].y) * A - (quad[n].x - line[0].x) * O
115*/
116    double adj = line[1].x - line[0].x;
117    double opp = line[1].y - line[0].y;
118    double r[3];
119    for (int n = 0; n < 3; ++n) {
120        r[n] = (quad[n].y - line[0].y) * adj - (quad[n].x - line[0].x) * opp;
121    }
122    double A = r[2];
123    double B = r[1];
124    double C = r[0];
125    A += C - 2 * B; // A = a - 2*b + c
126    B -= C; // B = -(b - c)
127    return quadraticRootsValidT(A, 2 * B, C, roots);
128}
129
130int intersect() {
131    addEndPoints();
132    double rootVals[2];
133    int roots = intersectRay(rootVals);
134    for (int index = 0; index < roots; ++index) {
135        double quadT = rootVals[index];
136        double lineT = findLineT(quadT);
137        if (pinTs(quadT, lineT)) {
138            _Point pt;
139            xy_at_t(line, lineT, pt.x, pt.y);
140            intersections.insert(quadT, lineT, pt);
141        }
142    }
143    return intersections.fUsed;
144}
145
146int horizontalIntersect(double axisIntercept, double roots[2]) {
147    double D = quad[2].y; // f
148    double E = quad[1].y; // e
149    double F = quad[0].y; // d
150    D += F - 2 * E; // D = d - 2*e + f
151    E -= F; // E = -(d - e)
152    F -= axisIntercept;
153    return quadraticRootsValidT(D, 2 * E, F, roots);
154}
155
156int horizontalIntersect(double axisIntercept, double left, double right, bool flipped) {
157    addHorizontalEndPoints(left, right, axisIntercept);
158    double rootVals[2];
159    int roots = horizontalIntersect(axisIntercept, rootVals);
160    for (int index = 0; index < roots; ++index) {
161        _Point pt;
162        double quadT = rootVals[index];
163        xy_at_t(quad, quadT, pt.x, pt.y);
164        double lineT = (pt.x - left) / (right - left);
165        if (pinTs(quadT, lineT)) {
166            intersections.insert(quadT, lineT, pt);
167        }
168    }
169    if (flipped) {
170        flip();
171    }
172    return intersections.fUsed;
173}
174
175int verticalIntersect(double axisIntercept, double roots[2]) {
176    double D = quad[2].x; // f
177    double E = quad[1].x; // e
178    double F = quad[0].x; // d
179    D += F - 2 * E; // D = d - 2*e + f
180    E -= F; // E = -(d - e)
181    F -= axisIntercept;
182    return quadraticRootsValidT(D, 2 * E, F, roots);
183}
184
185int verticalIntersect(double axisIntercept, double top, double bottom, bool flipped) {
186    addVerticalEndPoints(top, bottom, axisIntercept);
187    double rootVals[2];
188    int roots = verticalIntersect(axisIntercept, rootVals);
189    for (int index = 0; index < roots; ++index) {
190        _Point pt;
191        double quadT = rootVals[index];
192        xy_at_t(quad, quadT, pt.x, pt.y);
193        double lineT = (pt.y - top) / (bottom - top);
194        if (pinTs(quadT, lineT)) {
195            intersections.insert(quadT, lineT, pt);
196        }
197    }
198    if (flipped) {
199        flip();
200    }
201    return intersections.fUsed;
202}
203
204protected:
205
206// add endpoints first to get zero and one t values exactly
207void addEndPoints()
208{
209    for (int qIndex = 0; qIndex < 3; qIndex += 2) {
210        for (int lIndex = 0; lIndex < 2; lIndex++) {
211            if (quad[qIndex] == line[lIndex]) {
212                intersections.insert(qIndex >> 1, lIndex, line[lIndex]);
213            }
214        }
215    }
216}
217
218void addHorizontalEndPoints(double left, double right, double y)
219{
220    for (int qIndex = 0; qIndex < 3; qIndex += 2) {
221        if (quad[qIndex].y != y) {
222            continue;
223        }
224        if (quad[qIndex].x == left) {
225            intersections.insert(qIndex >> 1, 0, quad[qIndex]);
226        }
227        if (quad[qIndex].x == right) {
228            intersections.insert(qIndex >> 1, 1, quad[qIndex]);
229        }
230    }
231}
232
233void addVerticalEndPoints(double top, double bottom, double x)
234{
235    for (int qIndex = 0; qIndex < 3; qIndex += 2) {
236        if (quad[qIndex].x != x) {
237            continue;
238        }
239        if (quad[qIndex].y == top) {
240            intersections.insert(qIndex >> 1, 0, quad[qIndex]);
241        }
242        if (quad[qIndex].y == bottom) {
243            intersections.insert(qIndex >> 1, 1, quad[qIndex]);
244        }
245    }
246}
247
248double findLineT(double t) {
249    double x, y;
250    xy_at_t(quad, t, x, y);
251    double dx = line[1].x - line[0].x;
252    double dy = line[1].y - line[0].y;
253    if (fabs(dx) > fabs(dy)) {
254        return (x - line[0].x) / dx;
255    }
256    return (y - line[0].y) / dy;
257}
258
259void flip() {
260    // OPTIMIZATION: instead of swapping, pass original line, use [1].y - [0].y
261    int roots = intersections.fUsed;
262    for (int index = 0; index < roots; ++index) {
263        intersections.fT[1][index] = 1 - intersections.fT[1][index];
264    }
265}
266
267static bool pinTs(double& quadT, double& lineT) {
268    if (!approximately_one_or_less(lineT)) {
269        return false;
270    }
271    if (!approximately_zero_or_more(lineT)) {
272        return false;
273    }
274    if (precisely_less_than_zero(quadT)) {
275        quadT = 0;
276    } else if (precisely_greater_than_one(quadT)) {
277        quadT = 1;
278    }
279    if (precisely_less_than_zero(lineT)) {
280        lineT = 0;
281    } else if (precisely_greater_than_one(lineT)) {
282        lineT = 1;
283    }
284    return true;
285}
286
287private:
288
289const Quadratic& quad;
290const _Line& line;
291Intersections& intersections;
292};
293
294// utility for pairs of coincident quads
295static double horizontalIntersect(const Quadratic& quad, const _Point& pt) {
296    LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
297    double rootVals[2];
298    int roots = q.horizontalIntersect(pt.y, rootVals);
299    for (int index = 0; index < roots; ++index) {
300        double x;
301        double t = rootVals[index];
302        xy_at_t(quad, t, x, *(double*) 0);
303        if (AlmostEqualUlps(x, pt.x)) {
304            return t;
305        }
306    }
307    return -1;
308}
309
310static double verticalIntersect(const Quadratic& quad, const _Point& pt) {
311    LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
312    double rootVals[2];
313    int roots = q.verticalIntersect(pt.x, rootVals);
314    for (int index = 0; index < roots; ++index) {
315        double y;
316        double t = rootVals[index];
317        xy_at_t(quad, t, *(double*) 0, y);
318        if (AlmostEqualUlps(y, pt.y)) {
319            return t;
320        }
321    }
322    return -1;
323}
324
325double axialIntersect(const Quadratic& q1, const _Point& p, bool vertical) {
326    if (vertical) {
327        return verticalIntersect(q1, p);
328    }
329    return horizontalIntersect(q1, p);
330}
331
332int horizontalIntersect(const Quadratic& quad, double left, double right,
333        double y, double tRange[2]) {
334    LineQuadraticIntersections q(quad, *((_Line*) 0), *((Intersections*) 0));
335    double rootVals[2];
336    int result = q.horizontalIntersect(y, rootVals);
337    int tCount = 0;
338    for (int index = 0; index < result; ++index) {
339        double x, y;
340        xy_at_t(quad, rootVals[index], x, y);
341        if (x < left || x > right) {
342            continue;
343        }
344        tRange[tCount++] = rootVals[index];
345    }
346    return tCount;
347}
348
349int horizontalIntersect(const Quadratic& quad, double left, double right, double y,
350        bool flipped, Intersections& intersections) {
351    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
352    return q.horizontalIntersect(y, left, right, flipped);
353}
354
355int verticalIntersect(const Quadratic& quad, double top, double bottom, double x,
356        bool flipped, Intersections& intersections) {
357    LineQuadraticIntersections q(quad, *((_Line*) 0), intersections);
358    return q.verticalIntersect(x, top, bottom, flipped);
359}
360
361int intersect(const Quadratic& quad, const _Line& line, Intersections& i) {
362    LineQuadraticIntersections q(quad, line, i);
363    return q.intersect();
364}
365
366int intersectRay(const Quadratic& quad, const _Line& line, Intersections& i) {
367    LineQuadraticIntersections q(quad, line, i);
368    return q.intersectRay(i.fT[0]);
369}
370