blob: b82b301a770bd01dfefc6df5214a8bf71da18163 [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.com9d5f99b2013-01-22 12:55:54 +00007
8#include "CubicUtilities.h"
caryclark@google.comc6825902012-02-03 22:07:47 +00009#include "CurveIntersection.h"
caryclark@google.com639df892012-01-10 21:46:10 +000010#include "Intersections.h"
caryclark@google.comc6825902012-02-03 22:07:47 +000011#include "IntersectionUtilities.h"
caryclark@google.com639df892012-01-10 21:46:10 +000012#include "LineIntersection.h"
caryclark@google.com9f602912013-01-24 21:47:16 +000013#include "LineUtilities.h"
caryclark@google.com639df892012-01-10 21:46:10 +000014
caryclark@google.comf9502d72013-02-04 14:06:49 +000015#define DEBUG_COMPUTE_DELTA 1
16#define COMPUTE_DELTA 0
17
caryclark@google.com0d3d09e2012-12-10 14:50:04 +000018static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
19
caryclark@google.comc6825902012-02-03 22:07:47 +000020class CubicIntersections : public Intersections {
21public:
22
rmistry@google.comd6176b02012-08-23 18:14:13 +000023CubicIntersections(const Cubic& c1, const Cubic& c2, Intersections& i)
caryclark@google.comc6825902012-02-03 22:07:47 +000024 : cubic1(c1)
25 , cubic2(c2)
26 , intersections(i)
rmistry@google.comd6176b02012-08-23 18:14:13 +000027 , depth(0)
caryclark@google.comc6825902012-02-03 22:07:47 +000028 , splits(0) {
caryclark@google.com639df892012-01-10 21:46:10 +000029}
30
caryclark@google.comc6825902012-02-03 22:07:47 +000031bool intersect() {
caryclark@google.com639df892012-01-10 21:46:10 +000032 double minT1, minT2, maxT1, maxT2;
33 if (!bezier_clip(cubic2, cubic1, minT1, maxT1)) {
34 return false;
35 }
36 if (!bezier_clip(cubic1, cubic2, minT2, maxT2)) {
37 return false;
38 }
caryclark@google.comc6825902012-02-03 22:07:47 +000039 int split;
caryclark@google.com639df892012-01-10 21:46:10 +000040 if (maxT1 - minT1 < maxT2 - minT2) {
41 intersections.swap();
caryclark@google.comc6825902012-02-03 22:07:47 +000042 minT2 = 0;
43 maxT2 = 1;
44 split = maxT1 - minT1 > tClipLimit;
45 } else {
46 minT1 = 0;
47 maxT1 = 1;
48 split = (maxT2 - minT2 > tClipLimit) << 1;
49 }
50 return chop(minT1, maxT1, minT2, maxT2, split);
caryclark@google.com639df892012-01-10 21:46:10 +000051}
caryclark@google.comc6825902012-02-03 22:07:47 +000052
53protected:
rmistry@google.comd6176b02012-08-23 18:14:13 +000054
caryclark@google.comc6825902012-02-03 22:07:47 +000055bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
56 Cubic smaller, larger;
rmistry@google.comd6176b02012-08-23 18:14:13 +000057 // FIXME: carry last subdivide and reduceOrder result with cubic
caryclark@google.comc6825902012-02-03 22:07:47 +000058 sub_divide(cubic1, minT1, maxT1, intersections.swapped() ? larger : smaller);
59 sub_divide(cubic2, minT2, maxT2, intersections.swapped() ? smaller : larger);
60 Cubic smallResult;
61 if (reduceOrder(smaller, smallResult,
62 kReduceOrder_NoQuadraticsAllowed) <= 2) {
63 Cubic largeResult;
64 if (reduceOrder(larger, largeResult,
65 kReduceOrder_NoQuadraticsAllowed) <= 2) {
66 const _Line& smallLine = (const _Line&) smallResult;
67 const _Line& largeLine = (const _Line&) largeResult;
68 double smallT[2];
69 double largeT[2];
70 // FIXME: this doesn't detect or deal with coincident lines
71 if (!::intersect(smallLine, largeLine, smallT, largeT)) {
72 return false;
73 }
74 if (intersections.swapped()) {
rmistry@google.comd6176b02012-08-23 18:14:13 +000075 smallT[0] = interp(minT2, maxT2, smallT[0]);
76 largeT[0] = interp(minT1, maxT1, largeT[0]);
caryclark@google.comc6825902012-02-03 22:07:47 +000077 } else {
rmistry@google.comd6176b02012-08-23 18:14:13 +000078 smallT[0] = interp(minT1, maxT1, smallT[0]);
79 largeT[0] = interp(minT2, maxT2, largeT[0]);
caryclark@google.comc6825902012-02-03 22:07:47 +000080 }
81 intersections.add(smallT[0], largeT[0]);
82 return true;
83 }
84 }
85 double minT, maxT;
86 if (!bezier_clip(smaller, larger, minT, maxT)) {
87 if (minT == maxT) {
88 if (intersections.swapped()) {
89 minT1 = (minT1 + maxT1) / 2;
90 minT2 = interp(minT2, maxT2, minT);
91 } else {
92 minT1 = interp(minT1, maxT1, minT);
93 minT2 = (minT2 + maxT2) / 2;
94 }
95 intersections.add(minT1, minT2);
96 return true;
97 }
98 return false;
99 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000100
caryclark@google.comc6825902012-02-03 22:07:47 +0000101 int split;
102 if (intersections.swapped()) {
103 double newMinT1 = interp(minT1, maxT1, minT);
104 double newMaxT1 = interp(minT1, maxT1, maxT);
105 split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
106#define VERBOSE 0
107#if VERBOSE
108 printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n",
109 __FUNCTION__, depth, splits, newMinT1, newMaxT1, minT1, maxT1,
110 split);
111#endif
112 minT1 = newMinT1;
113 maxT1 = newMaxT1;
114 } else {
115 double newMinT2 = interp(minT2, maxT2, minT);
116 double newMaxT2 = interp(minT2, maxT2, maxT);
117 split = newMaxT2 - newMinT2 > (maxT2 - minT2) * tClipLimit;
118#if VERBOSE
119 printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n",
120 __FUNCTION__, depth, splits, newMinT2, newMaxT2, minT2, maxT2,
121 split);
122#endif
123 minT2 = newMinT2;
124 maxT2 = newMaxT2;
125 }
126 return chop(minT1, maxT1, minT2, maxT2, split);
127}
128
129bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
130 ++depth;
131 intersections.swap();
132 if (split) {
133 ++splits;
134 if (split & 2) {
135 double middle1 = (maxT1 + minT1) / 2;
136 intersect(minT1, middle1, minT2, maxT2);
137 intersect(middle1, maxT1, minT2, maxT2);
138 } else {
139 double middle2 = (maxT2 + minT2) / 2;
140 intersect(minT1, maxT1, minT2, middle2);
141 intersect(minT1, maxT1, middle2, maxT2);
142 }
143 --splits;
144 intersections.swap();
145 --depth;
146 return intersections.intersected();
147 }
148 bool result = intersect(minT1, maxT1, minT2, maxT2);
149 intersections.swap();
150 --depth;
151 return result;
152}
153
154private:
155
caryclark@google.comc6825902012-02-03 22:07:47 +0000156const Cubic& cubic1;
157const Cubic& cubic2;
158Intersections& intersections;
159int depth;
160int splits;
161};
162
163bool intersect(const Cubic& c1, const Cubic& c2, Intersections& i) {
164 CubicIntersections c(c1, c2, i);
165 return c.intersect();
166}
167
caryclark@google.comf9502d72013-02-04 14:06:49 +0000168#if COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000169static void cubicTangent(const Cubic& cubic, double t, _Line& tangent, _Point& pt, _Point& dxy) {
170 xy_at_t(cubic, t, tangent[0].x, tangent[0].y);
171 pt = tangent[1] = tangent[0];
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000172 dxdy_at_t(cubic, t, dxy);
caryclark@google.comf9502d72013-02-04 14:06:49 +0000173 if (dxy.approximatelyZero()) {
174 if (approximately_zero(t)) {
175 SkASSERT(cubic[0].approximatelyEqual(cubic[1]));
176 dxy = cubic[2];
177 dxy -= cubic[0];
178 } else {
179 SkASSERT(approximately_equal(t, 1));
180 SkASSERT(cubic[3].approximatelyEqual(cubic[2]));
181 dxy = cubic[3];
182 dxy -= cubic[1];
183 }
184 SkASSERT(!dxy.approximatelyZero());
185 }
caryclark@google.com9f602912013-01-24 21:47:16 +0000186 tangent[0] -= dxy;
187 tangent[1] += dxy;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000188#if DEBUG_COMPUTE_DELTA
189 SkDebugf("%s t=%1.9g tangent=(%1.9g,%1.9g %1.9g,%1.9g)"
190 " pt=(%1.9g %1.9g) dxy=(%1.9g %1.9g)\n", __FUNCTION__, t,
191 tangent[0].x, tangent[0].y, tangent[1].x, tangent[1].y, pt.x, pt.y,
192 dxy.x, dxy.y);
193#endif
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000194}
caryclark@google.comf9502d72013-02-04 14:06:49 +0000195#endif
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000196
caryclark@google.comf9502d72013-02-04 14:06:49 +0000197#if COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000198static double cubicDelta(const _Point& dxy, _Line& tangent, double scale) {
199 double tangentLen = dxy.length();
200 tangent[0] -= tangent[1];
201 double intersectLen = tangent[0].length();
202 double result = intersectLen / tangentLen + scale;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000203#if DEBUG_COMPUTE_DELTA
204 SkDebugf("%s tangent=(%1.9g,%1.9g %1.9g,%1.9g) intersectLen=%1.9g tangentLen=%1.9g scale=%1.9g"
205 " result=%1.9g\n", __FUNCTION__, tangent[0].x, tangent[0].y, tangent[1].x, tangent[1].y,
206 intersectLen, tangentLen, scale, result);
207#endif
caryclark@google.com9f602912013-01-24 21:47:16 +0000208 return result;
209}
caryclark@google.comf9502d72013-02-04 14:06:49 +0000210#endif
caryclark@google.com9f602912013-01-24 21:47:16 +0000211
caryclark@google.comf9502d72013-02-04 14:06:49 +0000212#if COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000213// FIXME: after testing, make this static
caryclark@google.comf9502d72013-02-04 14:06:49 +0000214static void computeDelta(const Cubic& c1, double t1, double scale1, const Cubic& c2, double t2,
caryclark@google.com9f602912013-01-24 21:47:16 +0000215 double scale2, double& delta1, double& delta2) {
caryclark@google.comf9502d72013-02-04 14:06:49 +0000216#if DEBUG_COMPUTE_DELTA
217 SkDebugf("%s c1=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) t1=%1.9g scale1=%1.9g"
218 " c2=(%1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g %1.9g,%1.9g) t2=%1.9g scale2=%1.9g\n",
219 __FUNCTION__,
220 c1[0].x, c1[0].y, c1[1].x, c1[1].y, c1[2].x, c1[2].y, c1[3].x, c1[3].y, t1, scale1,
221 c2[0].x, c2[0].y, c2[1].x, c2[1].y, c2[2].x, c2[2].y, c2[3].x, c2[3].y, t2, scale2);
222#endif
caryclark@google.com9f602912013-01-24 21:47:16 +0000223 _Line tangent1, tangent2, line1, line2;
224 _Point dxy1, dxy2;
225 cubicTangent(c1, t1, line1, tangent1[0], dxy1);
226 cubicTangent(c2, t2, line2, tangent2[0], dxy2);
227 double range1[2], range2[2];
228 int found = intersect(line1, line2, range1, range2);
229 if (found == 0) {
230 range1[0] = 0.5;
231 } else {
232 SkASSERT(found == 1);
233 }
234 xy_at_t(line1, range1[0], tangent1[1].x, tangent1[1].y);
235#if SK_DEBUG
236 if (found == 1) {
237 xy_at_t(line2, range2[0], tangent2[1].x, tangent2[1].y);
238 SkASSERT(tangent2[1].approximatelyEqual(tangent1[1]));
239 }
240#endif
241 tangent2[1] = tangent1[1];
242 delta1 = cubicDelta(dxy1, tangent1, scale1 / precisionUnit);
243 delta2 = cubicDelta(dxy2, tangent2, scale2 / precisionUnit);
244}
245
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000246#if SK_DEBUG
caryclark@google.com9f602912013-01-24 21:47:16 +0000247int debugDepth;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000248#endif
caryclark@google.comf9502d72013-02-04 14:06:49 +0000249#endif
250
251static int quadPart(const Cubic& cubic, double tStart, double tEnd, Quadratic& simple) {
252 Cubic part;
253 sub_divide(cubic, tStart, tEnd, part);
254 Quadratic quad;
255 demote_cubic_to_quad(part, quad);
256 // FIXME: should reduceOrder be looser in this use case if quartic is going to blow up on an
257 // extremely shallow quadratic?
258 int order = reduceOrder(quad, simple);
259 return order;
260}
261
262static void intersectWithOrder(const Quadratic& simple1, int order1, const Quadratic& simple2,
263 int order2, Intersections& i) {
264 if (order1 == 3 && order2 == 3) {
265 intersect2(simple1, simple2, i);
266 } else if (order1 <= 2 && order2 <= 2) {
267 i.fUsed = intersect((const _Line&) simple1, (const _Line&) simple2, i.fT[0], i.fT[1]);
268 } else if (order1 == 3 && order2 <= 2) {
269 intersect(simple1, (const _Line&) simple2, i);
270 } else {
271 SkASSERT(order1 <= 2 && order2 == 3);
272 intersect(simple2, (const _Line&) simple1, i);
273 for (int s = 0; s < i.fUsed; ++s) {
274 SkTSwap(i.fT[0][s], i.fT[1][s]);
275 }
276 }
277}
278
279static void doIntersect(const Cubic& cubic1, double t1s, double t1m, double t1e,
280 const Cubic& cubic2, double t2s, double t2m, double t2e, Intersections& i) {
281 i.upDepth();
282 // divide the quadratics at the new t value and try again
283 double p1s = t1s;
284 double p1e = t1m;
285 for (int p1 = 0; p1 < 2; ++p1) {
286 Quadratic s1a;
287 int o1a = quadPart(cubic1, p1s, p1e, s1a);
288 double p2s = t2s;
289 double p2e = t2m;
290 for (int p2 = 0; p2 < 2; ++p2) {
291 Quadratic s2a;
292 int o2a = quadPart(cubic2, p2s, p2e, s2a);
293 Intersections locals;
294 #if 0 && SK_DEBUG
295 if (0.366025505 >= p1s && 0.366025887 <= p1e
296 && 0.633974342 >= p2s && 0.633975487 <= p2e) {
297 SkDebugf("t1=(%1.9g,%1.9g) o1=%d t2=(%1.9g,%1.9g) o2=%d\n",
298 p1s, p1e, o1a, p2s, p2e, o2a);
299 if (o1a == 2) {
300 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
301 s1a[0].x, s1a[0].y, s1a[1].x, s1a[1].y);
302 } else {
303 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
304 s1a[0].x, s1a[0].y, s1a[1].x, s1a[1].y, s1a[2].x, s1a[2].y);
305 }
306 if (o2a == 2) {
307 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
308 s2a[0].x, s2a[0].y, s2a[1].x, s2a[1].y);
309 } else {
310 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
311 s2a[0].x, s2a[0].y, s2a[1].x, s2a[1].y, s2a[2].x, s2a[2].y);
312 }
313 Intersections xlocals;
314 intersectWithOrder(s1a, o1a, s2a, o2a, xlocals);
skia.committer@gmail.com0c38ed32013-02-05 07:02:01 +0000315 }
caryclark@google.comf9502d72013-02-04 14:06:49 +0000316 #endif
317 intersectWithOrder(s1a, o1a, s2a, o2a, locals);
318 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
319 double to1 = p1s + (p1e - p1s) * locals.fT[0][tIdx];
320 double to2 = p2s + (p2e - p2s) * locals.fT[1][tIdx];
321 // if the computed t is not sufficiently precise, iterate
322 _Point p1, p2;
323 xy_at_t(cubic1, to1, p1.x, p1.y);
324 xy_at_t(cubic2, to2, p2.x, p2.y);
325 #if 0 && SK_DEBUG
326 SkDebugf("to1=%1.9g p1=(%1.9g,%1.9g) to2=%1.9g p2=(%1.9g,%1.9g) d=%1.9g\n",
327 to1, p1.x, p1.y, to2, p2.x, p2.y, p1.distance(p2));
skia.committer@gmail.com0c38ed32013-02-05 07:02:01 +0000328
caryclark@google.comf9502d72013-02-04 14:06:49 +0000329 #endif
330 if (p1.approximatelyEqual(p2)) {
331 i.insert(i.swapped() ? to2 : to1, i.swapped() ? to1 : to2);
332 } else {
333 doIntersect(cubic1, p1s, to1, p1e, cubic2, p2s, to2, p2e, i);
334 }
335 }
336 p2s = p2e;
337 p2e = t2e;
338 }
339 p1s = p1e;
340 p1e = t1e;
341 }
342 i.downDepth();
343}
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000344
caryclark@google.com73ca6242013-01-17 21:02:47 +0000345// this flavor approximates the cubics with quads to find the intersecting ts
346// OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used
347// to create the approximations, could be stored in the cubic segment
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000348// FIXME: this strategy needs to intersect the convex hull on either end with the opposite to
349// account for inset quadratics that cause the endpoint intersection to avoid detection
350// the segments can be very short -- the length of the maximum quadratic error (precision)
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000351static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2,
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000352 double t2s, double t2e, double precisionScale, Intersections& i) {
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000353 Cubic c1, c2;
354 sub_divide(cubic1, t1s, t1e, c1);
355 sub_divide(cubic2, t2s, t2e, c2);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000356 SkTDArray<double> ts1;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000357 cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000358 SkTDArray<double> ts2;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000359 cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000360 double t1Start = t1s;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000361 int ts1Count = ts1.count();
362 for (int i1 = 0; i1 <= ts1Count; ++i1) {
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000363 const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1;
364 const double t1 = t1s + (t1e - t1s) * tEnd1;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000365 Quadratic s1;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000366 int o1 = quadPart(cubic1, t1Start, t1, s1);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000367 double t2Start = t2s;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000368 int ts2Count = ts2.count();
369 for (int i2 = 0; i2 <= ts2Count; ++i2) {
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000370 const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1;
371 const double t2 = t2s + (t2e - t2s) * tEnd2;
caryclark@google.com73ca6242013-01-17 21:02:47 +0000372 Quadratic s2;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000373 int o2 = quadPart(cubic2, t2Start, t2, s2);
caryclark@google.com73ca6242013-01-17 21:02:47 +0000374 Intersections locals;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000375 intersectWithOrder(s1, o1, s2, o2, locals);
skia.committer@gmail.com0c38ed32013-02-05 07:02:01 +0000376
caryclark@google.com73ca6242013-01-17 21:02:47 +0000377 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
378 double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx];
379 double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx];
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000380 // if the computed t is not sufficiently precise, iterate
381 _Point p1, p2;
382 xy_at_t(cubic1, to1, p1.x, p1.y);
383 xy_at_t(cubic2, to2, p2.x, p2.y);
384 if (p1.approximatelyEqual(p2)) {
385 i.insert(i.swapped() ? to2 : to1, i.swapped() ? to1 : to2);
386 } else {
caryclark@google.comf9502d72013-02-04 14:06:49 +0000387 #if COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000388 double dt1, dt2;
389 computeDelta(cubic1, to1, (t1e - t1s), cubic2, to2, (t2e - t2s), dt1, dt2);
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000390 double scale = precisionScale;
391 if (dt1 > 0.125 || dt2 > 0.125) {
392 scale /= 2;
393 SkDebugf("%s scale=%1.9g\n", __FUNCTION__, scale);
394 }
395#if SK_DEBUG
caryclark@google.com9f602912013-01-24 21:47:16 +0000396 ++debugDepth;
caryclark@google.comaa358312013-01-29 20:28:49 +0000397 SkASSERT(debugDepth < 10);
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000398#endif
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000399 i.swap();
caryclark@google.com9f602912013-01-24 21:47:16 +0000400 intersect2(cubic2, SkTMax(to2 - dt2, 0.), SkTMin(to2 + dt2, 1.),
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000401 cubic1, SkTMax(to1 - dt1, 0.), SkTMin(to1 + dt1, 1.), scale, i);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000402 i.swap();
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000403#if SK_DEBUG
caryclark@google.com9f602912013-01-24 21:47:16 +0000404 --debugDepth;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000405#endif
caryclark@google.comf9502d72013-02-04 14:06:49 +0000406 #else
407 doIntersect(cubic1, t1Start, to1, t1, cubic2, t2Start, to2, t2, i);
408 #endif
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000409 }
caryclark@google.com73ca6242013-01-17 21:02:47 +0000410 }
411 t2Start = t2;
412 }
413 t1Start = t1;
414 }
415 return i.intersected();
416}
417
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000418static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, const _Rect& bounds2,
419 Intersections& i) {
420 _Line line1;
caryclark@google.comf9502d72013-02-04 14:06:49 +0000421 line1[1] = cubic1[start ? 0 : 3];
422 if (line1[1].approximatelyEqual(cubic2[0]) || line1[1].approximatelyEqual(cubic2[3])) {
423 return false;
424 }
425 line1[0] = line1[1];
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000426 _Point dxy1 = line1[0] - cubic1[start ? 1 : 2];
caryclark@google.comf9502d72013-02-04 14:06:49 +0000427 if (dxy1.approximatelyZero()) {
428 dxy1 = line1[0] - cubic1[start ? 2 : 1];
429 }
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000430 dxy1 /= precisionUnit;
431 line1[1] += dxy1;
432 _Rect line1Bounds;
433 line1Bounds.setBounds(line1);
434 if (!bounds2.intersects(line1Bounds)) {
435 return false;
436 }
437 _Line line2;
438 line2[0] = line2[1] = line1[0];
439 _Point dxy2 = line2[0] - cubic1[start ? 3 : 0];
caryclark@google.comf9502d72013-02-04 14:06:49 +0000440 SkASSERT(!dxy2.approximatelyZero());
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000441 dxy2 /= precisionUnit;
442 line2[1] += dxy2;
443#if 0 // this is so close to the first bounds test it isn't worth the short circuit test
444 _Rect line2Bounds;
445 line2Bounds.setBounds(line2);
446 if (!bounds2.intersects(line2Bounds)) {
447 return false;
448 }
449#endif
450 Intersections local1;
451 if (!intersect(cubic2, line1, local1)) {
452 return false;
453 }
454 Intersections local2;
455 if (!intersect(cubic2, line2, local2)) {
456 return false;
457 }
458 double tMin, tMax;
459 tMin = tMax = local1.fT[0][0];
460 for (int index = 1; index < local1.fUsed; ++index) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000461 tMin = SkTMin(tMin, local1.fT[0][index]);
462 tMax = SkTMax(tMax, local1.fT[0][index]);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000463 }
464 for (int index = 1; index < local2.fUsed; ++index) {
caryclark@google.comaa358312013-01-29 20:28:49 +0000465 tMin = SkTMin(tMin, local2.fT[0][index]);
466 tMax = SkTMax(tMax, local2.fT[0][index]);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000467 }
caryclark@google.comf9502d72013-02-04 14:06:49 +0000468#if SK_DEBUG && COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000469 debugDepth = 0;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000470#endif
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000471 return intersect2(cubic1, start ? 0 : 1, start ? 1.0 / precisionUnit : 1 - 1.0 / precisionUnit,
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000472 cubic2, tMin, tMax, 1, i);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000473}
474
caryclark@google.comf9502d72013-02-04 14:06:49 +0000475// FIXME: add intersection of convex hull on cubics' ends with the opposite cubic. The hull line
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000476// segments can be constructed to be only as long as the calculated precision suggests. If the hull
477// line segments intersect the cubic, then use the intersections to construct a subdivision for
478// quadratic curve fitting.
479bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
caryclark@google.comf9502d72013-02-04 14:06:49 +0000480#if SK_DEBUG && COMPUTE_DELTA
caryclark@google.com9f602912013-01-24 21:47:16 +0000481 debugDepth = 0;
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000482#endif
483 bool result = intersect2(c1, 0, 1, c2, 0, 1, 1, i);
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000484 // FIXME: pass in cached bounds from caller
485 _Rect c1Bounds, c2Bounds;
486 c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
487 c2Bounds.setBounds(c2);
488 result |= intersectEnd(c1, false, c2, c2Bounds, i);
489 result |= intersectEnd(c1, true, c2, c2Bounds, i);
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000490 i.swap();
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000491 result |= intersectEnd(c2, false, c1, c1Bounds, i);
492 result |= intersectEnd(c2, true, c1, c1Bounds, i);
caryclark@google.com85ec74c2013-01-28 19:25:51 +0000493 i.swap();
caryclark@google.com05c4bad2013-01-19 13:22:39 +0000494 return result;
495}
496
caryclark@google.com73ca6242013-01-17 21:02:47 +0000497int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) {
498 SkTDArray<double> ts;
499 double precision = calcPrecision(cubic);
500 cubic_to_quadratics(cubic, precision, ts);
501 double tStart = 0;
502 Cubic part;
503 int tsCount = ts.count();
504 for (int idx = 0; idx <= tsCount; ++idx) {
505 double t = idx < tsCount ? ts[idx] : 1;
506 Quadratic q1;
507 sub_divide(cubic, tStart, t, part);
508 demote_cubic_to_quad(part, q1);
509 Intersections locals;
510 intersect2(q1, quad, locals);
511 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
512 double globalT = tStart + (t - tStart) * locals.fT[0][tIdx];
513 i.insertOne(globalT, 0);
514 globalT = locals.fT[1][tIdx];
515 i.insertOne(globalT, 1);
516 }
517 tStart = t;
518 }
519 return i.used();
520}
521
522bool intersect(const Cubic& cubic, Intersections& i) {
523 SkTDArray<double> ts;
524 double precision = calcPrecision(cubic);
525 cubic_to_quadratics(cubic, precision, ts);
526 int tsCount = ts.count();
527 if (tsCount == 1) {
528 return false;
529 }
530 double t1Start = 0;
531 Cubic part;
532 for (int idx = 0; idx < tsCount; ++idx) {
533 double t1 = ts[idx];
534 Quadratic q1;
535 sub_divide(cubic, t1Start, t1, part);
536 demote_cubic_to_quad(part, q1);
537 double t2Start = t1;
538 for (int i2 = idx + 1; i2 <= tsCount; ++i2) {
539 const double t2 = i2 < tsCount ? ts[i2] : 1;
540 Quadratic q2;
541 sub_divide(cubic, t2Start, t2, part);
542 demote_cubic_to_quad(part, q2);
543 Intersections locals;
544 intersect2(q1, q2, locals);
545 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
546 // discard intersections at cusp? (maximum curvature)
547 double t1sect = locals.fT[0][tIdx];
548 double t2sect = locals.fT[1][tIdx];
549 if (idx + 1 == i2 && t1sect == 1 && t2sect == 0) {
550 continue;
551 }
552 double to1 = t1Start + (t1 - t1Start) * t1sect;
553 double to2 = t2Start + (t2 - t2Start) * t2sect;
554 i.insert(to1, to2);
555 }
556 t2Start = t2;
557 }
558 t1Start = t1;
559 }
560 return i.intersected();
561}