blob: 6d77efc8a493e9a0c4b6fb9efe0de4b9db327781 [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.com639df892012-01-10 21:46:10 +00008#include "Intersections.h"
9#include "IntersectionUtilities.h"
10#include "LineIntersection.h"
caryclark@google.coma7e483d2012-08-28 20:44:43 +000011#include "LineUtilities.h"
12#include "QuadraticLineSegments.h"
13#include "QuadraticUtilities.h"
14#include <algorithm> // for swap
caryclark@google.com639df892012-01-10 21:46:10 +000015
16class QuadraticIntersections : public Intersections {
17public:
18
rmistry@google.comd6176b02012-08-23 18:14:13 +000019QuadraticIntersections(const Quadratic& q1, const Quadratic& q2, Intersections& i)
caryclark@google.com639df892012-01-10 21:46:10 +000020 : quad1(q1)
21 , quad2(q2)
22 , intersections(i)
rmistry@google.comd6176b02012-08-23 18:14:13 +000023 , depth(0)
caryclark@google.com639df892012-01-10 21:46:10 +000024 , splits(0) {
25}
26
27bool intersect() {
28 double minT1, minT2, maxT1, maxT2;
29 if (!bezier_clip(quad2, quad1, minT1, maxT1)) {
30 return false;
31 }
32 if (!bezier_clip(quad1, quad2, minT2, maxT2)) {
33 return false;
34 }
caryclark@google.coma7e483d2012-08-28 20:44:43 +000035 quad1Divisions = 1 / subDivisions(quad1);
36 quad2Divisions = 1 / subDivisions(quad2);
caryclark@google.com639df892012-01-10 21:46:10 +000037 int split;
38 if (maxT1 - minT1 < maxT2 - minT2) {
39 intersections.swap();
40 minT2 = 0;
41 maxT2 = 1;
42 split = maxT1 - minT1 > tClipLimit;
43 } else {
44 minT1 = 0;
45 maxT1 = 1;
46 split = (maxT2 - minT2 > tClipLimit) << 1;
47 }
48 return chop(minT1, maxT1, minT2, maxT2, split);
49}
50
51protected:
rmistry@google.comd6176b02012-08-23 18:14:13 +000052
caryclark@google.com639df892012-01-10 21:46:10 +000053bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
caryclark@google.coma7e483d2012-08-28 20:44:43 +000054 bool t1IsLine = maxT1 - minT1 <= quad1Divisions;
55 bool t2IsLine = maxT2 - minT2 <= quad2Divisions;
56 if (t1IsLine | t2IsLine) {
57 return intersectAsLine(minT1, maxT1, minT2, maxT2, t1IsLine, t2IsLine);
58 }
caryclark@google.com639df892012-01-10 21:46:10 +000059 Quadratic smaller, larger;
rmistry@google.comd6176b02012-08-23 18:14:13 +000060 // FIXME: carry last subdivide and reduceOrder result with quad
caryclark@google.com639df892012-01-10 21:46:10 +000061 sub_divide(quad1, minT1, maxT1, intersections.swapped() ? larger : smaller);
62 sub_divide(quad2, minT2, maxT2, intersections.swapped() ? smaller : larger);
caryclark@google.com639df892012-01-10 21:46:10 +000063 double minT, maxT;
64 if (!bezier_clip(smaller, larger, minT, maxT)) {
caryclark@google.coma7e483d2012-08-28 20:44:43 +000065 if (approximately_equal(minT, maxT)) {
66 double smallT, largeT;
67 _Point q2pt, q1pt;
caryclark@google.com639df892012-01-10 21:46:10 +000068 if (intersections.swapped()) {
caryclark@google.coma7e483d2012-08-28 20:44:43 +000069 largeT = interp(minT2, maxT2, minT);
70 xy_at_t(quad2, largeT, q2pt.x, q2pt.y);
71 xy_at_t(quad1, minT1, q1pt.x, q1pt.y);
72 if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
73 smallT = minT1;
74 } else {
75 xy_at_t(quad1, maxT1, q1pt.x, q1pt.y); // FIXME: debug code
76 assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
77 smallT = maxT1;
78 }
caryclark@google.com639df892012-01-10 21:46:10 +000079 } else {
caryclark@google.coma7e483d2012-08-28 20:44:43 +000080 smallT = interp(minT1, maxT1, minT);
81 xy_at_t(quad1, smallT, q1pt.x, q1pt.y);
82 xy_at_t(quad2, minT2, q2pt.x, q2pt.y);
83 if (approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y)) {
84 largeT = minT2;
85 } else {
86 xy_at_t(quad2, maxT2, q2pt.x, q2pt.y); // FIXME: debug code
87 assert(approximately_equal(q2pt.x, q1pt.x) && approximately_equal(q2pt.y, q1pt.y));
88 largeT = maxT2;
89 }
caryclark@google.com639df892012-01-10 21:46:10 +000090 }
caryclark@google.coma7e483d2012-08-28 20:44:43 +000091 intersections.add(smallT, largeT);
caryclark@google.com639df892012-01-10 21:46:10 +000092 return true;
93 }
94 return false;
95 }
caryclark@google.com639df892012-01-10 21:46:10 +000096 int split;
97 if (intersections.swapped()) {
98 double newMinT1 = interp(minT1, maxT1, minT);
99 double newMaxT1 = interp(minT1, maxT1, maxT);
100 split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
caryclark@google.com198e0542012-03-30 18:47:02 +0000101#define VERBOSE 0
102#if VERBOSE
caryclark@google.com639df892012-01-10 21:46:10 +0000103 printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n", __FUNCTION__, depth,
104 splits, newMinT1, newMaxT1, minT1, maxT1, split);
caryclark@google.com198e0542012-03-30 18:47:02 +0000105#endif
caryclark@google.com639df892012-01-10 21:46:10 +0000106 minT1 = newMinT1;
107 maxT1 = newMaxT1;
108 } else {
109 double newMinT2 = interp(minT2, maxT2, minT);
110 double newMaxT2 = interp(minT2, maxT2, maxT);
111 split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
caryclark@google.com198e0542012-03-30 18:47:02 +0000112#if VERBOSE
caryclark@google.com639df892012-01-10 21:46:10 +0000113 printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n", __FUNCTION__, depth,
114 splits, newMinT2, newMaxT2, minT2, maxT2, split);
caryclark@google.com198e0542012-03-30 18:47:02 +0000115#endif
caryclark@google.com639df892012-01-10 21:46:10 +0000116 minT2 = newMinT2;
117 maxT2 = newMaxT2;
118 }
119 return chop(minT1, maxT1, minT2, maxT2, split);
120}
121
caryclark@google.coma7e483d2012-08-28 20:44:43 +0000122bool intersectAsLine(double minT1, double maxT1, double minT2, double maxT2,
123 bool treat1AsLine, bool treat2AsLine)
124{
125 _Line line1, line2;
126 if (intersections.swapped()) {
127 std::swap(treat1AsLine, treat2AsLine);
128 std::swap(minT1, minT2);
129 std::swap(maxT1, maxT2);
130 }
131 // do line/quadratic or even line/line intersection instead
132 if (treat1AsLine) {
133 xy_at_t(quad1, minT1, line1[0].x, line1[0].y);
134 xy_at_t(quad1, maxT1, line1[1].x, line1[1].y);
135 }
136 if (treat2AsLine) {
137 xy_at_t(quad2, minT2, line2[0].x, line2[0].y);
138 xy_at_t(quad2, maxT2, line2[1].x, line2[1].y);
139 }
140 int pts;
141 double smallT, largeT;
142 if (treat1AsLine & treat2AsLine) {
143 double t1[2], t2[2];
144 pts = ::intersect(line1, line2, t1, t2);
145 for (int index = 0; index < pts; ++index) {
146 smallT = interp(minT1, maxT1, t1[index]);
147 largeT = interp(minT2, maxT2, t2[index]);
148 if (pts == 2) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000149 intersections.addCoincident(smallT, largeT, true);
caryclark@google.coma7e483d2012-08-28 20:44:43 +0000150 } else {
151 intersections.add(smallT, largeT);
152 }
153 }
154 } else {
155 Intersections lq;
156 pts = ::intersect(treat1AsLine ? quad2 : quad1,
157 treat1AsLine ? line1 : line2, lq);
158 bool coincident = false;
159 if (pts == 2) { // if the line and edge are coincident treat differently
160 _Point midQuad, midLine;
161 double midQuadT = (lq.fT[0][0] + lq.fT[0][1]) / 2;
162 xy_at_t(treat1AsLine ? quad2 : quad1, midQuadT, midQuad.x, midQuad.y);
163 double lineT = t_at(treat1AsLine ? line1 : line2, midQuad);
164 xy_at_t(treat1AsLine ? line1 : line2, lineT, midLine.x, midLine.y);
165 coincident = approximately_equal(midQuad.x, midLine.x)
166 && approximately_equal(midQuad.y, midLine.y);
167 }
168 for (int index = 0; index < pts; ++index) {
169 smallT = lq.fT[0][index];
170 largeT = lq.fT[1][index];
171 if (treat1AsLine) {
172 smallT = interp(minT1, maxT1, smallT);
173 } else {
174 largeT = interp(minT2, maxT2, largeT);
175 }
176 if (coincident) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000177 intersections.addCoincident(smallT, largeT, true);
caryclark@google.coma7e483d2012-08-28 20:44:43 +0000178 } else {
179 intersections.add(smallT, largeT);
180 }
181 }
182 }
183 return pts > 0;
184}
185
caryclark@google.com639df892012-01-10 21:46:10 +0000186bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
187 ++depth;
188 intersections.swap();
189 if (split) {
190 ++splits;
191 if (split & 2) {
192 double middle1 = (maxT1 + minT1) / 2;
193 intersect(minT1, middle1, minT2, maxT2);
194 intersect(middle1, maxT1, minT2, maxT2);
195 } else {
196 double middle2 = (maxT2 + minT2) / 2;
197 intersect(minT1, maxT1, minT2, middle2);
198 intersect(minT1, maxT1, middle2, maxT2);
199 }
200 --splits;
201 intersections.swap();
202 --depth;
203 return intersections.intersected();
204 }
205 bool result = intersect(minT1, maxT1, minT2, maxT2);
206 intersections.swap();
207 --depth;
208 return result;
209}
210
211private:
212
213static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
214const Quadratic& quad1;
215const Quadratic& quad2;
216Intersections& intersections;
217int depth;
218int splits;
caryclark@google.coma7e483d2012-08-28 20:44:43 +0000219double quad1Divisions; // line segments to approximate original within error
220double quad2Divisions;
caryclark@google.com639df892012-01-10 21:46:10 +0000221};
222
caryclark@google.comc6825902012-02-03 22:07:47 +0000223bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000224 if (implicit_matches(q1, q2)) {
225 // FIXME: compute T values
226 // compute the intersections of the ends to find the coincident span
227 bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y);
228 double t;
229 if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000230 i.addCoincident(t, 0, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000231 }
232 if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000233 i.addCoincident(t, 1, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000234 }
235 useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y);
236 if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000237 i.addCoincident(0, t, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000238 }
239 if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) {
caryclark@google.com32546db2012-08-31 20:55:07 +0000240 i.addCoincident(1, t, false);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000241 }
caryclark@google.com32546db2012-08-31 20:55:07 +0000242 assert(i.fCoincidentUsed <= 2);
243 return i.fCoincidentUsed > 0;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000244 }
caryclark@google.com639df892012-01-10 21:46:10 +0000245 QuadraticIntersections q(q1, q2, i);
246 return q.intersect();
247}
248
249
250// Another approach is to start with the implicit form of one curve and solve
251// by substituting in the parametric form of the other.
252// The downside of this approach is that early rejects are difficult to come by.
253// http://planetmath.org/encyclopedia/GaloisTheoreticDerivationOfTheQuarticFormula.html#step
254/*
255given x^4 + ax^3 + bx^2 + cx + d
256the resolvent cubic is x^3 - 2bx^2 + (b^2 + ac - 4d)x + (c^2 + a^2d - abc)
257use the cubic formula (CubicRoots.cpp) to find the radical expressions t1, t2, and t3.
258
259(x - r1 r2) (x - r3 r4) = x^2 - (t2 + t3 - t1) / 2 x + d
260s = r1*r2 = ((t2 + t3 - t1) + sqrt((t2 + t3 - t1)^2 - 16*d)) / 4
261t = r3*r4 = ((t2 + t3 - t1) - sqrt((t2 + t3 - t1)^2 - 16*d)) / 4
262
263u = r1+r2 = (-a + sqrt(a^2 - 4*t1)) / 2
264v = r3+r4 = (-a - sqrt(a^2 - 4*t1)) / 2
265
266r1 = (u + sqrt(u^2 - 4*s)) / 2
267r2 = (u - sqrt(u^2 - 4*s)) / 2
268r3 = (v + sqrt(v^2 - 4*t)) / 2
269r4 = (v - sqrt(v^2 - 4*t)) / 2
270*/
271
272
273/* square root of complex number
274http://en.wikipedia.org/wiki/Square_root#Square_roots_of_negative_and_complex_numbers
275Algebraic formula
276When the number is expressed using Cartesian coordinates the following formula
277 can be used for the principal square root:[5][6]
278
279sqrt(x + iy) = sqrt((r + x) / 2) +/- i*sqrt((r - x) / 2)
280
281where the sign of the imaginary part of the root is taken to be same as the sign
282 of the imaginary part of the original number, and
rmistry@google.comd6176b02012-08-23 18:14:13 +0000283
caryclark@google.com639df892012-01-10 21:46:10 +0000284r = abs(x + iy) = sqrt(x^2 + y^2)
285
rmistry@google.comd6176b02012-08-23 18:14:13 +0000286is the absolute value or modulus of the original number. The real part of the
caryclark@google.com639df892012-01-10 21:46:10 +0000287principal value is always non-negative.
rmistry@google.comd6176b02012-08-23 18:14:13 +0000288The other square root is simply –1 times the principal square root; in other
caryclark@google.com639df892012-01-10 21:46:10 +0000289words, the two square roots of a number sum to 0.
290 */