blob: 0f9a92a93ab9769f2fa84951188bbc49246e6a58 [file] [log] [blame]
caryclark@google.comfa0588f2012-04-26 21:01:06 +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.comb45a1b42012-05-18 20:50:33 +00007#include "Simplify.h"
caryclark@google.comfa0588f2012-04-26 21:01:06 +00008
9#undef SkASSERT
10#define SkASSERT(cond) while (!(cond)) { sk_throw(); }
11
caryclark@google.com15fa1382012-05-07 20:49:36 +000012// Terminology:
13// A Path contains one of more Contours
14// A Contour is made up of Segment array
caryclark@google.comb45a1b42012-05-18 20:50:33 +000015// A Segment is described by a Verb and a Point array with 2, 3, or 4 points
16// A Verb is one of Line, Quad(ratic), or Cubic
caryclark@google.com15fa1382012-05-07 20:49:36 +000017// A Segment contains a Span array
18// A Span is describes a portion of a Segment using starting and ending T
19// T values range from 0 to 1, where 0 is the first Point in the Segment
20
caryclark@google.comfa0588f2012-04-26 21:01:06 +000021// FIXME: remove once debugging is complete
22#if 0 // set to 1 for no debugging whatsoever
23
24//const bool gxRunTestsInOneThread = false;
25
26#define DEBUG_ADD_INTERSECTING_TS 0
27#define DEBUG_BRIDGE 0
28#define DEBUG_DUMP 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000029#define DEBUG_PATH_CONSTRUCTION 0
30#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000031
32#else
33
34//const bool gRunTestsInOneThread = true;
35
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000036#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000037#define DEBUG_BRIDGE 1
38#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000039#define DEBUG_PATH_CONSTRUCTION 1
40#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041
42#endif
43
44#if DEBUG_DUMP
45static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000046// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000047static int gContourID;
48static int gSegmentID;
49#endif
50
51static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
52 Intersections& intersections) {
53 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
54 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
55 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
56}
57
58static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
59 Intersections& intersections) {
60 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
61 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
62 intersect(aQuad, bLine, intersections);
63 return intersections.fUsed;
64}
65
66static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
67 Intersections& intersections) {
68 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
69 {a[3].fX, a[3].fY}};
70 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
71 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
72}
73
74static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
75 Intersections& intersections) {
76 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
77 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
78 intersect(aQuad, bQuad, intersections);
79 return intersections.fUsed;
80}
81
82static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
83 Intersections& intersections) {
84 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
85 {a[3].fX, a[3].fY}};
86 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
87 {b[3].fX, b[3].fY}};
88 intersect(aCubic, bCubic, intersections);
89 return intersections.fUsed;
90}
91
92static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
93 SkScalar y, bool flipped, Intersections& intersections) {
94 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
95 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
96}
97
98static int VLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
99 SkScalar y, bool flipped, Intersections& intersections) {
100 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
101 return verticalIntersect(aLine, left, right, y, flipped, intersections);
102}
103
104static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
105 SkScalar y, bool flipped, Intersections& intersections) {
106 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
107 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
108}
109
110static int VQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
111 SkScalar y, bool flipped, Intersections& intersections) {
112 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
113 return verticalIntersect(aQuad, left, right, y, flipped, intersections);
114}
115
116static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
117 SkScalar y, bool flipped, Intersections& intersections) {
118 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
119 {a[3].fX, a[3].fY}};
120 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
121}
122
123static int VCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
124 SkScalar y, bool flipped, Intersections& intersections) {
125 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
126 {a[3].fX, a[3].fY}};
127 return verticalIntersect(aCubic, left, right, y, flipped, intersections);
128}
129
130static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
131 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
132 double x, y;
133 xy_at_t(line, t, x, y);
134 out->fX = SkDoubleToScalar(x);
135 out->fY = SkDoubleToScalar(y);
136}
137
138static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
139 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
140 double x, y;
141 xy_at_t(quad, t, x, y);
142 out->fX = SkDoubleToScalar(x);
143 out->fY = SkDoubleToScalar(y);
144}
145
146static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
147 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
148 {a[3].fX, a[3].fY}};
149 double x, y;
150 xy_at_t(cubic, t, x, y);
151 out->fX = SkDoubleToScalar(x);
152 out->fY = SkDoubleToScalar(y);
153}
154
155static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
156 NULL,
157 LineXYAtT,
158 QuadXYAtT,
159 CubicXYAtT
160};
161
162static SkScalar LineXAtT(const SkPoint a[2], double t) {
163 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
164 double x;
165 xy_at_t(aLine, t, x, *(double*) 0);
166 return SkDoubleToScalar(x);
167}
168
169static SkScalar QuadXAtT(const SkPoint a[3], double t) {
170 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
171 double x;
172 xy_at_t(quad, t, x, *(double*) 0);
173 return SkDoubleToScalar(x);
174}
175
176static SkScalar CubicXAtT(const SkPoint a[4], double t) {
177 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
178 {a[3].fX, a[3].fY}};
179 double x;
180 xy_at_t(cubic, t, x, *(double*) 0);
181 return SkDoubleToScalar(x);
182}
183
184static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
185 NULL,
186 LineXAtT,
187 QuadXAtT,
188 CubicXAtT
189};
190
191static SkScalar LineYAtT(const SkPoint a[2], double t) {
192 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
193 double y;
194 xy_at_t(aLine, t, *(double*) 0, y);
195 return SkDoubleToScalar(y);
196}
197
198static SkScalar QuadYAtT(const SkPoint a[3], double t) {
199 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
200 double y;
201 xy_at_t(quad, t, *(double*) 0, y);
202 return SkDoubleToScalar(y);
203}
204
205static SkScalar CubicYAtT(const SkPoint a[4], double t) {
206 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
207 {a[3].fX, a[3].fY}};
208 double y;
209 xy_at_t(cubic, t, *(double*) 0, y);
210 return SkDoubleToScalar(y);
211}
212
213static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
214 NULL,
215 LineYAtT,
216 QuadYAtT,
217 CubicYAtT
218};
219
220static void LineSubDivide(const SkPoint a[2], double startT, double endT,
221 SkPoint sub[2]) {
222 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
223 _Line dst;
224 sub_divide(aLine, startT, endT, dst);
225 sub[0].fX = SkDoubleToScalar(dst[0].x);
226 sub[0].fY = SkDoubleToScalar(dst[0].y);
227 sub[1].fX = SkDoubleToScalar(dst[1].x);
228 sub[1].fY = SkDoubleToScalar(dst[1].y);
229}
230
231static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
232 SkPoint sub[3]) {
233 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
234 {a[2].fX, a[2].fY}};
235 Quadratic dst;
236 sub_divide(aQuad, startT, endT, dst);
237 sub[0].fX = SkDoubleToScalar(dst[0].x);
238 sub[0].fY = SkDoubleToScalar(dst[0].y);
239 sub[1].fX = SkDoubleToScalar(dst[1].x);
240 sub[1].fY = SkDoubleToScalar(dst[1].y);
241 sub[2].fX = SkDoubleToScalar(dst[2].x);
242 sub[2].fY = SkDoubleToScalar(dst[2].y);
243}
244
245static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
246 SkPoint sub[4]) {
247 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
248 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
249 Cubic dst;
250 sub_divide(aCubic, startT, endT, dst);
251 sub[0].fX = SkDoubleToScalar(dst[0].x);
252 sub[0].fY = SkDoubleToScalar(dst[0].y);
253 sub[1].fX = SkDoubleToScalar(dst[1].x);
254 sub[1].fY = SkDoubleToScalar(dst[1].y);
255 sub[2].fX = SkDoubleToScalar(dst[2].x);
256 sub[2].fY = SkDoubleToScalar(dst[2].y);
257 sub[3].fX = SkDoubleToScalar(dst[3].x);
258 sub[3].fY = SkDoubleToScalar(dst[3].y);
259}
260
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000261static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
262 SkPoint []) = {
263 NULL,
264 LineSubDivide,
265 QuadSubDivide,
266 CubicSubDivide
267};
268
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000269#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000270static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
271 SkRect& bounds) {
272 SkPoint dst[3];
273 QuadSubDivide(a, startT, endT, dst);
274 bounds.fLeft = bounds.fRight = dst[0].fX;
275 bounds.fTop = bounds.fBottom = dst[0].fY;
276 for (int index = 1; index < 3; ++index) {
277 bounds.growToInclude(dst[index].fX, dst[index].fY);
278 }
279}
280
281static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
282 SkRect& bounds) {
283 SkPoint dst[4];
284 CubicSubDivide(a, startT, endT, dst);
285 bounds.fLeft = bounds.fRight = dst[0].fX;
286 bounds.fTop = bounds.fBottom = dst[0].fY;
287 for (int index = 1; index < 4; ++index) {
288 bounds.growToInclude(dst[index].fX, dst[index].fY);
289 }
290}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000291#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000292
caryclark@google.com15fa1382012-05-07 20:49:36 +0000293static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000294 SkTDArray<SkPoint>& reducePts) {
295 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
296 {a[2].fX, a[2].fY}};
297 Quadratic dst;
298 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000299 if (order == 3) {
300 return SkPath::kQuad_Verb;
301 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000302 for (int index = 0; index < order; ++index) {
303 SkPoint* pt = reducePts.append();
304 pt->fX = SkDoubleToScalar(dst[index].x);
305 pt->fY = SkDoubleToScalar(dst[index].y);
306 }
307 return (SkPath::Verb) (order - 1);
308}
309
310static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
311 SkTDArray<SkPoint>& reducePts) {
312 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
313 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
314 Cubic dst;
315 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000316 if (order == 4) {
317 return SkPath::kCubic_Verb;
318 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000319 for (int index = 0; index < order; ++index) {
320 SkPoint* pt = reducePts.append();
321 pt->fX = SkDoubleToScalar(dst[index].x);
322 pt->fY = SkDoubleToScalar(dst[index].y);
323 }
324 return (SkPath::Verb) (order - 1);
325}
326
caryclark@google.com15fa1382012-05-07 20:49:36 +0000327static bool QuadIsLinear(const SkPoint a[3]) {
328 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
329 {a[2].fX, a[2].fY}};
330 return isLinear(aQuad, 0, 2);
331}
332
333static bool CubicIsLinear(const SkPoint a[4]) {
334 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
335 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
336 return isLinear(aCubic, 0, 3);
337}
338
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000339static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
340 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
341 double x[2];
342 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000343 xy_at_t(aLine, endT, x[1], *(double*) 0);
344 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000345}
346
347static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
348 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
349 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000350 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000351}
352
353static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
354 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
355 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000356 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000357}
358
359static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
360 NULL,
361 LineLeftMost,
362 QuadLeftMost,
363 CubicLeftMost
364};
365
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000366#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000367static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
368 const SkPoint& below) {
369 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
370 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
371 return implicit_matches_ulps(aLine, bLine, 32);
372}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000373#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000374
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000375class Segment;
376
caryclark@google.com15fa1382012-05-07 20:49:36 +0000377// sorting angles
378// given angles of {dx dy ddx ddy dddx dddy} sort them
379class Angle {
380public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000381 // FIXME: this is bogus for quads and cubics
382 // if the quads and cubics' line from end pt to ctrl pt are coincident,
383 // there's no obvious way to determine the curve ordering from the
384 // derivatives alone. In particular, if one quadratic's coincident tangent
385 // is longer than the other curve, the final control point can place the
386 // longer curve on either side of the shorter one.
387 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
388 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000389 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000390 if ((fDy < 0) ^ (rh.fDy < 0)) {
391 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000392 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000393 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
394 return fDx < rh.fDx;
395 }
396 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000397 if (cmp) {
398 return cmp < 0;
399 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000400 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
401 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000402 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000403 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
404 return fDDx < rh.fDDx;
405 }
406 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000407 if (cmp) {
408 return cmp < 0;
409 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000410 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
411 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000412 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000413 if (fDDDy == 0 && rh.fDDDy == 0) {
414 return fDDDx < rh.fDDDx;
415 }
416 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000417 }
418
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419 int end() const {
420 return fEnd;
421 }
422
423 // since all angles share a point, this needs to know which point
424 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
425 // practically, this should only be called by addAngle
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000426 void set(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000427 int start, int end, bool coincident) {
428 SkASSERT(start != end);
429 fSegment = segment;
430 fStart = start;
431 fEnd = end;
432 fCoincident = coincident;
433 fDx = pts[1].fX - pts[0].fX; // b - a
434 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000435 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000436 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000437 return;
438 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000439 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
440 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000441 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000442 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000443 return;
444 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000445 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
446 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
447 }
448
449 // noncoincident quads/cubics may have the same initial angle
450 // as lines, so must sort by derivatives as well
451 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000452 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000453 int start, int end, bool coincident) {
454 fSegment = segment;
455 fStart = start;
456 fEnd = end;
457 fCoincident = coincident;
458 fDx = pts[1].fX - pts[0].fX; // b - a
459 fDy = pts[1].fY - pts[0].fY;
460 if (verb == SkPath::kLine_Verb) {
461 fDDx = fDDy = fDDDx = fDDDy = 0;
462 return;
463 }
464 if (verb == SkPath::kQuad_Verb) {
465 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
466 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
467 int larger = std::max(abs(uplsX), abs(uplsY));
468 int shift = 0;
469 double flatT;
470 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
471 LineParameters implicitLine;
472 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
473 implicitLine.lineEndPoints(tangent);
474 implicitLine.normalize();
475 while (larger > UlpsEpsilon * 1024) {
476 larger >>= 2;
477 ++shift;
478 flatT = 0.5 / (1 << shift);
479 QuadXYAtT(pts, flatT, &ddPt);
480 _Point _pt = {ddPt.fX, ddPt.fY};
481 double distance = implicitLine.pointDistance(_pt);
482 if (approximately_zero(distance)) {
483 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
484 break;
485 }
486 }
487 flatT = 0.5 / (1 << shift);
488 QuadXYAtT(pts, flatT, &ddPt);
489 fDDx = ddPt.fX - pts[0].fX;
490 fDDy = ddPt.fY - pts[0].fY;
491 SkASSERT(fDDx != 0 || fDDy != 0);
492 fDDDx = fDDDy = 0;
493 return;
494 }
495 SkASSERT(0); // FIXME: add cubic case
496 }
497
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000498 Segment* segment() const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000499 return fSegment;
500 }
501
502 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000503 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000504 }
505
506 int start() const {
507 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000508 }
509
510private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000511 SkScalar fDx;
512 SkScalar fDy;
513 SkScalar fDDx;
514 SkScalar fDDy;
515 SkScalar fDDDx;
516 SkScalar fDDDy;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000517 Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000518 int fStart;
519 int fEnd;
520 bool fCoincident;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000521};
522
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000523static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
524 int angleCount = angles.count();
525 int angleIndex;
526 angleList.setReserve(angleCount);
527 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
528 *angleList.append() = &angles[angleIndex];
529 }
530 QSort<Angle>(angleList.begin(), angleList.end() - 1);
531}
532
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000533// Bounds, unlike Rect, does not consider a vertical line to be empty.
534struct Bounds : public SkRect {
535 static bool Intersects(const Bounds& a, const Bounds& b) {
536 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
537 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
538 }
539
540 bool isEmpty() {
541 return fLeft > fRight || fTop > fBottom
542 || fLeft == fRight && fTop == fBottom
543 || isnan(fLeft) || isnan(fRight)
544 || isnan(fTop) || isnan(fBottom);
545 }
546
547 void setCubicBounds(const SkPoint a[4]) {
548 _Rect dRect;
549 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
550 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
551 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000552 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
553 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000554 }
555
556 void setQuadBounds(const SkPoint a[3]) {
557 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
558 {a[2].fX, a[2].fY}};
559 _Rect dRect;
560 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000561 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
562 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000563 }
564};
565
caryclark@google.com15fa1382012-05-07 20:49:36 +0000566struct Span {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000567 double fT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000568 Segment* fOther;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000569 double fOtherT; // value at fOther[fOtherIndex].fT
570 int fOtherIndex; // can't be used during intersection
caryclark@google.com15fa1382012-05-07 20:49:36 +0000571 int fWinding; // accumulated from contours surrounding this one
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000572 // OPTIMIZATION: coincident needs only 2 bits (values are -1, 0, 1)
caryclark@google.com15fa1382012-05-07 20:49:36 +0000573 int fCoincident; // -1 start of coincidence, 0 no coincidence, 1 end
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000574 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000575};
576
577class Segment {
578public:
579 Segment() {
580#if DEBUG_DUMP
581 fID = ++gSegmentID;
582#endif
583 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000584
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000585 void addAngle(SkTDArray<Angle>& angles, int start, int end,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000586 bool coincident) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000587 SkASSERT(start != end);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000588 int smaller = SkMin32(start, end);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000589 if (fTs[smaller].fDone) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000590 return;
591 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000592 SkPoint edge[4];
593 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
594 Angle* angle = angles.append();
595 angle->set(edge, fVerb, this, start, end, coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000596 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000597
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000598 void addCubic(const SkPoint pts[4]) {
599 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000600 fBounds.setCubicBounds(pts);
601 }
602
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000603 void addCurveTo(int start, int end, SkPath& path) {
604 SkPoint edge[4];
605 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000606 #if DEBUG_PATH_CONSTRUCTION
607 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
608 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
609 if (fVerb > 1) {
610 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
611 }
612 if (fVerb > 2) {
613 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
614 }
615 SkDebugf("\n");
616 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000617 switch (fVerb) {
618 case SkPath::kLine_Verb:
619 path.lineTo(edge[1].fX, edge[1].fY);
620 break;
621 case SkPath::kQuad_Verb:
622 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
623 break;
624 case SkPath::kCubic_Verb:
625 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
626 edge[3].fX, edge[3].fY);
627 break;
628 }
629 }
630
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000631 void addLine(const SkPoint pts[2]) {
632 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000633 fBounds.set(pts, 2);
634 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000635
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000636 void addMoveTo(int tIndex, SkPath& path) {
637 SkPoint pt;
638 double firstT = t(tIndex);
639 xyAtT(firstT, &pt);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000640 #if DEBUG_PATH_CONSTRUCTION
641 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
642 #endif
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000643 path.moveTo(pt.fX, pt.fY);
644 }
645
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000646 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000647 void addOtherT(int index, double otherT, int otherIndex) {
648 Span& span = fTs[index];
649 span.fOtherT = otherT;
650 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000651 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000652
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000653 void addQuad(const SkPoint pts[3]) {
654 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000655 fBounds.setQuadBounds(pts);
656 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000657
caryclark@google.com15fa1382012-05-07 20:49:36 +0000658 int addT(double newT, Segment& other, int coincident) {
659 // FIXME: in the pathological case where there is a ton of intercepts,
660 // binary search?
661 int insertedAt = -1;
662 Span* span;
663 size_t tCount = fTs.count();
caryclark@google.com15fa1382012-05-07 20:49:36 +0000664 for (size_t idx2 = 0; idx2 < tCount; ++idx2) {
665 // OPTIMIZATION: if there are three or more identical Ts, then
666 // the fourth and following could be further insertion-sorted so
667 // that all the edges are clockwise or counterclockwise.
668 // This could later limit segment tests to the two adjacent
669 // neighbors, although it doesn't help with determining which
670 // circular direction to go in.
671 if (newT <= fTs[idx2].fT) {
672 insertedAt = idx2;
673 span = fTs.insert(idx2);
674 goto finish;
675 }
676 }
677 insertedAt = tCount;
678 span = fTs.append();
679finish:
680 span->fT = newT;
681 span->fOther = &other;
caryclark@google.com495f8e42012-05-31 13:13:11 +0000682 span->fWinding = 0;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000683 if (span->fDone = newT == 1) {
684 ++fDoneSpans;
685 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000686 span->fCoincident = coincident;
687 fCoincident |= coincident;
688 return insertedAt;
689 }
690
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000691 void addTwoAngles(int start, int end, const SkPoint& endLoc,
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000692 const Span* endSpan, bool startCo, SkTDArray<Angle>& angles) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000693 // add edge leading into junction
694 addAngle(angles, end, start, startCo);
695 // add edge leading away from junction
696 bool coincident;
caryclark@google.com495f8e42012-05-31 13:13:11 +0000697 int step = SkSign32(end - start);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000698 int tIndex = nextSpan(end, step, endLoc, endSpan, NULL, coincident);
699 if (tIndex >= 0) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000700 lastSpan(tIndex, step, endLoc, endSpan->fT, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000701 addAngle(angles, end, tIndex, coincident);
702 }
703 }
704
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000705 const Bounds& bounds() const {
706 return fBounds;
707 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000708
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000709 void buildAngles(int index, int last, int step, const SkPoint& loc,
710 SkTDArray<Angle>& angles) const {
711 SkASSERT(index - last != 0);
712 SkASSERT((index - last < 0) ^ (step < 0));
713 int end = last + step;
714 do {
715 Span* span = &fTs[index];
716 Segment* other = span->fOther;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000717 if (other->done()) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000718 continue;
719 }
720 // if there is only one live crossing, and no coincidence, continue
721 // in the same direction
722 // if there is coincidence, the only choice may be to reverse direction
723 // find edge on either side of intersection
724 int oIndex = span->fOtherIndex;
725 Span* otherSpan = &other->fTs[oIndex];
726 SkASSERT(otherSpan->fOther == this);
727 // if done == -1, prior span has already been processed
728 bool otherCo;
729 int localStep = step;
730 int next = other->nextSpan(oIndex, localStep, loc, otherSpan,
731 NULL, otherCo);
732 if (next < 0) {
733 localStep = -step;
734 next = other->nextSpan(oIndex, localStep, loc, otherSpan,
735 NULL, otherCo);
736 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000737 other->lastSpan(next, localStep, loc, otherSpan->fT, otherCo);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000738 // add candidate into and away from junction
739 other->addTwoAngles(next, oIndex, loc, span, otherCo, angles);
740
741 } while ((index += step) != end);
742 }
743
744 // figure out if the segment's ascending T goes clockwise or not
745 // not enough context to write this as shown
746 // instead, add all segments meeting at the top
747 // sort them using buildAngleList
748 // find the first in the sort
749 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000750 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000751 SkASSERT(0); // incomplete
752 return false;
753 }
754
caryclark@google.com495f8e42012-05-31 13:13:11 +0000755 bool coincident(int index, const Angle* angle) const {
756 Span* span;
757 double referenceT = fTs[index].fT;
758 int lesser = index;
759 while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
760 span = &fTs[lesser];
761 if (span->fOther == angle->segment()) {
762 goto checkOther;
763 }
764 }
765 do {
766 span = &fTs[index];
767 if (span->fOther == angle->segment()) {
768 break;
769 }
770
771 } while (++index < fTs.count() && referenceT == fTs[index].fT);
772 checkOther:
773 SkASSERT(!span->fDone);
774 return span->fCoincident;
775 }
776
caryclark@google.com15fa1382012-05-07 20:49:36 +0000777 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000778 SkASSERT(fDoneSpans <= fTs.count());
779 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000780 }
781
caryclark@google.com15fa1382012-05-07 20:49:36 +0000782 int findCoincidentEnd(int start) const {
783 int tCount = fTs.count();
784 SkASSERT(start < tCount);
785 const Span& span = fTs[start];
786 SkASSERT(span.fCoincident);
787 for (int index = start + 1; index < tCount; ++index) {
788 const Span& match = fTs[index];
789 if (match.fOther == span.fOther) {
790 SkASSERT(match.fCoincident);
791 return index;
792 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000793 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000794 SkASSERT(0); // should never get here
795 return -1;
796 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000797
caryclark@google.com15fa1382012-05-07 20:49:36 +0000798 // start is the index of the beginning T of this edge
799 // it is guaranteed to have an end which describes a non-zero length (?)
800 // winding -1 means ccw, 1 means cw
801 // step is in/out -1 or 1
802 // spanIndex is returned
caryclark@google.com495f8e42012-05-31 13:13:11 +0000803 Segment* findNext(int winding, const int startIndex, const int endIndex,
804 int& nextStart, int& nextEnd) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000805 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000806 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000807 SkASSERT(startIndex < endIndex ? startIndex < count - 1
808 : startIndex > 0);
809 Span* startSpan = &fTs[startIndex];
caryclark@google.com15fa1382012-05-07 20:49:36 +0000810 // FIXME:
811 // since Ts can be stepped either way, done markers must be careful
812 // not to assume that segment was only ascending in T. This shouldn't
813 // be a problem unless pathologically a segment can be partially
814 // ascending and partially descending -- maybe quads/cubic can do this?
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000815
816
caryclark@google.com495f8e42012-05-31 13:13:11 +0000817 int step = SkSign32(endIndex - startIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000818 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
819 xyAtT(startSpan->fT, &startLoc);
820 SkPoint endLoc;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000821 bool startCo;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000822 int end = nextSpan(startIndex, step, startLoc, startSpan, &endLoc,
823 startCo);
824 SkASSERT(end >= 0);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000825
826 // preflight for coincidence -- if present, it may change winding
827 // considerations and whether reversed edges can be followed
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000828 bool many;
829 int last = lastSpan(end, step, endLoc, fTs[end].fT, startCo, &many);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000830
831 // Discard opposing direction candidates if no coincidence was found.
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000832 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000833 Segment* other;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000834 if (!many) {
835 // mark the smaller of startIndex, endIndex done, and all adjacent
836 // spans with the same T value (but not 'other' spans)
caryclark@google.com495f8e42012-05-31 13:13:11 +0000837 markDone(SkMin32(startIndex, endIndex), winding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000838 SkASSERT(!startCo);
caryclark@google.com15fa1382012-05-07 20:49:36 +0000839 // move in winding direction until edge in correct direction
840 // balance wrong direction edges before finding correct one
841 // this requres that the intersection is angularly sorted
842 // for a single intersection, special case -- choose the opposite
843 // edge that steps the same
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000844 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +0000845 nextStart = endSpan->fOtherIndex;
846 nextEnd = nextStart + step;
847 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +0000848 return other;
849 }
850
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000851 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +0000852 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000853 SkASSERT(startIndex - endIndex != 0);
854 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000855 addTwoAngles(startIndex, end, endLoc, endSpan, startCo, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000856 buildAngles(end, last, step, endLoc, angles);
857 SkTDArray<Angle*> sorted;
858 sortAngles(angles, sorted);
859 // find the starting edge
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000860 int firstIndex = -1;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000861 int angleCount = angles.count();
862 int angleIndex;
863 const Angle* angle;
864 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
865 angle = sorted[angleIndex];
866 if (angle->segment() == this && angle->start() == end &&
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000867 angle->end() == startIndex) {
868 firstIndex = angleIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000869 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000870 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000871 }
caryclark@google.com495f8e42012-05-31 13:13:11 +0000872
873 // put some thought into handling coincident edges
874 // 1) defer the initial moveTo/curveTo until we know that firstIndex
875 // isn't coincident (although maybe findTop could tell us that)
876 // 2) allow the below to mark and skip coincident pairs
877 // 3) return something (null?) if all segments cancel each other out
878 // 4) if coincident edges don't cancel, figure out which to return (follow)
879
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000880 SkASSERT(firstIndex >= 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000881 int startWinding = winding;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000882 int nextIndex = firstIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000883 const Angle* nextAngle;
884 do {
885 if (++nextIndex == angleCount) {
886 nextIndex = 0;
887 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000888 SkASSERT(nextIndex != firstIndex); // should never wrap around
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000889 nextAngle = sorted[nextIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +0000890 int maxWinding = winding;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000891 winding -= nextAngle->sign();
caryclark@google.com495f8e42012-05-31 13:13:11 +0000892 if (abs(maxWinding) < abs(winding)) {
893 maxWinding = winding;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000894 }
caryclark@google.com495f8e42012-05-31 13:13:11 +0000895 other = nextAngle->segment();
896 if (!winding) {
897 if (!startCo || !coincident(startIndex, nextAngle)) {
898 break;
899 }
900 markAndChaseCoincident(startIndex, endIndex, other);
901 return NULL;
902 }
903 // if the winding is non-zero, nextAngle does not connect to
904 // current chain. If we haven't done so already, mark the angle
905 // as done, record the winding value, and mark connected unambiguous
906 // segments as well.
907 if (other->winding(nextAngle) == 0) {
908 other->markAndChaseWinding(nextAngle, maxWinding);
909 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000910
911 } while (true);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000912 markDone(SkMin32(startIndex, endIndex), startWinding);
913 nextStart = nextAngle->start();
914 nextEnd = nextAngle->end();
caryclark@google.comaf46cff2012-05-22 21:12:00 +0000915 return other;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000916 }
917
918
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000919 // so the span needs to contain the pairing info found here
920 // this should include the winding computed for the edge, and
921 // what edge it connects to, and whether it is discarded
922 // (maybe discarded == abs(winding) > 1) ?
923 // only need derivatives for duration of sorting, add a new struct
924 // for pairings, remove extra spans that have zero length and
925 // reference an unused other
926 // for coincident, the last span on the other may be marked done
927 // (always?)
928
caryclark@google.com15fa1382012-05-07 20:49:36 +0000929 // if loop is exhausted, contour may be closed.
930 // FIXME: pass in close point so we can check for closure
931
932 // given a segment, and a sense of where 'inside' is, return the next
933 // segment. If this segment has an intersection, or ends in multiple
934 // segments, find the mate that continues the outside.
935 // note that if there are multiples, but no coincidence, we can limit
936 // choices to connections in the correct direction
937
938 // mark found segments as done
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000939
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000940 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000941 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +0000942 int count = fTs.count();
943 if (count < 3) { // require t=0, x, 1 at minimum
944 return;
945 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000946 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000947 int moCount;
948 Span* match;
949 Segment* mOther;
950 do {
951 match = &fTs[matchIndex];
952 mOther = match->fOther;
953 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000954 if (moCount >= 3) {
955 break;
956 }
957 if (++matchIndex >= count) {
958 return;
959 }
960 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +0000961 SkPoint matchPt;
962 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
963 xyAtT(match->fT, &matchPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000964 // look for a pair of nearby T values that map to the same (x,y) value
965 // if found, see if the pair of other segments share a common point. If
966 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000967 for (int index = matchIndex + 1; index < count; ++index) {
968 Span* test = &fTs[index];
caryclark@google.com495f8e42012-05-31 13:13:11 +0000969 if (test->fCoincident) {
970 continue;
971 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000972 Segment* tOther = test->fOther;
973 int toCount = tOther->fTs.count();
974 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000975 continue;
976 }
977 SkPoint testPt;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000978 xyAtT(test->fT, &testPt);
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000979 if (matchPt != testPt) {
980 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000981 moCount = toCount;
982 match = test;
983 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000984 matchPt = testPt;
985 continue;
986 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000987 int moStart = -1;
988 int moEnd = -1;
989 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000990 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000991 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +0000992 if (moSpan.fCoincident) {
993 continue;
994 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000995 if (moSpan.fOther == this) {
996 if (moSpan.fOtherT == match->fT) {
997 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000998 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000999 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001000 continue;
1001 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001002 if (moSpan.fOther == tOther) {
1003 SkASSERT(moEnd == -1);
1004 moEnd = moIndex;
1005 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001006 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001007 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001008 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001009 continue;
1010 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001011 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1012 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001013 continue;
1014 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001015 int toStart = -1;
1016 int toEnd = -1;
1017 double toStartT, toEndT;
1018 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1019 Span& toSpan = tOther->fTs[toIndex];
1020 if (toSpan.fOther == this) {
1021 if (toSpan.fOtherT == test->fT) {
1022 toStart = toIndex;
1023 toStartT = toSpan.fT;
1024 }
1025 continue;
1026 }
1027 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1028 SkASSERT(toEnd == -1);
1029 toEnd = toIndex;
1030 toEndT = toSpan.fT;
1031 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001032 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001033 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1034 if (toStart <= 0 || toEnd <= 0) {
1035 continue;
1036 }
1037 if (toStartT == toEndT) {
1038 continue;
1039 }
1040 // test to see if the segment between there and here is linear
1041 if (!mOther->isLinear(moStart, moEnd)
1042 || !tOther->isLinear(toStart, toEnd)) {
1043 continue;
1044 }
1045 mOther->fTs[moStart].fCoincident = -1;
1046 tOther->fTs[toStart].fCoincident = -1;
1047 mOther->fTs[moEnd].fCoincident = 1;
1048 tOther->fTs[toEnd].fCoincident = 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001049 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001050 }
1051
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001052 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1053 // and use more concise logic like the old edge walker code?
1054 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001055 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001056 // iterate through T intersections and return topmost
1057 // topmost tangent from y-min to first pt is closer to horizontal
1058 int firstT = 0;
1059 int lastT = 0;
1060 SkScalar topY = fPts[0].fY;
1061 int count = fTs.count();
1062 int index;
1063 for (index = 1; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001064 const Span& span = fTs[index];
1065 double t = span.fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001066 SkScalar yIntercept = t == 1 ? fPts[fVerb].fY : yAtT(t);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001067 if (topY > yIntercept) {
1068 topY = yIntercept;
1069 firstT = lastT = index;
1070 } else if (topY == yIntercept) {
1071 lastT = index;
1072 }
1073 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001074 // if there's only a pair of segments, go with the endpoint chosen above
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001075 if (firstT == lastT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001076 tIndex = firstT;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001077 endIndex = firstT > 0 ? tIndex - 1 : tIndex + 1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001078 return this;
1079 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001080 // sort the edges to find the leftmost
1081 SkPoint startLoc; // OPTIMIZATION: store this in the t span?
1082 const Span* startSpan = &fTs[firstT];
1083 xyAtT(startSpan->fT, &startLoc);
1084 SkPoint endLoc;
1085 bool nextCo;
1086 int end = nextSpan(firstT, 1, startLoc, startSpan, &endLoc, nextCo);
1087 if (end == -1) {
1088 end = nextSpan(firstT, -1, startLoc, startSpan, &endLoc, nextCo);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001089 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001090 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001091 // if the topmost T is not on end, or is three-way or more, find left
1092 // look for left-ness from tLeft to firstT (matching y of other)
1093 SkTDArray<Angle> angles;
1094 SkASSERT(firstT - end != 0);
1095 addTwoAngles(end, firstT, endLoc, &fTs[firstT], nextCo, angles);
1096 buildAngles(firstT, lastT, 1, startLoc, angles);
1097 SkTDArray<Angle*> sorted;
1098 sortAngles(angles, sorted);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001099 Segment* leftSegment = sorted[0]->segment();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001100 tIndex = sorted[0]->end();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001101 endIndex = sorted[0]->start();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001102 return leftSegment;
1103 }
1104
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001105 // FIXME: not crazy about this
1106 // when the intersections are performed, the other index is into an
1107 // incomplete array. as the array grows, the indices become incorrect
1108 // while the following fixes the indices up again, it isn't smart about
1109 // skipping segments whose indices are already correct
1110 // assuming we leave the code that wrote the index in the first place
1111 void fixOtherTIndex() {
1112 int iCount = fTs.count();
1113 for (int i = 0; i < iCount; ++i) {
1114 Span& iSpan = fTs[i];
1115 double oT = iSpan.fOtherT;
1116 Segment* other = iSpan.fOther;
1117 int oCount = other->fTs.count();
1118 for (int o = 0; o < oCount; ++o) {
1119 Span& oSpan = other->fTs[o];
1120 if (oT == oSpan.fT && this == oSpan.fOther) {
1121 iSpan.fOtherIndex = o;
1122 }
1123 }
1124 }
1125 }
1126
caryclark@google.com495f8e42012-05-31 13:13:11 +00001127 // OPTIMIZATION: uses tail recursion. Unwise?
1128 void innerCoincidentChase(int step, Segment* other) {
1129 // find other at index
1130 SkASSERT(!done());
1131 const Span* start = NULL;
1132 const Span* end = NULL;
1133 int index, startIndex, endIndex;
1134 int count = fTs.count();
1135 for (index = 0; index < count; ++index) {
1136 const Span& span = fTs[index];
1137 if (!span.fCoincident || span.fOther != other) {
1138 continue;
1139 }
1140 if (!start) {
1141 if (span.fDone) {
1142 continue;
1143 }
1144 startIndex = index;
1145 start = &span;
1146 } else {
1147 SkASSERT(!end);
1148 endIndex = index;
1149 end = &span;
1150 }
1151 }
1152 if (!end) {
1153 return;
1154 }
1155 Segment* next;
1156 Segment* nextOther;
1157 if (step < 0) {
1158 next = start->fT <= 0 ? NULL : this;
1159 nextOther = other->fTs[start->fOtherIndex].fT >= 1 ? NULL : other;
1160 } else {
1161 next = end->fT >= 1 ? NULL : this;
1162 nextOther = other->fTs[end->fOtherIndex].fT <= 0 ? NULL : other;
1163 }
1164 SkASSERT(!next || !nextOther);
1165 for (index = 0; index < count; ++index) {
1166 const Span& span = fTs[index];
1167 if (span.fCoincident || span.fOther == other) {
1168 continue;
1169 }
1170 bool checkNext = !next && (step < 0 ? span.fT <= 0
1171 && span.fOtherT >= 1 : span.fT >= 1 && span.fOtherT <= 0);
1172 bool checkOther = !nextOther && (step < 0 ? span.fT == start->fT
1173 && span.fOtherT <= 0 : span.fT == end->fT && span.fOtherT >= 1);
1174 if (!checkNext && !checkOther) {
1175 continue;
1176 }
1177 Segment* oSegment = span.fOther;
1178 if (oSegment->done()) {
1179 continue;
1180 }
1181 int oCount = oSegment->fTs.count();
1182 for (int oIndex = 0; oIndex < oCount; ++oIndex) {
1183 const Span& oSpan = oSegment->fTs[oIndex];
1184 if (oSpan.fT > 0 && oSpan.fT < 1) {
1185 continue;
1186 }
1187 if (!oSpan.fCoincident) {
1188 continue;
1189 }
1190 if (checkNext && (oSpan.fT <= 0 ^ step < 0)) {
1191 next = oSegment;
1192 checkNext = false;
1193 }
1194 if (checkOther && (oSpan.fT >= 1 ^ step < 0)) {
1195 nextOther = oSegment;
1196 checkOther = false;
1197 }
1198 }
1199 }
1200 markDone(SkMin32(startIndex, endIndex), 0);
1201 other->markDone(SkMin32(start->fOtherIndex, end->fOtherIndex), 0);
1202 if (next && nextOther) {
1203 next->innerCoincidentChase(step, nextOther);
1204 }
1205 }
1206
1207 // OPTIMIZATION: uses tail recursion. Unwise?
1208 void innerChase(int index, int step, int winding) {
1209 SkPoint loc; // OPTIMIZATION: store this in the t span?
1210 bool coincident;
1211 int end = nextSpan(index, step, &loc, coincident);
1212 bool many;
1213 lastSpan(end, step, loc, fTs[end].fT, coincident, &many);
1214 if (many) {
1215 return;
1216 }
1217 Span* endSpan = &fTs[end];
1218 Segment* other = endSpan->fOther;
1219 index = endSpan->fOtherIndex;
1220 int otherEnd = other->nextSpan(index, step, &loc, coincident);
1221 other->innerChase(index, step, winding);
1222 other->markDone(SkMin32(index, otherEnd), winding);
1223 }
1224
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001225 void init(const SkPoint pts[], SkPath::Verb verb) {
1226 fPts = pts;
1227 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001228 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001229 fCoincident = 0;
1230 }
1231
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001232 bool intersected() const {
1233 return fTs.count() > 0;
1234 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001235
1236 bool isLinear(int start, int end) const {
1237 if (fVerb == SkPath::kLine_Verb) {
1238 return true;
1239 }
1240 if (fVerb == SkPath::kQuad_Verb) {
1241 SkPoint qPart[3];
1242 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1243 return QuadIsLinear(qPart);
1244 } else {
1245 SkASSERT(fVerb == SkPath::kCubic_Verb);
1246 SkPoint cPart[4];
1247 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1248 return CubicIsLinear(cPart);
1249 }
1250 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001251
1252 bool isHorizontal() const {
1253 return fBounds.fTop == fBounds.fBottom;
1254 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001255
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001256 bool isVertical() const {
1257 return fBounds.fLeft == fBounds.fRight;
1258 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001259
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001260 // last does not check for done spans because done is only set for the start
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001261 int lastSpan(int end, int step, const SkPoint& startLoc,
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001262 double startT, bool& coincident, bool* manyPtr = NULL) const {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001263 int last = end;
1264 int count = fTs.count();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001265 SkPoint lastLoc;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001266 int found = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001267 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001268 end = last;
1269 if (fTs[end].fCoincident == -step) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001270 coincident = true;
1271 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001272 if (step > 0 ? ++last >= count : --last < 0) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001273 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001274 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001275 const Span& lastSpan = fTs[last];
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001276 if (lastSpan.fT == startT) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001277 ++found;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001278 continue;
1279 }
caryclark@google.comfcd4f3e2012-05-07 21:09:32 +00001280 xyAtT(lastSpan.fT, &lastLoc);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001281 if (startLoc != lastLoc) {
1282 break;
1283 }
1284 ++found;
1285 } while (true);
1286 if (manyPtr) {
1287 *manyPtr = found > 0;
1288 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001289 return end;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001290 }
1291
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001292 SkScalar leftMost(int start, int end) const {
1293 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1294 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001295
caryclark@google.com495f8e42012-05-31 13:13:11 +00001296 void markAndChaseCoincident(int index, int endIndex, Segment* other) {
1297 int step = SkSign32(endIndex - index);
1298 innerCoincidentChase(step, other);
1299 }
1300
1301 // this span is excluded by the winding rule -- chase the ends
1302 // as long as they are unambiguous to mark connections as done
1303 // and give them the same winding value
1304 void markAndChaseWinding(const Angle* angle, int winding) {
1305 int index = angle->start();
1306 int endIndex = angle->end();
1307 int step = SkSign32(endIndex - index);
1308 innerChase(index, step, winding);
1309 markDone(SkMin32(index, endIndex), winding);
1310 }
1311
1312 // FIXME: this should also mark spans with equal (x,y)
1313 void markDone(int index, int winding) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001314 SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001315 double referenceT = fTs[index].fT;
1316 int lesser = index;
1317 while (--lesser >= 0 && referenceT == fTs[lesser].fT) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001318 Span& span = fTs[lesser];
1319 SkASSERT(!span.fDone);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001320 fTs[lesser].fDone = true;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001321 SkASSERT(!span.fWinding || span.fWinding == winding);
1322 span.fWinding = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001323 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001324 }
1325 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001326 Span& span = fTs[index];
1327 SkASSERT(!span.fDone);
1328 span.fDone = true;
1329 SkASSERT(!span.fWinding || span.fWinding == winding);
1330 span.fWinding = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001331 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001332 } while (++index < fTs.count() && referenceT == fTs[index].fT);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001333 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001334
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001335 // note the assert logic looks for unexpected done of span start
caryclark@google.com495f8e42012-05-31 13:13:11 +00001336 // FIXME: compute fromLoc on the fly
caryclark@google.com15fa1382012-05-07 20:49:36 +00001337 int nextSpan(int from, int step, const SkPoint& fromLoc,
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001338 const Span* fromSpan, SkPoint* toLoc, bool& coincident) const {
1339 coincident = false;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001340 SkASSERT(!done());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001341 int count = fTs.count();
1342 int to = from;
1343 while (step > 0 ? ++to < count : --to >= 0) {
1344 Span* span = &fTs[to];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001345 if (span->fCoincident == step) {
1346 coincident = true;
1347 }
1348 if (fromSpan->fT == span->fT) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001349 continue;
1350 }
1351 SkPoint loc;
1352 xyAtT(span->fT, &loc);
1353 if (fromLoc == loc) {
1354 continue;
1355 }
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001356 SkASSERT(step < 0 || !fTs[from].fDone);
1357 SkASSERT(step > 0 || !span->fDone);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001358 if (toLoc) {
1359 *toLoc = loc;
1360 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001361 return to;
1362 }
1363 return -1;
1364 }
1365
caryclark@google.com495f8e42012-05-31 13:13:11 +00001366 int nextSpan(int from, int step, SkPoint* toLoc, bool& coincident) const {
1367 const Span& fromSpan = fTs[from];
1368 coincident = false;
1369 SkASSERT(!done());
1370 int count = fTs.count();
1371 int to = from;
1372 SkPoint fromLoc;
1373 fromLoc.fX = SK_ScalarNaN;
1374 while (step > 0 ? ++to < count : --to >= 0) {
1375 const Span& span = fTs[to];
1376 if (span.fCoincident == step) {
1377 coincident = true;
1378 }
1379 if (fromSpan.fT == span.fT) {
1380 continue;
1381 }
1382 SkPoint loc;
1383 xyAtT(span.fT, &loc);
1384 if (SkScalarIsNaN(fromLoc.fX)) {
1385 xyAtT(fromSpan.fT, &fromLoc);
1386 }
1387 if (fromLoc == loc) {
1388 continue;
1389 }
1390 SkASSERT(step < 0 || !fromSpan.fDone);
1391 SkASSERT(step > 0 || !span.fDone);
1392 if (toLoc) {
1393 *toLoc = loc;
1394 }
1395 return to;
1396 }
1397 return -1;
1398 }
1399
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001400 const SkPoint* pts() const {
1401 return fPts;
1402 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001403
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001404 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001405 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001406 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1407 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001408 }
1409
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001410 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001411 double t(int tIndex) const {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001412 SkASSERT(tIndex >= 0);
1413 SkASSERT(tIndex < fTs.count());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001414 return fTs[tIndex].fT;
1415 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001416
1417 void updatePts(const SkPoint pts[]) {
1418 fPts = pts;
1419 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001420
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001421 SkPath::Verb verb() const {
1422 return fVerb;
1423 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001424
1425 bool winding(const Angle* angle) {
1426 int start = angle->start();
1427 int end = angle->end();
1428 int index = SkMin32(start, end);
1429 Span& span = fTs[index];
1430 return span.fWinding;
1431 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001432
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001433 SkScalar xAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001434 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001435 return (*SegmentXAtT[fVerb])(fPts, t);
1436 }
1437
1438 void xyAtT(double t, SkPoint* pt) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001439 SkASSERT(t >= 0 && t <= 1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001440 (*SegmentXYAtT[fVerb])(fPts, t, pt);
1441 }
1442
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001443 SkScalar yAtT(double t) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001444 SkASSERT(t >= 0 && t <= 1);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001445 return (*SegmentYAtT[fVerb])(fPts, t);
1446 }
1447
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001448#if DEBUG_DUMP
1449 void dump() const {
1450 const char className[] = "Segment";
1451 const int tab = 4;
1452 for (int i = 0; i < fTs.count(); ++i) {
1453 SkPoint out;
1454 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1455 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001456 " otherT=%1.9g winding=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001457 tab + sizeof(className), className, fID,
1458 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001459 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWinding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001460 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001461 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001462 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001463 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001464 }
1465#endif
1466
1467private:
1468 const SkPoint* fPts;
1469 SkPath::Verb fVerb;
1470 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001471 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
1472 // FIXME: coincident only needs two bits (-1, 0, 1)
1473 int fCoincident; // non-zero if some coincident span inside
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001474 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001475#if DEBUG_DUMP
1476 int fID;
1477#endif
1478};
1479
1480class Contour {
1481public:
1482 Contour() {
1483 reset();
1484#if DEBUG_DUMP
1485 fID = ++gContourID;
1486#endif
1487 }
1488
1489 bool operator<(const Contour& rh) const {
1490 return fBounds.fTop == rh.fBounds.fTop
1491 ? fBounds.fLeft < rh.fBounds.fLeft
1492 : fBounds.fTop < rh.fBounds.fTop;
1493 }
1494
1495 void addCubic(const SkPoint pts[4]) {
1496 fSegments.push_back().addCubic(pts);
1497 fContainsCurves = true;
1498 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001499
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001500 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001501 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001502 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001503 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001504
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001505 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001506 fSegments.push_back().addQuad(pts);
1507 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001508 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001509 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001510
1511 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001512 return fBounds;
1513 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001514
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001515 void complete() {
1516 setBounds();
1517 fContainsIntercepts = false;
1518 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001519
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001520 void containsIntercepts() {
1521 fContainsIntercepts = true;
1522 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001523
1524 void findTooCloseToCall(int winding) {
1525 int segmentCount = fSegments.count();
1526 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1527 fSegments[sIndex].findTooCloseToCall(winding);
1528 }
1529 }
1530
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001531 void fixOtherTIndex() {
1532 int segmentCount = fSegments.count();
1533 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1534 fSegments[sIndex].fixOtherTIndex();
1535 }
1536 }
1537
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001538 void reset() {
1539 fSegments.reset();
1540 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001541 fContainsCurves = fContainsIntercepts = false;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001542 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001543
1544 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
1545 // we need to sort and walk edges in y, but that on the surface opens the
1546 // same can of worms as before. But then, this is a rough sort based on
1547 // segments' top, and not a true sort, so it could be ameniable to regular
1548 // sorting instead of linear searching. Still feel like I'm missing something
1549 Segment* topSegment() {
1550 int segmentCount = fSegments.count();
1551 SkASSERT(segmentCount > 0);
1552 int best = -1;
1553 Segment* bestSegment = NULL;
1554 while (++best < segmentCount) {
1555 Segment* testSegment = &fSegments[best];
1556 if (testSegment->done()) {
1557 continue;
1558 }
1559 bestSegment = testSegment;
1560 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001561 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001562 if (!bestSegment) {
1563 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001564 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001565 SkScalar bestTop = bestSegment->bounds().fTop;
1566 for (int test = best + 1; test < segmentCount; ++test) {
1567 Segment* testSegment = &fSegments[test];
1568 if (testSegment->done()) {
1569 continue;
1570 }
1571 SkScalar testTop = testSegment->bounds().fTop;
1572 if (bestTop > testTop) {
1573 bestTop = testTop;
1574 bestSegment = testSegment;
1575 }
1576 }
1577 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001578 }
1579
1580#if DEBUG_DUMP
1581 void dump() {
1582 int i;
1583 const char className[] = "Contour";
1584 const int tab = 4;
1585 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
1586 for (i = 0; i < fSegments.count(); ++i) {
1587 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
1588 className, i);
1589 fSegments[i].dump();
1590 }
1591 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
1592 tab + sizeof(className), className,
1593 fBounds.fLeft, fBounds.fTop,
1594 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001595 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
1596 className, fContainsIntercepts);
1597 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
1598 className, fContainsCurves);
1599 }
1600#endif
1601
1602protected:
1603 void setBounds() {
1604 int count = fSegments.count();
1605 if (count == 0) {
1606 SkDebugf("%s empty contour\n", __FUNCTION__);
1607 SkASSERT(0);
1608 // FIXME: delete empty contour?
1609 return;
1610 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001611 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001612 for (int index = 1; index < count; ++index) {
1613 fBounds.growToInclude(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001614 }
1615 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001616
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001617public:
1618 SkTArray<Segment> fSegments; // not worth accessor functions?
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001619
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001620private:
1621 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001622 bool fContainsIntercepts;
1623 bool fContainsCurves;
1624#if DEBUG_DUMP
1625 int fID;
1626#endif
1627};
1628
1629class EdgeBuilder {
1630public:
1631
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001632EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001633 : fPath(path)
1634 , fCurrentContour(NULL)
1635 , fContours(contours)
1636{
1637#if DEBUG_DUMP
1638 gContourID = 0;
1639 gSegmentID = 0;
1640#endif
1641 walk();
1642}
1643
1644protected:
1645
1646void complete() {
1647 if (fCurrentContour && fCurrentContour->fSegments.count()) {
1648 fCurrentContour->complete();
1649 fCurrentContour = NULL;
1650 }
1651}
1652
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001653void walk() {
1654 // FIXME:remove once we can access path pts directly
1655 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
1656 SkPoint pts[4];
1657 SkPath::Verb verb;
1658 do {
1659 verb = iter.next(pts);
1660 *fPathVerbs.append() = verb;
1661 if (verb == SkPath::kMove_Verb) {
1662 *fPathPts.append() = pts[0];
1663 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
1664 fPathPts.append(verb, &pts[1]);
1665 }
1666 } while (verb != SkPath::kDone_Verb);
1667 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001668
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001669 SkPath::Verb reducedVerb;
1670 uint8_t* verbPtr = fPathVerbs.begin();
1671 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001672 const SkPoint* finalCurveStart = NULL;
1673 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001674 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
1675 switch (verb) {
1676 case SkPath::kMove_Verb:
1677 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001678 if (!fCurrentContour) {
1679 fCurrentContour = fContours.push_back_n(1);
1680 finalCurveEnd = pointsPtr++;
1681 *fExtra.append() = -1; // start new contour
1682 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001683 continue;
1684 case SkPath::kLine_Verb:
1685 // skip degenerate points
1686 if (pointsPtr[-1].fX != pointsPtr[0].fX
1687 || pointsPtr[-1].fY != pointsPtr[0].fY) {
1688 fCurrentContour->addLine(&pointsPtr[-1]);
1689 }
1690 break;
1691 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001692
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001693 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
1694 if (reducedVerb == 0) {
1695 break; // skip degenerate points
1696 }
1697 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001698 *fExtra.append() =
1699 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001700 break;
1701 }
1702 fCurrentContour->addQuad(&pointsPtr[-1]);
1703 break;
1704 case SkPath::kCubic_Verb:
1705 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
1706 if (reducedVerb == 0) {
1707 break; // skip degenerate points
1708 }
1709 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001710 *fExtra.append() =
1711 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001712 break;
1713 }
1714 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001715 *fExtra.append() =
1716 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001717 break;
1718 }
1719 fCurrentContour->addCubic(&pointsPtr[-1]);
1720 break;
1721 case SkPath::kClose_Verb:
1722 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001723 if (finalCurveStart && finalCurveEnd
1724 && *finalCurveStart != *finalCurveEnd) {
1725 *fReducePts.append() = *finalCurveStart;
1726 *fReducePts.append() = *finalCurveEnd;
1727 *fExtra.append() =
1728 fCurrentContour->addLine(fReducePts.end() - 2);
1729 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001730 complete();
1731 continue;
1732 default:
1733 SkDEBUGFAIL("bad verb");
1734 return;
1735 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001736 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001737 pointsPtr += verb;
1738 SkASSERT(fCurrentContour);
1739 }
1740 complete();
1741 if (fCurrentContour && !fCurrentContour->fSegments.count()) {
1742 fContours.pop_back();
1743 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001744 // correct pointers in contours since fReducePts may have moved as it grew
1745 int cIndex = 0;
1746 fCurrentContour = &fContours[0];
1747 int extraCount = fExtra.count();
1748 SkASSERT(fExtra[0] == -1);
1749 int eIndex = 0;
1750 int rIndex = 0;
1751 while (++eIndex < extraCount) {
1752 int offset = fExtra[eIndex];
1753 if (offset < 0) {
1754 fCurrentContour = &fContours[++cIndex];
1755 continue;
1756 }
1757 Segment& segment = fCurrentContour->fSegments[offset - 1];
1758 segment.updatePts(&fReducePts[rIndex]);
1759 rIndex += segment.verb() + 1;
1760 }
1761 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001762}
1763
1764private:
1765 const SkPath& fPath;
1766 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001767 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001768 Contour* fCurrentContour;
1769 SkTArray<Contour>& fContours;
1770 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001771 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001772};
1773
1774class Work {
1775public:
1776 enum SegmentType {
1777 kHorizontalLine_Segment = -1,
1778 kVerticalLine_Segment = 0,
1779 kLine_Segment = SkPath::kLine_Verb,
1780 kQuad_Segment = SkPath::kQuad_Verb,
1781 kCubic_Segment = SkPath::kCubic_Verb,
1782 };
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001783
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001784 // FIXME: does it make sense to write otherIndex now if we're going to
1785 // fix it up later?
1786 void addOtherT(int index, double otherT, int otherIndex) {
1787 fContour->fSegments[fIndex].addOtherT(index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001788 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001789
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001790 // Avoid collapsing t values that are close to the same since
1791 // we walk ts to describe consecutive intersections. Since a pair of ts can
1792 // be nearly equal, any problems caused by this should be taken care
1793 // of later.
1794 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com15fa1382012-05-07 20:49:36 +00001795 int addT(double newT, const Work& other, int coincident) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001796 fContour->containsIntercepts();
caryclark@google.com15fa1382012-05-07 20:49:36 +00001797 return fContour->fSegments[fIndex].addT(newT,
1798 other.fContour->fSegments[other.fIndex], coincident);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001799 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001800
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001801 bool advance() {
1802 return ++fIndex < fLast;
1803 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001804
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001805 SkScalar bottom() const {
1806 return bounds().fBottom;
1807 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001808
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001809 const Bounds& bounds() const {
1810 return fContour->fSegments[fIndex].bounds();
1811 }
1812
1813 const SkPoint* cubic() const {
1814 return fCubic;
1815 }
1816
1817 void init(Contour* contour) {
1818 fContour = contour;
1819 fIndex = 0;
1820 fLast = contour->fSegments.count();
1821 }
1822
1823 SkScalar left() const {
1824 return bounds().fLeft;
1825 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001826
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001827 void promoteToCubic() {
1828 fCubic[0] = pts()[0];
1829 fCubic[2] = pts()[1];
1830 fCubic[3] = pts()[2];
1831 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
1832 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
1833 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
1834 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
1835 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001836
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001837 const SkPoint* pts() const {
1838 return fContour->fSegments[fIndex].pts();
1839 }
1840
1841 SkScalar right() const {
1842 return bounds().fRight;
1843 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001844
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001845 ptrdiff_t segmentIndex() const {
1846 return fIndex;
1847 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001848
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001849 SegmentType segmentType() const {
1850 const Segment& segment = fContour->fSegments[fIndex];
1851 SegmentType type = (SegmentType) segment.verb();
1852 if (type != kLine_Segment) {
1853 return type;
1854 }
1855 if (segment.isHorizontal()) {
1856 return kHorizontalLine_Segment;
1857 }
1858 if (segment.isVertical()) {
1859 return kVerticalLine_Segment;
1860 }
1861 return kLine_Segment;
1862 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001863
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001864 bool startAfter(const Work& after) {
1865 fIndex = after.fIndex;
1866 return advance();
1867 }
1868
1869 SkScalar top() const {
1870 return bounds().fTop;
1871 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001872
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001873 SkPath::Verb verb() const {
1874 return fContour->fSegments[fIndex].verb();
1875 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001876
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001877 SkScalar x() const {
1878 return bounds().fLeft;
1879 }
1880
1881 bool xFlipped() const {
1882 return x() != pts()[0].fX;
1883 }
1884
1885 SkScalar y() const {
1886 return bounds().fTop;
1887 }
1888
1889 bool yFlipped() const {
1890 return y() != pts()[0].fX;
1891 }
1892
1893protected:
1894 Contour* fContour;
1895 SkPoint fCubic[4];
1896 int fIndex;
1897 int fLast;
1898};
1899
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001900#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001901static void debugShowLineIntersection(int pts, const Work& wt,
1902 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001903 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001904 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
1905 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
1906 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
1907 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001908 return;
1909 }
1910 SkPoint wtOutPt, wnOutPt;
1911 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
1912 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001913 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001914 __FUNCTION__,
1915 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
1916 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
1917 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001918 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001919 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001920 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001921 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
1922 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
1923 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001924 SkDebugf(" wnTs[1]=%g", wnTs[1]);
1925 SkDebugf("\n");
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001926 }
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001927#else
1928static void debugShowLineIntersection(int , const Work& ,
1929 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001930}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001931#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001932
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001933static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001934
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001935 if (test != next) {
1936 if (test->bounds().fBottom < next->bounds().fTop) {
1937 return false;
1938 }
1939 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
1940 return true;
1941 }
1942 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001943 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001944 wt.init(test);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001945 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001946 Work wn;
1947 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001948 if (test == next && !wn.startAfter(wt)) {
1949 continue;
1950 }
1951 do {
1952 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
1953 continue;
1954 }
1955 int pts;
1956 Intersections ts;
1957 bool swap = false;
1958 switch (wt.segmentType()) {
1959 case Work::kHorizontalLine_Segment:
1960 swap = true;
1961 switch (wn.segmentType()) {
1962 case Work::kHorizontalLine_Segment:
1963 case Work::kVerticalLine_Segment:
1964 case Work::kLine_Segment: {
1965 pts = HLineIntersect(wn.pts(), wt.left(),
1966 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001967 debugShowLineIntersection(pts, wt, wn,
1968 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001969 break;
1970 }
1971 case Work::kQuad_Segment: {
1972 pts = HQuadIntersect(wn.pts(), wt.left(),
1973 wt.right(), wt.y(), wt.xFlipped(), ts);
1974 break;
1975 }
1976 case Work::kCubic_Segment: {
1977 pts = HCubicIntersect(wn.pts(), wt.left(),
1978 wt.right(), wt.y(), wt.xFlipped(), ts);
1979 break;
1980 }
1981 default:
1982 SkASSERT(0);
1983 }
1984 break;
1985 case Work::kVerticalLine_Segment:
1986 swap = true;
1987 switch (wn.segmentType()) {
1988 case Work::kHorizontalLine_Segment:
1989 case Work::kVerticalLine_Segment:
1990 case Work::kLine_Segment: {
1991 pts = VLineIntersect(wn.pts(), wt.top(),
1992 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001993 debugShowLineIntersection(pts, wt, wn,
1994 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001995 break;
1996 }
1997 case Work::kQuad_Segment: {
1998 pts = VQuadIntersect(wn.pts(), wt.top(),
1999 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2000 break;
2001 }
2002 case Work::kCubic_Segment: {
2003 pts = VCubicIntersect(wn.pts(), wt.top(),
2004 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2005 break;
2006 }
2007 default:
2008 SkASSERT(0);
2009 }
2010 break;
2011 case Work::kLine_Segment:
2012 switch (wn.segmentType()) {
2013 case Work::kHorizontalLine_Segment:
2014 pts = HLineIntersect(wt.pts(), wn.left(),
2015 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002016 debugShowLineIntersection(pts, wt, wn,
2017 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002018 break;
2019 case Work::kVerticalLine_Segment:
2020 pts = VLineIntersect(wt.pts(), wn.top(),
2021 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002022 debugShowLineIntersection(pts, wt, wn,
2023 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002024 break;
2025 case Work::kLine_Segment: {
2026 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2027 debugShowLineIntersection(pts, wt, wn,
2028 ts.fT[1], ts.fT[0]);
2029 break;
2030 }
2031 case Work::kQuad_Segment: {
2032 swap = true;
2033 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2034 break;
2035 }
2036 case Work::kCubic_Segment: {
2037 swap = true;
2038 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2039 break;
2040 }
2041 default:
2042 SkASSERT(0);
2043 }
2044 break;
2045 case Work::kQuad_Segment:
2046 switch (wn.segmentType()) {
2047 case Work::kHorizontalLine_Segment:
2048 pts = HQuadIntersect(wt.pts(), wn.left(),
2049 wn.right(), wn.y(), wn.xFlipped(), ts);
2050 break;
2051 case Work::kVerticalLine_Segment:
2052 pts = VQuadIntersect(wt.pts(), wn.top(),
2053 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2054 break;
2055 case Work::kLine_Segment: {
2056 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2057 break;
2058 }
2059 case Work::kQuad_Segment: {
2060 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2061 break;
2062 }
2063 case Work::kCubic_Segment: {
2064 wt.promoteToCubic();
2065 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2066 break;
2067 }
2068 default:
2069 SkASSERT(0);
2070 }
2071 break;
2072 case Work::kCubic_Segment:
2073 switch (wn.segmentType()) {
2074 case Work::kHorizontalLine_Segment:
2075 pts = HCubicIntersect(wt.pts(), wn.left(),
2076 wn.right(), wn.y(), wn.xFlipped(), ts);
2077 break;
2078 case Work::kVerticalLine_Segment:
2079 pts = VCubicIntersect(wt.pts(), wn.top(),
2080 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2081 break;
2082 case Work::kLine_Segment: {
2083 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2084 break;
2085 }
2086 case Work::kQuad_Segment: {
2087 wn.promoteToCubic();
2088 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2089 break;
2090 }
2091 case Work::kCubic_Segment: {
2092 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2093 break;
2094 }
2095 default:
2096 SkASSERT(0);
2097 }
2098 break;
2099 default:
2100 SkASSERT(0);
2101 }
2102 // in addition to recording T values, record matching segment
caryclark@google.com15fa1382012-05-07 20:49:36 +00002103 int coincident = pts == 2 && wn.segmentType() <= Work::kLine_Segment
2104 && wt.segmentType() <= Work::kLine_Segment ? -1 :0;
2105 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002106 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2107 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002108 int testTAt = wt.addT(ts.fT[swap][pt], wn, coincident);
2109 int nextTAt = wn.addT(ts.fT[!swap][pt], wt, coincident);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002110 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
2111 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002112 coincident = -coincident;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002113 }
2114 } while (wn.advance());
2115 } while (wt.advance());
2116 return true;
2117}
2118
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002119// see if coincidence is formed by clipping non-concident segments
2120static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
2121 int contourCount = contourList.count();
2122 for (size_t cIndex = 0; cIndex < contourCount; ++cIndex) {
2123 Contour* contour = contourList[cIndex];
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002124 contour->findTooCloseToCall(winding);
2125 }
2126}
2127
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002128
2129// OPTIMIZATION: not crazy about linear search here to find top active y.
2130// seems like we should break down and do the sort, or maybe sort each
2131// contours' segments?
2132// Once the segment array is built, there's no reason I can think of not to
2133// sort it in Y. hmmm
2134static Segment* findTopContour(SkTDArray<Contour*>& contourList,
2135 int contourCount) {
2136 int cIndex = 0;
2137 Segment* topStart;
2138 do {
2139 Contour* topContour = contourList[cIndex];
2140 topStart = topContour->topSegment();
2141 } while (!topStart && ++cIndex < contourCount);
2142 if (!topStart) {
2143 return NULL;
2144 }
2145 SkScalar top = topStart->bounds().fTop;
2146 for (int cTest = cIndex + 1; cTest < contourCount; ++cTest) {
2147 Contour* contour = contourList[cTest];
2148 if (top < contour->bounds().fTop) {
2149 continue;
2150 }
2151 Segment* test = contour->topSegment();
2152 if (top > test->bounds().fTop) {
2153 cIndex = cTest;
2154 topStart = test;
2155 top = test->bounds().fTop;
2156 }
2157 }
2158 return topStart;
2159}
2160
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002161// Each segment may have an inside or an outside. Segments contained within
2162// winding may have insides on either side, and form a contour that should be
2163// ignored. Segments that are coincident with opposing direction segments may
2164// have outsides on either side, and should also disappear.
2165// 'Normal' segments will have one inside and one outside. Subsequent connections
2166// when winding should follow the intersection direction. If more than one edge
2167// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002168 // since we start with leftmost top edge, we'll traverse through a
2169 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002170static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002171 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002172 int winding = 0; // there are no contours outside this one
caryclark@google.com15fa1382012-05-07 20:49:36 +00002173 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002174 Segment* topStart = findTopContour(contourList, contourCount);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002175 if (!topStart) {
2176 break;
2177 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002178 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00002179 // follow edges to intersection by changing the index by direction.
2180 int index, endIndex;
2181 Segment* topSegment = topStart->findTop(index, endIndex);
2182 Segment* current = topSegment;
2183 winding += SkSign32(index - endIndex);
2184 bool first = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002185 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002186 SkASSERT(!current->done());
2187 int nextStart, nextEnd;
2188 Segment* next = current->findNext(winding, index, endIndex,
2189 nextStart, nextEnd);
2190 if (!next) {
2191 break;
2192 }
2193 if (first) {
2194 current->addMoveTo(index, simple);
2195 first = false;
2196 }
2197 current->addCurveTo(index, endIndex, simple);
2198 current = next;
2199 index = nextStart;
2200 endIndex = nextEnd;
2201 } while (current != topSegment);
2202 if (!first) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002203 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com495f8e42012-05-31 13:13:11 +00002204 SkDebugf("%s close\n", __FUNCTION__);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002205 #endif
caryclark@google.com495f8e42012-05-31 13:13:11 +00002206 simple.close();
2207 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002208 } while (true);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002209 // FIXME: more work to be done for contained (but not intersecting)
2210 // segments
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002211}
2212
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002213static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
2214 int contourCount = contourList.count();
2215 for (int cTest = 0; cTest < contourCount; ++cTest) {
2216 Contour* contour = contourList[cTest];
2217 contour->fixOtherTIndex();
2218 }
2219}
2220
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002221static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002222 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002223 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002224 if (count == 0) {
2225 return;
2226 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002227 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002228 *list.append() = &contours[index];
2229 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002230 QSort<Contour>(list.begin(), list.end() - 1);
2231}
2232
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002233void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002234 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002235 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002236 simple.reset();
2237 simple.setFillType(SkPath::kEvenOdd_FillType);
2238
2239 // turn path into list of segments
2240 SkTArray<Contour> contours;
2241 // FIXME: add self-intersecting cubics' T values to segment
2242 EdgeBuilder builder(path, contours);
2243 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002244 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002245 Contour** currentPtr = contourList.begin();
2246 if (!currentPtr) {
2247 return;
2248 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002249 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002250 // find all intersections between segments
2251 do {
2252 Contour** nextPtr = currentPtr;
2253 Contour* current = *currentPtr++;
2254 Contour* next;
2255 do {
2256 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002257 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002258 } while (currentPtr != listEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002259 fixOtherTIndex(contourList);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002260 // eat through coincident edges
2261 coincidenceCheck(contourList, winding);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002262 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002263 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002264}
2265