blob: d5a5bdef780b0a66ad5be74d3bfe78b00d76b7de [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"
caryclark@google.comc6825902012-02-03 22:07:47 +00009#include "IntersectionUtilities.h"
caryclark@google.com639df892012-01-10 21:46:10 +000010#include "LineIntersection.h"
11
caryclark@google.com0d3d09e2012-12-10 14:50:04 +000012static const double tClipLimit = 0.8; // http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf see Multiple intersections
13
caryclark@google.comc6825902012-02-03 22:07:47 +000014class CubicIntersections : public Intersections {
15public:
16
rmistry@google.comd6176b02012-08-23 18:14:13 +000017CubicIntersections(const Cubic& c1, const Cubic& c2, Intersections& i)
caryclark@google.comc6825902012-02-03 22:07:47 +000018 : cubic1(c1)
19 , cubic2(c2)
20 , intersections(i)
rmistry@google.comd6176b02012-08-23 18:14:13 +000021 , depth(0)
caryclark@google.comc6825902012-02-03 22:07:47 +000022 , splits(0) {
caryclark@google.com639df892012-01-10 21:46:10 +000023}
24
caryclark@google.comc6825902012-02-03 22:07:47 +000025bool intersect() {
caryclark@google.com639df892012-01-10 21:46:10 +000026 double minT1, minT2, maxT1, maxT2;
27 if (!bezier_clip(cubic2, cubic1, minT1, maxT1)) {
28 return false;
29 }
30 if (!bezier_clip(cubic1, cubic2, minT2, maxT2)) {
31 return false;
32 }
caryclark@google.comc6825902012-02-03 22:07:47 +000033 int split;
caryclark@google.com639df892012-01-10 21:46:10 +000034 if (maxT1 - minT1 < maxT2 - minT2) {
35 intersections.swap();
caryclark@google.comc6825902012-02-03 22:07:47 +000036 minT2 = 0;
37 maxT2 = 1;
38 split = maxT1 - minT1 > tClipLimit;
39 } else {
40 minT1 = 0;
41 maxT1 = 1;
42 split = (maxT2 - minT2 > tClipLimit) << 1;
43 }
44 return chop(minT1, maxT1, minT2, maxT2, split);
caryclark@google.com639df892012-01-10 21:46:10 +000045}
caryclark@google.comc6825902012-02-03 22:07:47 +000046
47protected:
rmistry@google.comd6176b02012-08-23 18:14:13 +000048
caryclark@google.comc6825902012-02-03 22:07:47 +000049bool intersect(double minT1, double maxT1, double minT2, double maxT2) {
50 Cubic smaller, larger;
rmistry@google.comd6176b02012-08-23 18:14:13 +000051 // FIXME: carry last subdivide and reduceOrder result with cubic
caryclark@google.comc6825902012-02-03 22:07:47 +000052 sub_divide(cubic1, minT1, maxT1, intersections.swapped() ? larger : smaller);
53 sub_divide(cubic2, minT2, maxT2, intersections.swapped() ? smaller : larger);
54 Cubic smallResult;
55 if (reduceOrder(smaller, smallResult,
56 kReduceOrder_NoQuadraticsAllowed) <= 2) {
57 Cubic largeResult;
58 if (reduceOrder(larger, largeResult,
59 kReduceOrder_NoQuadraticsAllowed) <= 2) {
60 const _Line& smallLine = (const _Line&) smallResult;
61 const _Line& largeLine = (const _Line&) largeResult;
62 double smallT[2];
63 double largeT[2];
64 // FIXME: this doesn't detect or deal with coincident lines
65 if (!::intersect(smallLine, largeLine, smallT, largeT)) {
66 return false;
67 }
68 if (intersections.swapped()) {
rmistry@google.comd6176b02012-08-23 18:14:13 +000069 smallT[0] = interp(minT2, maxT2, smallT[0]);
70 largeT[0] = interp(minT1, maxT1, largeT[0]);
caryclark@google.comc6825902012-02-03 22:07:47 +000071 } else {
rmistry@google.comd6176b02012-08-23 18:14:13 +000072 smallT[0] = interp(minT1, maxT1, smallT[0]);
73 largeT[0] = interp(minT2, maxT2, largeT[0]);
caryclark@google.comc6825902012-02-03 22:07:47 +000074 }
75 intersections.add(smallT[0], largeT[0]);
76 return true;
77 }
78 }
79 double minT, maxT;
80 if (!bezier_clip(smaller, larger, minT, maxT)) {
81 if (minT == maxT) {
82 if (intersections.swapped()) {
83 minT1 = (minT1 + maxT1) / 2;
84 minT2 = interp(minT2, maxT2, minT);
85 } else {
86 minT1 = interp(minT1, maxT1, minT);
87 minT2 = (minT2 + maxT2) / 2;
88 }
89 intersections.add(minT1, minT2);
90 return true;
91 }
92 return false;
93 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000094
caryclark@google.comc6825902012-02-03 22:07:47 +000095 int split;
96 if (intersections.swapped()) {
97 double newMinT1 = interp(minT1, maxT1, minT);
98 double newMaxT1 = interp(minT1, maxT1, maxT);
99 split = (newMaxT1 - newMinT1 > (maxT1 - minT1) * tClipLimit) << 1;
100#define VERBOSE 0
101#if VERBOSE
102 printf("%s d=%d s=%d new1=(%g,%g) old1=(%g,%g) split=%d\n",
103 __FUNCTION__, depth, splits, newMinT1, newMaxT1, minT1, maxT1,
104 split);
105#endif
106 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;
112#if VERBOSE
113 printf("%s d=%d s=%d new2=(%g,%g) old2=(%g,%g) split=%d\n",
114 __FUNCTION__, depth, splits, newMinT2, newMaxT2, minT2, maxT2,
115 split);
116#endif
117 minT2 = newMinT2;
118 maxT2 = newMaxT2;
119 }
120 return chop(minT1, maxT1, minT2, maxT2, split);
121}
122
123bool chop(double minT1, double maxT1, double minT2, double maxT2, int split) {
124 ++depth;
125 intersections.swap();
126 if (split) {
127 ++splits;
128 if (split & 2) {
129 double middle1 = (maxT1 + minT1) / 2;
130 intersect(minT1, middle1, minT2, maxT2);
131 intersect(middle1, maxT1, minT2, maxT2);
132 } else {
133 double middle2 = (maxT2 + minT2) / 2;
134 intersect(minT1, maxT1, minT2, middle2);
135 intersect(minT1, maxT1, middle2, maxT2);
136 }
137 --splits;
138 intersections.swap();
139 --depth;
140 return intersections.intersected();
141 }
142 bool result = intersect(minT1, maxT1, minT2, maxT2);
143 intersections.swap();
144 --depth;
145 return result;
146}
147
148private:
149
caryclark@google.comc6825902012-02-03 22:07:47 +0000150const Cubic& cubic1;
151const Cubic& cubic2;
152Intersections& intersections;
153int depth;
154int splits;
155};
156
157bool intersect(const Cubic& c1, const Cubic& c2, Intersections& i) {
158 CubicIntersections c(c1, c2, i);
159 return c.intersect();
160}
161
caryclark@google.com73ca6242013-01-17 21:02:47 +0000162#include "CubicUtilities.h"
163
164// this flavor approximates the cubics with quads to find the intersecting ts
165// OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used
166// to create the approximations, could be stored in the cubic segment
167// fixme: this strategy needs to add short line segments on either end, or similarly extend the
168// initial and final quadratics
169bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) {
170 SkTDArray<double> ts1;
171 double precision1 = calcPrecision(c1);
172 cubic_to_quadratics(c1, precision1, ts1);
173 SkTDArray<double> ts2;
174 double precision2 = calcPrecision(c2);
175 cubic_to_quadratics(c2, precision2, ts2);
176 double t1Start = 0;
177 int ts1Count = ts1.count();
178 for (int i1 = 0; i1 <= ts1Count; ++i1) {
179 const double t1 = i1 < ts1Count ? ts1[i1] : 1;
180 Cubic part1;
181 sub_divide(c1, t1Start, t1, part1);
182 Quadratic q1;
183 demote_cubic_to_quad(part1, q1);
184 // start here;
185 // should reduceOrder be looser in this use case if quartic is going to blow up on an
186 // extremely shallow quadratic?
187 // maybe quadratics to lines need the same sort of recursive solution that I hope to find
188 // for cubics to quadratics ...
189 Quadratic s1;
190 int o1 = reduceOrder(q1, s1);
191 double t2Start = 0;
192 int ts2Count = ts2.count();
193 for (int i2 = 0; i2 <= ts2Count; ++i2) {
194 const double t2 = i2 < ts2Count ? ts2[i2] : 1;
195 Cubic part2;
196 sub_divide(c2, t2Start, t2, part2);
197 Quadratic q2;
198 demote_cubic_to_quad(part2, q2);
199 Quadratic s2;
200 double o2 = reduceOrder(q2, s2);
201 Intersections locals;
202 if (o1 == 3 && o2 == 3) {
203 intersect2(q1, q2, locals);
204 } else if (o1 <= 2 && o2 <= 2) {
205 i.fUsed = intersect((const _Line&) s1, (const _Line&) s2, i.fT[0], i.fT[1]);
206 } else if (o1 == 3 && o2 <= 2) {
207 intersect(q1, (const _Line&) s2, i);
208 } else {
209 SkASSERT(o1 <= 2 && o2 == 3);
210 intersect(q2, (const _Line&) s1, i);
211 for (int s = 0; s < i.fUsed; ++s) {
212 SkTSwap(i.fT[0][s], i.fT[1][s]);
213 }
214 }
215 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
216 double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx];
217 double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx];
218 i.insert(to1, to2);
219 }
220 t2Start = t2;
221 }
222 t1Start = t1;
223 }
224 return i.intersected();
225}
226
227int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) {
228 SkTDArray<double> ts;
229 double precision = calcPrecision(cubic);
230 cubic_to_quadratics(cubic, precision, ts);
231 double tStart = 0;
232 Cubic part;
233 int tsCount = ts.count();
234 for (int idx = 0; idx <= tsCount; ++idx) {
235 double t = idx < tsCount ? ts[idx] : 1;
236 Quadratic q1;
237 sub_divide(cubic, tStart, t, part);
238 demote_cubic_to_quad(part, q1);
239 Intersections locals;
240 intersect2(q1, quad, locals);
241 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
242 double globalT = tStart + (t - tStart) * locals.fT[0][tIdx];
243 i.insertOne(globalT, 0);
244 globalT = locals.fT[1][tIdx];
245 i.insertOne(globalT, 1);
246 }
247 tStart = t;
248 }
249 return i.used();
250}
251
252bool intersect(const Cubic& cubic, Intersections& i) {
253 SkTDArray<double> ts;
254 double precision = calcPrecision(cubic);
255 cubic_to_quadratics(cubic, precision, ts);
256 int tsCount = ts.count();
257 if (tsCount == 1) {
258 return false;
259 }
260 double t1Start = 0;
261 Cubic part;
262 for (int idx = 0; idx < tsCount; ++idx) {
263 double t1 = ts[idx];
264 Quadratic q1;
265 sub_divide(cubic, t1Start, t1, part);
266 demote_cubic_to_quad(part, q1);
267 double t2Start = t1;
268 for (int i2 = idx + 1; i2 <= tsCount; ++i2) {
269 const double t2 = i2 < tsCount ? ts[i2] : 1;
270 Quadratic q2;
271 sub_divide(cubic, t2Start, t2, part);
272 demote_cubic_to_quad(part, q2);
273 Intersections locals;
274 intersect2(q1, q2, locals);
275 for (int tIdx = 0; tIdx < locals.used(); ++tIdx) {
276 // discard intersections at cusp? (maximum curvature)
277 double t1sect = locals.fT[0][tIdx];
278 double t2sect = locals.fT[1][tIdx];
279 if (idx + 1 == i2 && t1sect == 1 && t2sect == 0) {
280 continue;
281 }
282 double to1 = t1Start + (t1 - t1Start) * t1sect;
283 double to2 = t2Start + (t2 - t2Start) * t2sect;
284 i.insert(to1, to2);
285 }
286 t2Start = t2;
287 }
288 t1Start = t1;
289 }
290 return i.intersected();
291}