blob: a9f7a40b798dff0c26aacab50d02f29430a462d5 [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
caryclark@google.com8dcf1142012-07-02 20:27:02 +000028#define DEBUG_CROSS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000029#define DEBUG_DUMP 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000030#define DEBUG_PATH_CONSTRUCTION 0
caryclark@google.com027de222012-07-12 12:52:50 +000031#define DEBUG_ACTIVE_SPANS 0
caryclark@google.com8dcf1142012-07-02 20:27:02 +000032#define DEBUG_WINDING 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000033#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com8dcf1142012-07-02 20:27:02 +000034#define DEBUG_MARK_DONE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000035
36#else
37
38//const bool gRunTestsInOneThread = true;
39
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000040#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000041#define DEBUG_BRIDGE 1
caryclark@google.com8dcf1142012-07-02 20:27:02 +000042#define DEBUG_CROSS 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000043#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000044#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.com027de222012-07-12 12:52:50 +000045#define DEBUG_ACTIVE_SPANS 01
caryclark@google.comfa4a6e92012-07-11 17:52:32 +000046#define DEBUG_WINDING 01
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000047#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa4a6e92012-07-11 17:52:32 +000048#define DEBUG_MARK_DONE 01
caryclark@google.comfa0588f2012-04-26 21:01:06 +000049
50#endif
51
caryclark@google.com027de222012-07-12 12:52:50 +000052#if DEBUG_ACTIVE_SPANS && !DEBUG_DUMP
53#undef DEBUG_DUMP
54#define DEBUG_DUMP 1
55#endif
56
caryclark@google.comfa0588f2012-04-26 21:01:06 +000057#if DEBUG_DUMP
58static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000059// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000060static int gContourID;
61static int gSegmentID;
62#endif
63
caryclark@google.com8dcf1142012-07-02 20:27:02 +000064#ifndef DEBUG_TEST
65#define DEBUG_TEST 0
66#endif
67
caryclark@google.comfa0588f2012-04-26 21:01:06 +000068static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
69 Intersections& intersections) {
70 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
71 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
72 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
73}
74
75static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
76 Intersections& intersections) {
77 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
78 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
79 intersect(aQuad, bLine, intersections);
80 return intersections.fUsed;
81}
82
83static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
84 Intersections& intersections) {
85 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
86 {a[3].fX, a[3].fY}};
87 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
88 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
89}
90
91static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
92 Intersections& intersections) {
93 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
94 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
95 intersect(aQuad, bQuad, intersections);
96 return intersections.fUsed;
97}
98
99static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
100 Intersections& intersections) {
101 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
102 {a[3].fX, a[3].fY}};
103 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
104 {b[3].fX, b[3].fY}};
105 intersect(aCubic, bCubic, intersections);
106 return intersections.fUsed;
107}
108
109static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
110 SkScalar y, bool flipped, Intersections& intersections) {
111 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
112 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
113}
114
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000115static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
116 SkScalar y, bool flipped, Intersections& intersections) {
117 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
118 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
119}
120
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000121static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
122 SkScalar y, bool flipped, Intersections& intersections) {
123 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
124 {a[3].fX, a[3].fY}};
125 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
126}
127
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000128static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
129 SkScalar x, bool flipped, Intersections& intersections) {
130 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
131 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
132}
133
134static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
135 SkScalar x, bool flipped, Intersections& intersections) {
136 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
137 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
138}
139
140static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
141 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000142 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
143 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000144 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000145}
146
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000147static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
148 SkScalar , SkScalar , bool , Intersections& ) = {
149 NULL,
150 VLineIntersect,
151 VQuadIntersect,
152 VCubicIntersect
153};
154
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000155static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
156 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
157 double x, y;
158 xy_at_t(line, t, x, y);
159 out->fX = SkDoubleToScalar(x);
160 out->fY = SkDoubleToScalar(y);
161}
162
163static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
164 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
165 double x, y;
166 xy_at_t(quad, t, x, y);
167 out->fX = SkDoubleToScalar(x);
168 out->fY = SkDoubleToScalar(y);
169}
170
171static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
172 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
173 {a[3].fX, a[3].fY}};
174 double x, y;
175 xy_at_t(cubic, t, x, y);
176 out->fX = SkDoubleToScalar(x);
177 out->fY = SkDoubleToScalar(y);
178}
179
180static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
181 NULL,
182 LineXYAtT,
183 QuadXYAtT,
184 CubicXYAtT
185};
186
187static SkScalar LineXAtT(const SkPoint a[2], double t) {
188 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
189 double x;
190 xy_at_t(aLine, t, x, *(double*) 0);
191 return SkDoubleToScalar(x);
192}
193
194static SkScalar QuadXAtT(const SkPoint a[3], double t) {
195 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
196 double x;
197 xy_at_t(quad, t, x, *(double*) 0);
198 return SkDoubleToScalar(x);
199}
200
201static SkScalar CubicXAtT(const SkPoint a[4], double t) {
202 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
203 {a[3].fX, a[3].fY}};
204 double x;
205 xy_at_t(cubic, t, x, *(double*) 0);
206 return SkDoubleToScalar(x);
207}
208
209static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
210 NULL,
211 LineXAtT,
212 QuadXAtT,
213 CubicXAtT
214};
215
216static SkScalar LineYAtT(const SkPoint a[2], double t) {
217 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
218 double y;
219 xy_at_t(aLine, t, *(double*) 0, y);
220 return SkDoubleToScalar(y);
221}
222
223static SkScalar QuadYAtT(const SkPoint a[3], double t) {
224 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
225 double y;
226 xy_at_t(quad, t, *(double*) 0, y);
227 return SkDoubleToScalar(y);
228}
229
230static SkScalar CubicYAtT(const SkPoint a[4], double t) {
231 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
232 {a[3].fX, a[3].fY}};
233 double y;
234 xy_at_t(cubic, t, *(double*) 0, y);
235 return SkDoubleToScalar(y);
236}
237
238static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
239 NULL,
240 LineYAtT,
241 QuadYAtT,
242 CubicYAtT
243};
244
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000245static SkScalar LineDXAtT(const SkPoint a[2], double ) {
246 return a[1].fX - a[0].fX;
247}
248
249static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
250 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
251 double x;
252 dxdy_at_t(quad, t, x, *(double*) 0);
253 return SkDoubleToScalar(x);
254}
255
256static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
257 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
258 {a[3].fX, a[3].fY}};
259 double x;
260 dxdy_at_t(cubic, t, x, *(double*) 0);
261 return SkDoubleToScalar(x);
262}
263
264static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
265 NULL,
266 LineDXAtT,
267 QuadDXAtT,
268 CubicDXAtT
269};
270
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000271static void LineSubDivide(const SkPoint a[2], double startT, double endT,
272 SkPoint sub[2]) {
273 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
274 _Line dst;
275 sub_divide(aLine, startT, endT, dst);
276 sub[0].fX = SkDoubleToScalar(dst[0].x);
277 sub[0].fY = SkDoubleToScalar(dst[0].y);
278 sub[1].fX = SkDoubleToScalar(dst[1].x);
279 sub[1].fY = SkDoubleToScalar(dst[1].y);
280}
281
282static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
283 SkPoint sub[3]) {
284 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
285 {a[2].fX, a[2].fY}};
286 Quadratic dst;
287 sub_divide(aQuad, startT, endT, dst);
288 sub[0].fX = SkDoubleToScalar(dst[0].x);
289 sub[0].fY = SkDoubleToScalar(dst[0].y);
290 sub[1].fX = SkDoubleToScalar(dst[1].x);
291 sub[1].fY = SkDoubleToScalar(dst[1].y);
292 sub[2].fX = SkDoubleToScalar(dst[2].x);
293 sub[2].fY = SkDoubleToScalar(dst[2].y);
294}
295
296static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
297 SkPoint sub[4]) {
298 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
299 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
300 Cubic dst;
301 sub_divide(aCubic, startT, endT, dst);
302 sub[0].fX = SkDoubleToScalar(dst[0].x);
303 sub[0].fY = SkDoubleToScalar(dst[0].y);
304 sub[1].fX = SkDoubleToScalar(dst[1].x);
305 sub[1].fY = SkDoubleToScalar(dst[1].y);
306 sub[2].fX = SkDoubleToScalar(dst[2].x);
307 sub[2].fY = SkDoubleToScalar(dst[2].y);
308 sub[3].fX = SkDoubleToScalar(dst[3].x);
309 sub[3].fY = SkDoubleToScalar(dst[3].y);
310}
311
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000312static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
313 SkPoint []) = {
314 NULL,
315 LineSubDivide,
316 QuadSubDivide,
317 CubicSubDivide
318};
319
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000320#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000321static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
322 SkRect& bounds) {
323 SkPoint dst[3];
324 QuadSubDivide(a, startT, endT, dst);
325 bounds.fLeft = bounds.fRight = dst[0].fX;
326 bounds.fTop = bounds.fBottom = dst[0].fY;
327 for (int index = 1; index < 3; ++index) {
328 bounds.growToInclude(dst[index].fX, dst[index].fY);
329 }
330}
331
332static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
333 SkRect& bounds) {
334 SkPoint dst[4];
335 CubicSubDivide(a, startT, endT, dst);
336 bounds.fLeft = bounds.fRight = dst[0].fX;
337 bounds.fTop = bounds.fBottom = dst[0].fY;
338 for (int index = 1; index < 4; ++index) {
339 bounds.growToInclude(dst[index].fX, dst[index].fY);
340 }
341}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000342#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000343
caryclark@google.com15fa1382012-05-07 20:49:36 +0000344static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000345 SkTDArray<SkPoint>& reducePts) {
346 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
347 {a[2].fX, a[2].fY}};
348 Quadratic dst;
349 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000350 if (order == 3) {
351 return SkPath::kQuad_Verb;
352 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000353 for (int index = 0; index < order; ++index) {
354 SkPoint* pt = reducePts.append();
355 pt->fX = SkDoubleToScalar(dst[index].x);
356 pt->fY = SkDoubleToScalar(dst[index].y);
357 }
358 return (SkPath::Verb) (order - 1);
359}
360
361static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
362 SkTDArray<SkPoint>& reducePts) {
363 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
364 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
365 Cubic dst;
366 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000367 if (order == 4) {
368 return SkPath::kCubic_Verb;
369 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000370 for (int index = 0; index < order; ++index) {
371 SkPoint* pt = reducePts.append();
372 pt->fX = SkDoubleToScalar(dst[index].x);
373 pt->fY = SkDoubleToScalar(dst[index].y);
374 }
375 return (SkPath::Verb) (order - 1);
376}
377
caryclark@google.com15fa1382012-05-07 20:49:36 +0000378static bool QuadIsLinear(const SkPoint a[3]) {
379 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
380 {a[2].fX, a[2].fY}};
381 return isLinear(aQuad, 0, 2);
382}
383
384static bool CubicIsLinear(const SkPoint a[4]) {
385 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
386 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
387 return isLinear(aCubic, 0, 3);
388}
389
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000390static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
391 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
392 double x[2];
393 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000394 xy_at_t(aLine, endT, x[1], *(double*) 0);
395 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000396}
397
398static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
399 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
400 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000401 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000402}
403
404static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
405 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
406 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000407 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000408}
409
410static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
411 NULL,
412 LineLeftMost,
413 QuadLeftMost,
414 CubicLeftMost
415};
416
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000417#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
419 const SkPoint& below) {
420 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
421 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
422 return implicit_matches_ulps(aLine, bLine, 32);
423}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000424#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000425
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000426class Segment;
427
caryclark@google.com15fa1382012-05-07 20:49:36 +0000428// sorting angles
429// given angles of {dx dy ddx ddy dddx dddy} sort them
430class Angle {
431public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000432 // FIXME: this is bogus for quads and cubics
433 // if the quads and cubics' line from end pt to ctrl pt are coincident,
434 // there's no obvious way to determine the curve ordering from the
435 // derivatives alone. In particular, if one quadratic's coincident tangent
436 // is longer than the other curve, the final control point can place the
437 // longer curve on either side of the shorter one.
438 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
439 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000440 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000441 if ((fDy < 0) ^ (rh.fDy < 0)) {
442 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000443 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
445 return fDx < rh.fDx;
446 }
447 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000448 if (cmp) {
449 return cmp < 0;
450 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000451 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
452 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000453 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000454 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
455 return fDDx < rh.fDDx;
456 }
457 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000458 if (cmp) {
459 return cmp < 0;
460 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000461 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
462 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000463 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000464 if (fDDDy == 0 && rh.fDDDy == 0) {
465 return fDDDx < rh.fDDDx;
466 }
467 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000468 }
469
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000470 int end() const {
471 return fEnd;
472 }
473
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000474 bool isHorizontal() const {
475 return fDy == 0 && fDDy == 0 && fDDDy == 0;
476 }
477
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000478 // since all angles share a point, this needs to know which point
479 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
480 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000481 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000482 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000483 SkASSERT(start != end);
484 fSegment = segment;
485 fStart = start;
486 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000487 fDx = pts[1].fX - pts[0].fX; // b - a
488 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000489 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000490 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000491 return;
492 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000493 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
494 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000495 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000496 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000497 return;
498 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000499 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
500 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
501 }
502
503 // noncoincident quads/cubics may have the same initial angle
504 // as lines, so must sort by derivatives as well
505 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000506 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000507 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000508 fSegment = segment;
509 fStart = start;
510 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000511 fDx = pts[1].fX - pts[0].fX; // b - a
512 fDy = pts[1].fY - pts[0].fY;
513 if (verb == SkPath::kLine_Verb) {
514 fDDx = fDDy = fDDDx = fDDDy = 0;
515 return;
516 }
517 if (verb == SkPath::kQuad_Verb) {
518 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
519 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
520 int larger = std::max(abs(uplsX), abs(uplsY));
521 int shift = 0;
522 double flatT;
523 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
524 LineParameters implicitLine;
525 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
526 implicitLine.lineEndPoints(tangent);
527 implicitLine.normalize();
528 while (larger > UlpsEpsilon * 1024) {
529 larger >>= 2;
530 ++shift;
531 flatT = 0.5 / (1 << shift);
532 QuadXYAtT(pts, flatT, &ddPt);
533 _Point _pt = {ddPt.fX, ddPt.fY};
534 double distance = implicitLine.pointDistance(_pt);
535 if (approximately_zero(distance)) {
536 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
537 break;
538 }
539 }
540 flatT = 0.5 / (1 << shift);
541 QuadXYAtT(pts, flatT, &ddPt);
542 fDDx = ddPt.fX - pts[0].fX;
543 fDDy = ddPt.fY - pts[0].fY;
544 SkASSERT(fDDx != 0 || fDDy != 0);
545 fDDDx = fDDDy = 0;
546 return;
547 }
548 SkASSERT(0); // FIXME: add cubic case
549 }
550
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000551 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000552 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000553 }
554
555 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000556 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000557 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000558
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000559 int start() const {
560 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000561 }
562
563private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000564 SkScalar fDx;
565 SkScalar fDy;
566 SkScalar fDDx;
567 SkScalar fDDy;
568 SkScalar fDDDx;
569 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000570 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000571 int fStart;
572 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000573};
574
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000575static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
576 int angleCount = angles.count();
577 int angleIndex;
578 angleList.setReserve(angleCount);
579 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
580 *angleList.append() = &angles[angleIndex];
581 }
582 QSort<Angle>(angleList.begin(), angleList.end() - 1);
583}
584
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000585// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000586struct Bounds : public SkRect {
587 static bool Intersects(const Bounds& a, const Bounds& b) {
588 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
589 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
590 }
591
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000592 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
593 if (left < fLeft) {
594 fLeft = left;
595 }
596 if (top < fTop) {
597 fTop = top;
598 }
599 if (right > fRight) {
600 fRight = right;
601 }
602 if (bottom > fBottom) {
603 fBottom = bottom;
604 }
605 }
606
607 void add(const Bounds& toAdd) {
608 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
609 }
610
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000611 bool isEmpty() {
612 return fLeft > fRight || fTop > fBottom
613 || fLeft == fRight && fTop == fBottom
614 || isnan(fLeft) || isnan(fRight)
615 || isnan(fTop) || isnan(fBottom);
616 }
617
618 void setCubicBounds(const SkPoint a[4]) {
619 _Rect dRect;
620 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
621 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
622 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000623 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
624 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000625 }
626
627 void setQuadBounds(const SkPoint a[3]) {
628 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
629 {a[2].fX, a[2].fY}};
630 _Rect dRect;
631 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000632 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
633 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000634 }
635};
636
caryclark@google.com15fa1382012-05-07 20:49:36 +0000637struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000638 Segment* fOther;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000639 mutable SkPoint const* fPt; // lazily computed as needed
640 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000641 double fOtherT; // value at fOther[fOtherIndex].fT
642 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000643 int fWindSum; // accumulated from contours surrounding this one
644 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000645 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000646};
647
648class Segment {
649public:
650 Segment() {
651#if DEBUG_DUMP
652 fID = ++gSegmentID;
653#endif
654 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000655
656 bool activeAngles(int index) const {
657 double referenceT = fTs[index].fT;
658 int lesser = index;
659 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
660 if (activeAnglesInner(lesser)) {
661 return true;
662 }
663 }
664 do {
665 if (activeAnglesInner(index)) {
666 return true;
667 }
668 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
669 return false;
670 }
671
672 bool activeAnglesInner(int index) const {
673 Span* span = &fTs[index];
674 Segment* other = span->fOther;
675 int oIndex = span->fOtherIndex;
676 int next = other->nextSpan(oIndex, 1);
677 if (next > 0 && !other->fTs[oIndex].fDone) {
678 return true;
679 }
680 int prev = other->nextSpan(oIndex, -1);
681 // edge leading into junction
682 return prev >= 0 && !other->fTs[prev].fDone;
683 }
684
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000685 SkScalar activeTop() const {
686 SkASSERT(!done());
687 int count = fTs.count();
688 SkScalar result = SK_ScalarMax;
689 bool lastDone = true;
690 for (int index = 0; index < count; ++index) {
691 bool done = fTs[index].fDone;
692 if (!done || !lastDone) {
693 SkScalar y = yAtT(index);
694 if (result > y) {
695 result = y;
696 }
697 }
698 lastDone = done;
699 }
700 SkASSERT(result < SK_ScalarMax);
701 return result;
702 }
703
704 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000705 SkASSERT(start != end);
706 SkPoint edge[4];
707 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
708 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000709 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000710 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000711
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000712 void addCubic(const SkPoint pts[4]) {
713 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000714 fBounds.setCubicBounds(pts);
715 }
716
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000717 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000718 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000719 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000720 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000721 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000722 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000723 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000724 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
725 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
726 if (fVerb > 1) {
727 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
728 }
729 if (fVerb > 2) {
730 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
731 }
732 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000733 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000734 switch (fVerb) {
735 case SkPath::kLine_Verb:
736 path.lineTo(edge[1].fX, edge[1].fY);
737 break;
738 case SkPath::kQuad_Verb:
739 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
740 break;
741 case SkPath::kCubic_Verb:
742 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
743 edge[3].fX, edge[3].fY);
744 break;
745 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000746 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000747 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000748 }
749
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000750 void addLine(const SkPoint pts[2]) {
751 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000752 fBounds.set(pts, 2);
753 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000754
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000755 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
756 const SkPoint& pt = xyAtT(tIndex);
757 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000758 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000759 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000760 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000761 path.moveTo(pt.fX, pt.fY);
762 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000763 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000764 }
765
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000766 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000767 void addOtherT(int index, double otherT, int otherIndex) {
768 Span& span = fTs[index];
769 span.fOtherT = otherT;
770 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000771 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000772
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000773 void addQuad(const SkPoint pts[3]) {
774 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000775 fBounds.setQuadBounds(pts);
776 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000777
778 // Defer all coincident edge processing until
779 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000780
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000781// no need to be tricky; insert in normal T order
782// resolve overlapping ts when considering coincidence later
783
784 // add non-coincident intersection. Resulting edges are sorted in T.
785 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000786 // FIXME: in the pathological case where there is a ton of intercepts,
787 // binary search?
788 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000789 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000790 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000791 // OPTIMIZATION: if there are three or more identical Ts, then
792 // the fourth and following could be further insertion-sorted so
793 // that all the edges are clockwise or counterclockwise.
794 // This could later limit segment tests to the two adjacent
795 // neighbors, although it doesn't help with determining which
796 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000797 if (newT < fTs[index].fT) {
798 insertedAt = index;
799 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000800 }
801 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000802 Span* span;
803 if (insertedAt >= 0) {
804 span = fTs.insert(insertedAt);
805 } else {
806 insertedAt = tCount;
807 span = fTs.append();
808 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000809 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000810 span->fOther = other;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000811 span->fPt = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000812 span->fWindSum = SK_MinS32;
813 span->fWindValue = 1;
814 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000815 ++fDoneSpans;
816 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000817 return insertedAt;
818 }
819
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000820 // set spans from start to end to decrement by one
821 // note this walks other backwards
822 // FIMXE: there's probably an edge case that can be constructed where
823 // two span in one segment are separated by float epsilon on one span but
824 // not the other, if one segment is very small. For this
825 // case the counts asserted below may or may not be enough to separate the
826 // spans. Even if the counts work out, what if the spanw aren't correctly
827 // sorted? It feels better in such a case to match the span's other span
828 // pointer since both coincident segments must contain the same spans.
829 void addTCancel(double startT, double endT, Segment& other,
830 double oStartT, double oEndT) {
831 SkASSERT(endT - startT >= FLT_EPSILON);
832 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
833 int index = 0;
834 while (startT - fTs[index].fT >= FLT_EPSILON) {
835 ++index;
836 }
caryclark@google.comb9738012012-07-03 19:53:30 +0000837 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000838 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
839 ;
840 Span* test = &fTs[index];
841 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000842 do {
843 bool decrement = test->fWindValue && oTest->fWindValue;
844 Span* end = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000845 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000846 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000847 SkASSERT(end->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000848 if (--(end->fWindValue) == 0) {
849 end->fDone = true;
850 ++fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000851 }
852 }
853 end = &fTs[++index];
854 } while (end->fT - test->fT < FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000855 Span* oTestStart = oTest;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000856 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000857 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000858 SkASSERT(oTestStart->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000859 if (--(oTestStart->fWindValue) == 0) {
860 oTestStart->fDone = true;
861 ++other.fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000862 }
863 }
864 if (!oIndex) {
865 break;
866 }
867 oTestStart = &other.fTs[--oIndex];
868 } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
869 test = end;
870 oTest = oTestStart;
871 } while (test->fT < endT - FLT_EPSILON);
872 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000873 }
874
875 // set spans from start to end to increment the greater by one and decrement
876 // the lesser
877 void addTCoincident(double startT, double endT, Segment& other,
878 double oStartT, double oEndT) {
879 SkASSERT(endT - startT >= FLT_EPSILON);
880 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
881 int index = 0;
882 while (startT - fTs[index].fT >= FLT_EPSILON) {
883 ++index;
884 }
885 int oIndex = 0;
886 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
887 ++oIndex;
888 }
889 Span* test = &fTs[index];
890 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000891 SkTDArray<double> outsideTs;
892 SkTDArray<double> oOutsideTs;
893 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000894 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000895 bool decrementOther = test->fWindValue >= oTest->fWindValue;
896 Span* end = test;
897 double startT = end->fT;
898 double oStartT = oTest->fT;
899 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000900 if (transfer) {
901 if (decrementOther) {
902 ++(end->fWindValue);
903 } else {
904 SkASSERT(end->fWindValue > 0);
905 if (--(end->fWindValue) == 0) {
906 end->fDone = true;
907 ++fDoneSpans;
908 int outCount = outsideTs.count();
909 if (outCount == 0 || end->fT - outsideTs[outCount - 2]
910 >= FLT_EPSILON) {
911 *outsideTs.append() = end->fT;
912 *outsideTs.append() = oStartT;
913 }
914 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000915 }
916 }
917 end = &fTs[++index];
918 } while (end->fT - test->fT < FLT_EPSILON);
919 Span* oEnd = oTest;
920 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000921 if (transfer) {
922 if (decrementOther) {
923 SkASSERT(oEnd->fWindValue > 0);
924 if (--(oEnd->fWindValue) == 0) {
925 oEnd->fDone = true;
926 ++other.fDoneSpans;
927 int oOutCount = oOutsideTs.count();
928 if (oOutCount == 0 || oEnd->fT
929 - oOutsideTs[oOutCount - 2] >= FLT_EPSILON) {
930 *oOutsideTs.append() = oEnd->fT;
931 *oOutsideTs.append() = startT;
932 }
933 }
934 } else {
935 ++(oEnd->fWindValue);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000936 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000937 }
938 oEnd = &other.fTs[++oIndex];
939 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
940 test = end;
941 oTest = oEnd;
942 } while (test->fT < endT - FLT_EPSILON);
943 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
944 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
945 if (!done() && outsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000946 addTOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000947 }
948 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000949 other.addTOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000950 }
951 }
952
caryclark@google.comb9738012012-07-03 19:53:30 +0000953 void addTOutsides(const SkTDArray<double>& outsideTs, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000954 double otherEnd) {
955 int count = outsideTs.count();
956 double endT = 0;
957 int endSpan = 0;
958 for (int index = 0; index < count; index += 2) {
959 double t = outsideTs[index];
960 double otherT = outsideTs[index + 1];
961 if (t > 1 - FLT_EPSILON) {
962 return;
963 }
964 if (t - endT > FLT_EPSILON) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000965 endSpan = addTDonePair(t, other, otherT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000966 }
967 do {
968 endT = fTs[++endSpan].fT;
969 } while (endT - t < FLT_EPSILON);
970 }
971 addTPair(endT, other, otherEnd);
972 }
973
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000974 // match the other.fWindValue to its mates
975 int addTDonePair(double t, Segment& other, double otherT) {
976 int insertedAt = addTPair(t, other, otherT);
977 Span& end = fTs[insertedAt];
978 SkASSERT(end.fWindValue == 1);
979 end.fWindValue = 0;
980 end.fDone = true;
981 ++fDoneSpans;
982 Span& otherEnd = other.fTs[end.fOtherIndex];
983 Span* match = NULL;
984 if (end.fOtherIndex > 0) {
985 match = &other.fTs[end.fOtherIndex - 1];
986 }
987 if (!match || match->fT < otherT) {
988 match = &other.fTs[end.fOtherIndex + 1];
989 }
990 otherEnd.fWindValue = match->fWindValue;
991 return insertedAt;
992 }
993
caryclark@google.comb9738012012-07-03 19:53:30 +0000994 int addTPair(double t, Segment& other, double otherT) {
995 int insertedAt = addT(t, &other);
996 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000997 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +0000998 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000999 return insertedAt;
1000 }
1001
1002 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001003 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001004 if (fTs[SkMin32(end, start)].fWindValue > 0) {
1005 addAngle(angles, end, start);
1006 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001007 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001008 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001009 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001010 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001011 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001012 }
1013 }
1014
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001015 const Bounds& bounds() const {
1016 return fBounds;
1017 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001018
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001019 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001020 double referenceT = fTs[index].fT;
1021 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001022 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001023 buildAnglesInner(lesser, angles);
1024 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001025 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001026 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001027 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001028 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001029
1030 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1031 Span* span = &fTs[index];
1032 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001033 // if there is only one live crossing, and no coincidence, continue
1034 // in the same direction
1035 // if there is coincidence, the only choice may be to reverse direction
1036 // find edge on either side of intersection
1037 int oIndex = span->fOtherIndex;
1038 // if done == -1, prior span has already been processed
1039 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001040 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001041 if (next < 0) {
1042 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001043 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001044 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001045 // add candidate into and away from junction
1046 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001047 }
1048
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001049 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001050 SkASSERT(fVerb == SkPath::kLine_Verb);
1051 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1052 SkPoint dxy = fPts[0] - fPts[1];
1053 SkPoint odxy = other.fPts[0] - other.fPts[1];
1054 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001055 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001056
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001057 // figure out if the segment's ascending T goes clockwise or not
1058 // not enough context to write this as shown
1059 // instead, add all segments meeting at the top
1060 // sort them using buildAngleList
1061 // find the first in the sort
1062 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001063 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001064 SkASSERT(0); // incomplete
1065 return false;
1066 }
1067
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001068 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
1069 int start = 0;
1070 int bestT = -1;
1071 SkScalar top = bounds().fTop;
1072 SkScalar bottom = bounds().fBottom;
1073 int end;
1074 do {
1075 end = nextSpan(start, 1);
1076 SkPoint edge[4];
1077 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1078 // work with the original data directly
1079 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001080 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001081 Intersections intersections;
1082 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1083 false, intersections);
1084 if (pts == 0) {
1085 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001086 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001087 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1088 // if the intersection is edge on, wait for another one
1089 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001090 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001091 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1092 SkPoint pt;
1093 double foundT = intersections.fT[0][0];
1094 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1095 if (bestY < pt.fY) {
1096 bestY = pt.fY;
1097 bestT = foundT < 1 ? start : end;
1098 hitT = foundT;
1099 }
1100 start = end;
1101 } while (fTs[end].fT != 1);
1102 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001103 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001104
caryclark@google.com15fa1382012-05-07 20:49:36 +00001105 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001106 SkASSERT(fDoneSpans <= fTs.count());
1107 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001108 }
1109
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001110 // so the span needs to contain the pairing info found here
1111 // this should include the winding computed for the edge, and
1112 // what edge it connects to, and whether it is discarded
1113 // (maybe discarded == abs(winding) > 1) ?
1114 // only need derivatives for duration of sorting, add a new struct
1115 // for pairings, remove extra spans that have zero length and
1116 // reference an unused other
1117 // for coincident, the last span on the other may be marked done
1118 // (always?)
1119
1120 // if loop is exhausted, contour may be closed.
1121 // FIXME: pass in close point so we can check for closure
1122
1123 // given a segment, and a sense of where 'inside' is, return the next
1124 // segment. If this segment has an intersection, or ends in multiple
1125 // segments, find the mate that continues the outside.
1126 // note that if there are multiples, but no coincidence, we can limit
1127 // choices to connections in the correct direction
1128
1129 // mark found segments as done
1130
caryclark@google.com15fa1382012-05-07 20:49:36 +00001131 // start is the index of the beginning T of this edge
1132 // it is guaranteed to have an end which describes a non-zero length (?)
1133 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001134 // firstFind allows coincident edges to be treated differently
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001135 Segment* findNext(SkTDArray<Span*>& chase, int winding, const int startIndex,
1136 const int endIndex,
1137 int& nextStart, int& nextEnd, int& flipped, bool firstFind) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001138 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001139 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001140 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1141 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001142 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001143 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001144 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001145 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001146 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001147 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001148 // mark the smaller of startIndex, endIndex done, and all adjacent
1149 // spans with the same T value (but not 'other' spans)
caryclark@google.com495f8e42012-05-31 13:13:11 +00001150 markDone(SkMin32(startIndex, endIndex), winding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001151 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001152 nextStart = endSpan->fOtherIndex;
1153 nextEnd = nextStart + step;
1154 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001155 return other;
1156 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001157 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001158 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001159 SkASSERT(startIndex - endIndex != 0);
1160 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001161 addTwoAngles(startIndex, end, angles);
1162 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001163 SkTDArray<Angle*> sorted;
1164 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001165 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001166 int firstIndex = findStartingEdge(sorted, startIndex, end);
1167
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001168 SkASSERT(firstIndex >= 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001169 int startWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001170 int nextIndex = firstIndex + 1;
1171 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1172 const Angle* foundAngle = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001173 // iterate through the angle, and compute everyone's winding
1174 bool firstEdge = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001175 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001176 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001177 nextIndex = 0;
1178 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001179 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001180 int maxWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001181 Segment* nextSegment = nextAngle->segment();
1182 int windValue = nextSegment->windValue(nextAngle);
1183 SkASSERT(windValue > 0);
1184 winding -= nextAngle->sign() * windValue;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001185 #if DEBUG_WINDING
1186 SkDebugf("%s maxWinding=%d winding=%d\n", __FUNCTION__, maxWinding,
1187 winding);
1188 #endif
1189 if (maxWinding * winding < 0) {
1190 flipped = -flipped;
1191 SkDebugf("flipped sign %d %d\n", maxWinding, winding);
1192 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001193 firstEdge = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001194 if (!winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001195 if (!foundAngle) {
1196 foundAngle = nextAngle;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001197 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001198 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001199 }
1200 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001201 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001202 }
1203 // if the winding is non-zero, nextAngle does not connect to
1204 // current chain. If we haven't done so already, mark the angle
1205 // as done, record the winding value, and mark connected unambiguous
1206 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001207 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001208 if (abs(maxWinding) < abs(winding)) {
1209 maxWinding = winding;
1210 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001211 Span* last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001212 if (foundAngle) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001213 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001214 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001215 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1216 }
1217 if (last) {
1218 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001219 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001220 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001221 } while (++nextIndex != lastIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001222 sorted[firstIndex]->segment()->
1223 markDone(SkMin32(startIndex, endIndex), startWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001224 if (!foundAngle) {
1225 return NULL;
1226 }
1227 nextStart = foundAngle->start();
1228 nextEnd = foundAngle->end();
1229 return foundAngle->segment();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001230 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001231
1232 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1233 int angleCount = sorted.count();
1234 int firstIndex = -1;
1235 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1236 const Angle* angle = sorted[angleIndex];
1237 if (angle->segment() == this && angle->start() == end &&
1238 angle->end() == start) {
1239 firstIndex = angleIndex;
1240 break;
1241 }
1242 }
1243 return firstIndex;
1244 }
1245
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001246 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001247 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001248 int count = fTs.count();
1249 if (count < 3) { // require t=0, x, 1 at minimum
1250 return;
1251 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001252 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001253 int moCount;
1254 Span* match;
1255 Segment* mOther;
1256 do {
1257 match = &fTs[matchIndex];
1258 mOther = match->fOther;
1259 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001260 if (moCount >= 3) {
1261 break;
1262 }
1263 if (++matchIndex >= count) {
1264 return;
1265 }
1266 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001267 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001268 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001269 // look for a pair of nearby T values that map to the same (x,y) value
1270 // if found, see if the pair of other segments share a common point. If
1271 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001272 for (int index = matchIndex + 1; index < count; ++index) {
1273 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001274 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001275 continue;
1276 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001277 Segment* tOther = test->fOther;
1278 int toCount = tOther->fTs.count();
1279 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001280 continue;
1281 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001282 const SkPoint* testPt = &xyAtT(test);
1283 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001284 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001285 moCount = toCount;
1286 match = test;
1287 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001288 matchPt = testPt;
1289 continue;
1290 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001291 int moStart = -1;
1292 int moEnd = -1;
1293 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001294 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001295 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001296 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001297 continue;
1298 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001299 if (moSpan.fOther == this) {
1300 if (moSpan.fOtherT == match->fT) {
1301 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001302 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001303 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001304 continue;
1305 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001306 if (moSpan.fOther == tOther) {
1307 SkASSERT(moEnd == -1);
1308 moEnd = moIndex;
1309 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001310 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001311 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001312 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001313 continue;
1314 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001315 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1316 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001317 continue;
1318 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001319 int toStart = -1;
1320 int toEnd = -1;
1321 double toStartT, toEndT;
1322 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1323 Span& toSpan = tOther->fTs[toIndex];
1324 if (toSpan.fOther == this) {
1325 if (toSpan.fOtherT == test->fT) {
1326 toStart = toIndex;
1327 toStartT = toSpan.fT;
1328 }
1329 continue;
1330 }
1331 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1332 SkASSERT(toEnd == -1);
1333 toEnd = toIndex;
1334 toEndT = toSpan.fT;
1335 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001336 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001337 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1338 if (toStart <= 0 || toEnd <= 0) {
1339 continue;
1340 }
1341 if (toStartT == toEndT) {
1342 continue;
1343 }
1344 // test to see if the segment between there and here is linear
1345 if (!mOther->isLinear(moStart, moEnd)
1346 || !tOther->isLinear(toStart, toEnd)) {
1347 continue;
1348 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001349 // FIXME: defer implementation until the rest works
1350 // this may share code with regular coincident detection
1351 SkASSERT(0);
1352 #if 0
1353 if (flipped) {
1354 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1355 } else {
1356 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1357 }
1358 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001359 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001360 }
1361
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001362 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1363 // and use more concise logic like the old edge walker code?
1364 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001365 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001366 // iterate through T intersections and return topmost
1367 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001368 SkASSERT(!done());
1369 int firstT;
1370 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001371 SkPoint topPt;
1372 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001373 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001374 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001375 bool lastDone = true;
1376 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001377 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001378 if (!span.fDone || !lastDone) {
1379 const SkPoint& intercept = xyAtT(&span);
1380 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1381 && topPt.fX > intercept.fX)) {
1382 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001383 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001384 } else if (topPt == intercept) {
1385 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001386 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001387 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001388 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001389 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001390 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001391 int step = 1;
1392 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001393 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001394 step = -1;
1395 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001396 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001397 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001398 // if the topmost T is not on end, or is three-way or more, find left
1399 // look for left-ness from tLeft to firstT (matching y of other)
1400 SkTDArray<Angle> angles;
1401 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001402 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001403 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001404 SkTDArray<Angle*> sorted;
1405 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001406 // skip edges that have already been processed
1407 firstT = -1;
1408 Segment* leftSegment;
1409 do {
1410 const Angle* angle = sorted[++firstT];
1411 leftSegment = angle->segment();
1412 tIndex = angle->end();
1413 endIndex = angle->start();
1414 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001415 return leftSegment;
1416 }
1417
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001418 // FIXME: not crazy about this
1419 // when the intersections are performed, the other index is into an
1420 // incomplete array. as the array grows, the indices become incorrect
1421 // while the following fixes the indices up again, it isn't smart about
1422 // skipping segments whose indices are already correct
1423 // assuming we leave the code that wrote the index in the first place
1424 void fixOtherTIndex() {
1425 int iCount = fTs.count();
1426 for (int i = 0; i < iCount; ++i) {
1427 Span& iSpan = fTs[i];
1428 double oT = iSpan.fOtherT;
1429 Segment* other = iSpan.fOther;
1430 int oCount = other->fTs.count();
1431 for (int o = 0; o < oCount; ++o) {
1432 Span& oSpan = other->fTs[o];
1433 if (oT == oSpan.fT && this == oSpan.fOther) {
1434 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001435 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001436 }
1437 }
1438 }
1439 }
1440
caryclark@google.com495f8e42012-05-31 13:13:11 +00001441 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001442 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001443 int end = nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001444 if (multipleSpans(index, end)) {
1445 return index >= 0 ? &fTs[index] : NULL;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001446 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001447 const Span& endSpan = fTs[end];
1448 Segment* other = endSpan.fOther;
1449 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001450 int otherEnd = other->nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001451 Span* last = other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001452 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001453 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001454 }
1455
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001456 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001457 int end = nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001458 if (multipleSpans(index, end)) {
1459 return index >= 0 ? &fTs[index] : NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001460 }
1461 const Span& endSpan = fTs[end];
1462 Segment* other = endSpan.fOther;
1463 index = endSpan.fOtherIndex;
1464 int otherEnd = other->nextSpan(index, step);
1465 int min = SkMin32(index, otherEnd);
1466 if (other->fTs[min].fWindSum != SK_MinS32) {
1467 SkASSERT(other->fTs[index].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001468 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001469 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001470 Span* last = other->innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001471 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001472 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001473 }
1474
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001475 void init(const SkPoint pts[], SkPath::Verb verb) {
1476 fPts = pts;
1477 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001478 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001479 }
1480
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001481 bool intersected() const {
1482 return fTs.count() > 0;
1483 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001484
1485 bool isLinear(int start, int end) const {
1486 if (fVerb == SkPath::kLine_Verb) {
1487 return true;
1488 }
1489 if (fVerb == SkPath::kQuad_Verb) {
1490 SkPoint qPart[3];
1491 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1492 return QuadIsLinear(qPart);
1493 } else {
1494 SkASSERT(fVerb == SkPath::kCubic_Verb);
1495 SkPoint cPart[4];
1496 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1497 return CubicIsLinear(cPart);
1498 }
1499 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001500
1501 // OPTIMIZE: successive calls could start were the last leaves off
1502 // or calls could specialize to walk forwards or backwards
1503 bool isMissing(double startT) const {
1504 size_t tCount = fTs.count();
1505 for (size_t index = 0; index < tCount; ++index) {
1506 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1507 return false;
1508 }
1509 }
1510 return true;
1511 }
1512
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001513 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001514 int count = fTs.count();
1515 if (count == 2) {
1516 return true;
1517 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001518 double t = fTs[end].fT;
1519 if (t < FLT_EPSILON) {
1520 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001521 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001522 if (t > 1 - FLT_EPSILON) {
1523 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001524 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001525 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001526 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001527
1528 bool isHorizontal() const {
1529 return fBounds.fTop == fBounds.fBottom;
1530 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001531
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001532 bool isVertical() const {
1533 return fBounds.fLeft == fBounds.fRight;
1534 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001535
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001536 SkScalar leftMost(int start, int end) const {
1537 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1538 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001539
caryclark@google.com495f8e42012-05-31 13:13:11 +00001540 // this span is excluded by the winding rule -- chase the ends
1541 // as long as they are unambiguous to mark connections as done
1542 // and give them the same winding value
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001543 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001544 int index = angle->start();
1545 int endIndex = angle->end();
1546 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001547 Span* last = innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001548 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001549 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001550 }
1551
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001552 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 int index = angle->start();
1554 int endIndex = angle->end();
1555 int min = SkMin32(index, endIndex);
1556 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001557 Span* last = innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001558 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001559 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001560 }
1561
caryclark@google.com495f8e42012-05-31 13:13:11 +00001562 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001563 // This may be called when the segment is already marked done. While this
1564 // wastes time, it shouldn't do any more than spin through the T spans.
1565 // OPTIMIZATION: abort on first done found (assuming that this code is
1566 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001567 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001568 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001569 double referenceT = fTs[index].fT;
1570 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001571 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001572 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001573 if (span.fDone) {
1574 continue;
1575 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001576 #if DEBUG_MARK_DONE
1577 const SkPoint& pt = xyAtT(&span);
1578 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1579 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1580 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001581 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001582 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1583 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001584 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001585 }
1586 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001587 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001588 // SkASSERT(!span.fDone);
1589 if (span.fDone) {
1590 continue;
1591 }
1592 #if DEBUG_MARK_DONE
1593 const SkPoint& pt = xyAtT(&span);
1594 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1595 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1596 #endif
1597 span.fDone = true;
1598 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1599 span.fWindSum = winding;
1600 fDoneSpans++;
1601 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
1602 }
1603
1604 void markWinding(int index, int winding) {
1605 SkASSERT(!done());
1606 double referenceT = fTs[index].fT;
1607 int lesser = index;
1608 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
1609 Span& span = fTs[lesser];
1610 if (span.fDone) {
1611 continue;
1612 }
1613 SkASSERT(span.fWindValue == 1 || winding == 0);
1614 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1615 #if DEBUG_MARK_DONE
1616 const SkPoint& pt = xyAtT(&span);
1617 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1618 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1619 #endif
1620 span.fWindSum = winding;
1621 }
1622 do {
1623 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001624 // SkASSERT(!span.fDone || span.fCoincident);
1625 if (span.fDone) {
1626 continue;
1627 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001628 SkASSERT(span.fWindValue == 1 || winding == 0);
1629 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1630 #if DEBUG_MARK_DONE
1631 const SkPoint& pt = xyAtT(&span);
1632 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1633 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1634 #endif
1635 span.fWindSum = winding;
1636 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001637 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001638
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001639 bool multipleSpans(int& index, int end) const {
1640 if (end > index ? end + 1 >= fTs.count() : end <= 0) {
1641 return false;
1642 }
1643 // return span if when chasing, two or more radiating spans are not done
1644 int lesser = SkMin32(index, end);
1645 if (!activeAngles(lesser)) {
1646 index = -1;
1647 }
1648 index = lesser;
1649 return true;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001650 }
1651
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001652 // This has callers for two different situations: one establishes the end
1653 // of the current span, and one establishes the beginning of the next span
1654 // (thus the name). When this is looking for the end of the current span,
1655 // coincidence is found when the beginning Ts contain -step and the end
1656 // contains step. When it is looking for the beginning of the next, the
1657 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001658 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001659 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001660 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001661 int count = fTs.count();
1662 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001663 while (step > 0 ? ++to < count : --to >= 0) {
1664 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001665 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001666 continue;
1667 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001668 return to;
1669 }
1670 return -1;
1671 }
1672
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001673 const SkPoint* pts() const {
1674 return fPts;
1675 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001676
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001677 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001678 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001679 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1680 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001681 }
1682
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001683 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001684 const Span& span(int tIndex) const {
1685 return fTs[tIndex];
1686 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001687
1688 int spanSign(int startIndex, int endIndex) const {
1689 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
1690 fTs[endIndex].fWindValue;
1691 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001692
1693 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001694 double t(int tIndex) const {
1695 return fTs[tIndex].fT;
1696 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001697
1698 void updatePts(const SkPoint pts[]) {
1699 fPts = pts;
1700 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001701
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001702 SkPath::Verb verb() const {
1703 return fVerb;
1704 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001705
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001706 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001707 return fTs[tIndex].fWindSum;
1708 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001709
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001710 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001711 int start = angle->start();
1712 int end = angle->end();
1713 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001714 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001715 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001716
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001717 int windValue(int tIndex) const {
1718 return fTs[tIndex].fWindValue;
1719 }
1720
1721 int windValue(const Angle* angle) const {
1722 int start = angle->start();
1723 int end = angle->end();
1724 int index = SkMin32(start, end);
1725 return windValue(index);
1726 }
1727
1728 SkScalar xAtT(const Span* span) const {
1729 return xyAtT(span).fX;
1730 }
1731
1732 const SkPoint& xyAtT(int index) const {
1733 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001734 }
1735
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001736 const SkPoint& xyAtT(const Span* span) const {
1737 if (!span->fPt) {
1738 if (span->fT == 0) {
1739 span->fPt = &fPts[0];
1740 } else if (span->fT == 1) {
1741 span->fPt = &fPts[fVerb];
1742 } else {
1743 SkPoint* pt = fIntersections.append();
1744 (*SegmentXYAtT[fVerb])(fPts, span->fT, pt);
1745 span->fPt = pt;
1746 }
1747 }
1748 return *span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001749 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001750
1751 SkScalar yAtT(int index) const {
1752 return yAtT(&fTs[index]);
1753 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001754
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001755 SkScalar yAtT(const Span* span) const {
1756 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001757 }
1758
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001759#if DEBUG_DUMP
1760 void dump() const {
1761 const char className[] = "Segment";
1762 const int tab = 4;
1763 for (int i = 0; i < fTs.count(); ++i) {
1764 SkPoint out;
1765 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1766 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001767 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001768 tab + sizeof(className), className, fID,
1769 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001770 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001771 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001772 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001773 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001774 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001775 }
1776#endif
1777
caryclark@google.com027de222012-07-12 12:52:50 +00001778#if DEBUG_ACTIVE_SPANS
1779 void debugShowActiveSpans(int contourID, int segmentIndex) {
1780 if (done()) {
1781 return;
1782 }
1783 for (int i = 0; i < fTs.count(); ++i) {
1784 if (fTs[i].fDone) {
1785 continue;
1786 }
1787 SkDebugf("%s contour=%d segment=%d (%d)", __FUNCTION__, contourID,
1788 segmentIndex, fID);
1789 SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
1790 for (int vIndex = 1; vIndex <= fVerb; ++vIndex) {
1791 SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
1792 }
1793 const Span* span = &fTs[i];
1794 SkDebugf(") fT=%d (%1.9g) (%1.9g,%1.9g)", i, fTs[i].fT,
1795 xAtT(span), yAtT(i));
1796 const Segment* other = fTs[i].fOther;
1797 SkDebugf(" other=%d otherT=%1.9g otherIndex=%d", other->fID,
1798 fTs[i].fOtherT, fTs[i].fOtherIndex);
1799 SkDebugf(" windSum=%d windValue=%d\n", fTs[i].fWindSum,
1800 fTs[i].fWindValue);
1801 }
1802 }
1803#endif
1804
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001805private:
1806 const SkPoint* fPts;
1807 SkPath::Verb fVerb;
1808 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001809 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001810 // OPTIMIZATION:if intersections array is a pointer, the it could only
1811 // be allocated as needed instead of always initialized -- though maybe
1812 // the initialization is lightweight enough that it hardly matters
1813 mutable SkTDArray<SkPoint> fIntersections;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001814 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001815#if DEBUG_DUMP
1816 int fID;
1817#endif
1818};
1819
caryclark@google.comb9738012012-07-03 19:53:30 +00001820class Contour;
1821
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001822struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00001823 Contour* fContours[2];
1824 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001825 double fTs[2][2];
1826};
1827
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001828class Contour {
1829public:
1830 Contour() {
1831 reset();
1832#if DEBUG_DUMP
1833 fID = ++gContourID;
1834#endif
1835 }
1836
1837 bool operator<(const Contour& rh) const {
1838 return fBounds.fTop == rh.fBounds.fTop
1839 ? fBounds.fLeft < rh.fBounds.fLeft
1840 : fBounds.fTop < rh.fBounds.fTop;
1841 }
1842
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001843 void addCoincident(int index, Contour* other, int otherIndex,
1844 const Intersections& ts, bool swap) {
1845 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00001846 coincidence.fContours[0] = this;
1847 coincidence.fContours[1] = other;
1848 coincidence.fSegments[0] = index;
1849 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001850 coincidence.fTs[swap][0] = ts.fT[0][0];
1851 coincidence.fTs[swap][1] = ts.fT[0][1];
1852 coincidence.fTs[!swap][0] = ts.fT[1][0];
1853 coincidence.fTs[!swap][1] = ts.fT[1][1];
1854 }
1855
1856 void addCross(const Contour* crosser) {
1857#ifdef DEBUG_CROSS
1858 for (int index = 0; index < fCrosses.count(); ++index) {
1859 SkASSERT(fCrosses[index] != crosser);
1860 }
1861#endif
1862 *fCrosses.append() = crosser;
1863 }
1864
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001865 void addCubic(const SkPoint pts[4]) {
1866 fSegments.push_back().addCubic(pts);
1867 fContainsCurves = true;
1868 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001869
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001870 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001871 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001872 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001873 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001874
1875 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
1876 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
1877 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001878
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001879 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001880 fSegments.push_back().addQuad(pts);
1881 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001882 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001883 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001884
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001885 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
1886 containsIntercepts();
1887 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
1888 }
1889
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001890 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001891 return fBounds;
1892 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001893
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001894 void complete() {
1895 setBounds();
1896 fContainsIntercepts = false;
1897 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001898
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001899 void containsIntercepts() {
1900 fContainsIntercepts = true;
1901 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001902
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001903 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
1904 int &tIndex, double& hitT) {
1905 int segmentCount = fSegments.count();
1906 const Segment* bestSegment = NULL;
1907 for (int test = 0; test < segmentCount; ++test) {
1908 Segment* testSegment = &fSegments[test];
1909 const SkRect& bounds = testSegment->bounds();
1910 if (bounds.fTop < bestY) {
1911 continue;
1912 }
1913 if (bounds.fTop > basePt.fY) {
1914 continue;
1915 }
1916 if (bounds.fLeft > basePt.fX) {
1917 continue;
1918 }
1919 if (bounds.fRight < basePt.fX) {
1920 continue;
1921 }
1922 double testHitT;
1923 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
1924 if (testT >= 0) {
1925 bestSegment = testSegment;
1926 tIndex = testT;
1927 hitT = testHitT;
1928 }
1929 }
1930 return bestSegment;
1931 }
1932
1933 bool crosses(const Contour* crosser) const {
1934 if (this == crosser) {
1935 return true;
1936 }
1937 for (int index = 0; index < fCrosses.count(); ++index) {
1938 if (fCrosses[index] == crosser) {
1939 return true;
1940 }
1941 }
1942 return false;
1943 }
1944
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001945 void findTooCloseToCall(int winding) {
1946 int segmentCount = fSegments.count();
1947 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1948 fSegments[sIndex].findTooCloseToCall(winding);
1949 }
1950 }
1951
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001952 void fixOtherTIndex() {
1953 int segmentCount = fSegments.count();
1954 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1955 fSegments[sIndex].fixOtherTIndex();
1956 }
1957 }
1958
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001959 void reset() {
1960 fSegments.reset();
1961 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001962 fContainsCurves = fContainsIntercepts = false;
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00001963 fWindingSum = SK_MinS32;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001964 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001965
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001966 void resolveCoincidence(int winding) {
1967 int count = fCoincidences.count();
1968 for (int index = 0; index < count; ++index) {
1969 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00001970 Contour* thisContour = coincidence.fContours[0];
1971 Contour* otherContour = coincidence.fContours[1];
1972 int thisIndex = coincidence.fSegments[0];
1973 int otherIndex = coincidence.fSegments[1];
1974 Segment& thisOne = thisContour->fSegments[thisIndex];
1975 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001976 double startT = coincidence.fTs[0][0];
1977 double endT = coincidence.fTs[0][1];
1978 if (startT > endT) {
1979 SkTSwap<double>(startT, endT);
1980 }
1981 SkASSERT(endT - startT >= FLT_EPSILON);
1982 double oStartT = coincidence.fTs[1][0];
1983 double oEndT = coincidence.fTs[1][1];
1984 if (oStartT > oEndT) {
1985 SkTSwap<double>(oStartT, oEndT);
1986 }
1987 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.comb9738012012-07-03 19:53:30 +00001988 if (winding > 0 || thisOne.cancels(other)) {
1989 // make sure startT and endT have t entries
1990 if (thisOne.isMissing(startT) || other.isMissing(oEndT)) {
1991 thisOne.addTPair(startT, other, oEndT);
1992 }
1993 if (thisOne.isMissing(endT) || other.isMissing(oStartT)) {
1994 other.addTPair(oStartT, thisOne, endT);
1995 }
1996 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001997 } else {
caryclark@google.comb9738012012-07-03 19:53:30 +00001998 if (thisOne.isMissing(startT) || other.isMissing(oStartT)) {
1999 thisOne.addTPair(startT, other, oStartT);
2000 }
2001 if (thisOne.isMissing(endT) || other.isMissing(oEndT)) {
2002 other.addTPair(oEndT, thisOne, endT);
2003 }
2004 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002005 }
2006 }
2007 }
2008
2009 const SkTArray<Segment>& segments() {
2010 return fSegments;
2011 }
2012
2013 void setWinding(int winding) {
2014 SkASSERT(fWindingSum < 0);
2015 fWindingSum = winding;
2016 }
2017
caryclark@google.com15fa1382012-05-07 20:49:36 +00002018 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
2019 // we need to sort and walk edges in y, but that on the surface opens the
2020 // same can of worms as before. But then, this is a rough sort based on
2021 // segments' top, and not a true sort, so it could be ameniable to regular
2022 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002023 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00002024 int segmentCount = fSegments.count();
2025 SkASSERT(segmentCount > 0);
2026 int best = -1;
2027 Segment* bestSegment = NULL;
2028 while (++best < segmentCount) {
2029 Segment* testSegment = &fSegments[best];
2030 if (testSegment->done()) {
2031 continue;
2032 }
2033 bestSegment = testSegment;
2034 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002035 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002036 if (!bestSegment) {
2037 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002038 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002039 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002040 for (int test = best + 1; test < segmentCount; ++test) {
2041 Segment* testSegment = &fSegments[test];
2042 if (testSegment->done()) {
2043 continue;
2044 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002045 if (testSegment->bounds().fTop > bestTop) {
2046 continue;
2047 }
2048 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002049 if (bestTop > testTop) {
2050 bestTop = testTop;
2051 bestSegment = testSegment;
2052 }
2053 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002054 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002055 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002056 }
2057
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002058 int updateSegment(int index, const SkPoint* pts) {
2059 Segment& segment = fSegments[index];
2060 segment.updatePts(pts);
2061 return segment.verb() + 1;
2062 }
2063
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002064 int windSum() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002065 if (fWindingSum >= 0) {
2066 return fWindingSum;
2067 }
2068 // check peers
2069 int count = fCrosses.count();
2070 for (int index = 0; index < count; ++index) {
2071 const Contour* crosser = fCrosses[index];
2072 if (0 <= crosser->fWindingSum) {
2073 fWindingSum = crosser->fWindingSum;
2074 break;
2075 }
2076 }
2077 return fWindingSum;
2078 }
2079
2080#if DEBUG_TEST
2081 SkTArray<Segment>& debugSegments() {
2082 return fSegments;
2083 }
2084#endif
2085
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002086#if DEBUG_DUMP
2087 void dump() {
2088 int i;
2089 const char className[] = "Contour";
2090 const int tab = 4;
2091 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2092 for (i = 0; i < fSegments.count(); ++i) {
2093 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2094 className, i);
2095 fSegments[i].dump();
2096 }
2097 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2098 tab + sizeof(className), className,
2099 fBounds.fLeft, fBounds.fTop,
2100 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002101 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2102 className, fContainsIntercepts);
2103 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2104 className, fContainsCurves);
2105 }
2106#endif
2107
caryclark@google.com027de222012-07-12 12:52:50 +00002108#if DEBUG_ACTIVE_SPANS
2109 void debugShowActiveSpans() {
2110 for (int index = 0; index < fSegments.count(); ++index) {
2111 fSegments[index].debugShowActiveSpans(fID, index);
2112 }
2113 }
2114#endif
2115
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002116protected:
2117 void setBounds() {
2118 int count = fSegments.count();
2119 if (count == 0) {
2120 SkDebugf("%s empty contour\n", __FUNCTION__);
2121 SkASSERT(0);
2122 // FIXME: delete empty contour?
2123 return;
2124 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002125 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002126 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002127 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002128 }
2129 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002130
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002131private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002132 SkTArray<Segment> fSegments;
2133 SkTDArray<Coincidence> fCoincidences;
2134 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002135 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002136 bool fContainsIntercepts;
2137 bool fContainsCurves;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002138 int fWindingSum; // initial winding number outside
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002139#if DEBUG_DUMP
2140 int fID;
2141#endif
2142};
2143
2144class EdgeBuilder {
2145public:
2146
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002147EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002148 : fPath(path)
2149 , fCurrentContour(NULL)
2150 , fContours(contours)
2151{
2152#if DEBUG_DUMP
2153 gContourID = 0;
2154 gSegmentID = 0;
2155#endif
2156 walk();
2157}
2158
2159protected:
2160
2161void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002162 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002163 fCurrentContour->complete();
2164 fCurrentContour = NULL;
2165 }
2166}
2167
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002168void walk() {
2169 // FIXME:remove once we can access path pts directly
2170 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2171 SkPoint pts[4];
2172 SkPath::Verb verb;
2173 do {
2174 verb = iter.next(pts);
2175 *fPathVerbs.append() = verb;
2176 if (verb == SkPath::kMove_Verb) {
2177 *fPathPts.append() = pts[0];
2178 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2179 fPathPts.append(verb, &pts[1]);
2180 }
2181 } while (verb != SkPath::kDone_Verb);
2182 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002183
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002184 SkPath::Verb reducedVerb;
2185 uint8_t* verbPtr = fPathVerbs.begin();
2186 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002187 const SkPoint* finalCurveStart = NULL;
2188 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002189 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2190 switch (verb) {
2191 case SkPath::kMove_Verb:
2192 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002193 if (!fCurrentContour) {
2194 fCurrentContour = fContours.push_back_n(1);
2195 finalCurveEnd = pointsPtr++;
2196 *fExtra.append() = -1; // start new contour
2197 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002198 continue;
2199 case SkPath::kLine_Verb:
2200 // skip degenerate points
2201 if (pointsPtr[-1].fX != pointsPtr[0].fX
2202 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2203 fCurrentContour->addLine(&pointsPtr[-1]);
2204 }
2205 break;
2206 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002207
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002208 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2209 if (reducedVerb == 0) {
2210 break; // skip degenerate points
2211 }
2212 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002213 *fExtra.append() =
2214 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002215 break;
2216 }
2217 fCurrentContour->addQuad(&pointsPtr[-1]);
2218 break;
2219 case SkPath::kCubic_Verb:
2220 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2221 if (reducedVerb == 0) {
2222 break; // skip degenerate points
2223 }
2224 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002225 *fExtra.append() =
2226 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002227 break;
2228 }
2229 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002230 *fExtra.append() =
2231 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002232 break;
2233 }
2234 fCurrentContour->addCubic(&pointsPtr[-1]);
2235 break;
2236 case SkPath::kClose_Verb:
2237 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002238 if (finalCurveStart && finalCurveEnd
2239 && *finalCurveStart != *finalCurveEnd) {
2240 *fReducePts.append() = *finalCurveStart;
2241 *fReducePts.append() = *finalCurveEnd;
2242 *fExtra.append() =
2243 fCurrentContour->addLine(fReducePts.end() - 2);
2244 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002245 complete();
2246 continue;
2247 default:
2248 SkDEBUGFAIL("bad verb");
2249 return;
2250 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002251 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002252 pointsPtr += verb;
2253 SkASSERT(fCurrentContour);
2254 }
2255 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002256 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002257 fContours.pop_back();
2258 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002259 // correct pointers in contours since fReducePts may have moved as it grew
2260 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002261 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002262 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002263 int eIndex = 0;
2264 int rIndex = 0;
2265 while (++eIndex < extraCount) {
2266 int offset = fExtra[eIndex];
2267 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002268 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002269 continue;
2270 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002271 fCurrentContour = &fContours[cIndex];
2272 rIndex += fCurrentContour->updateSegment(offset - 1,
2273 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002274 }
2275 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002276}
2277
2278private:
2279 const SkPath& fPath;
2280 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002281 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002282 Contour* fCurrentContour;
2283 SkTArray<Contour>& fContours;
2284 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002285 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002286};
2287
2288class Work {
2289public:
2290 enum SegmentType {
2291 kHorizontalLine_Segment = -1,
2292 kVerticalLine_Segment = 0,
2293 kLine_Segment = SkPath::kLine_Verb,
2294 kQuad_Segment = SkPath::kQuad_Verb,
2295 kCubic_Segment = SkPath::kCubic_Verb,
2296 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002297
2298 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2299 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2300 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002301
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002302 // FIXME: does it make sense to write otherIndex now if we're going to
2303 // fix it up later?
2304 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002305 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002306 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002307
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002308 // Avoid collapsing t values that are close to the same since
2309 // we walk ts to describe consecutive intersections. Since a pair of ts can
2310 // be nearly equal, any problems caused by this should be taken care
2311 // of later.
2312 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002313 int addT(double newT, const Work& other) {
2314 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002315 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002316
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002317 bool advance() {
2318 return ++fIndex < fLast;
2319 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002320
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002321 SkScalar bottom() const {
2322 return bounds().fBottom;
2323 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002324
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002325 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002326 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002327 }
2328
2329 const SkPoint* cubic() const {
2330 return fCubic;
2331 }
2332
2333 void init(Contour* contour) {
2334 fContour = contour;
2335 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002336 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002337 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002338
2339 bool isAdjacent(const Work& next) {
2340 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2341 }
2342
2343 bool isFirstLast(const Work& next) {
2344 return fContour == next.fContour && fIndex == 0
2345 && next.fIndex == fLast - 1;
2346 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002347
2348 SkScalar left() const {
2349 return bounds().fLeft;
2350 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002351
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002352 void promoteToCubic() {
2353 fCubic[0] = pts()[0];
2354 fCubic[2] = pts()[1];
2355 fCubic[3] = pts()[2];
2356 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2357 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2358 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2359 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2360 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002361
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002362 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002363 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002364 }
2365
2366 SkScalar right() const {
2367 return bounds().fRight;
2368 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002369
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002370 ptrdiff_t segmentIndex() const {
2371 return fIndex;
2372 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002373
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002374 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002375 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002376 SegmentType type = (SegmentType) segment.verb();
2377 if (type != kLine_Segment) {
2378 return type;
2379 }
2380 if (segment.isHorizontal()) {
2381 return kHorizontalLine_Segment;
2382 }
2383 if (segment.isVertical()) {
2384 return kVerticalLine_Segment;
2385 }
2386 return kLine_Segment;
2387 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002388
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002389 bool startAfter(const Work& after) {
2390 fIndex = after.fIndex;
2391 return advance();
2392 }
2393
2394 SkScalar top() const {
2395 return bounds().fTop;
2396 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002397
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002398 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002399 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002400 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002401
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002402 SkScalar x() const {
2403 return bounds().fLeft;
2404 }
2405
2406 bool xFlipped() const {
2407 return x() != pts()[0].fX;
2408 }
2409
2410 SkScalar y() const {
2411 return bounds().fTop;
2412 }
2413
2414 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002415 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002416 }
2417
2418protected:
2419 Contour* fContour;
2420 SkPoint fCubic[4];
2421 int fIndex;
2422 int fLast;
2423};
2424
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002425#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002426static void debugShowLineIntersection(int pts, const Work& wt,
2427 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002428 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002429 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2430 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2431 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2432 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002433 return;
2434 }
2435 SkPoint wtOutPt, wnOutPt;
2436 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2437 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002438 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002439 __FUNCTION__,
2440 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2441 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2442 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002443 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002444 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002445 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002446 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2447 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2448 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002449 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002450 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002451 SkDebugf("\n");
2452}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002453#else
2454static void debugShowLineIntersection(int , const Work& ,
2455 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002456}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002457#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002458
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002459static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002460
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002461 if (test != next) {
2462 if (test->bounds().fBottom < next->bounds().fTop) {
2463 return false;
2464 }
2465 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2466 return true;
2467 }
2468 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002469 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002470 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002471 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002472 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002473 Work wn;
2474 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002475 if (test == next && !wn.startAfter(wt)) {
2476 continue;
2477 }
2478 do {
2479 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2480 continue;
2481 }
2482 int pts;
2483 Intersections ts;
2484 bool swap = false;
2485 switch (wt.segmentType()) {
2486 case Work::kHorizontalLine_Segment:
2487 swap = true;
2488 switch (wn.segmentType()) {
2489 case Work::kHorizontalLine_Segment:
2490 case Work::kVerticalLine_Segment:
2491 case Work::kLine_Segment: {
2492 pts = HLineIntersect(wn.pts(), wt.left(),
2493 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002494 debugShowLineIntersection(pts, wt, wn,
2495 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002496 break;
2497 }
2498 case Work::kQuad_Segment: {
2499 pts = HQuadIntersect(wn.pts(), wt.left(),
2500 wt.right(), wt.y(), wt.xFlipped(), ts);
2501 break;
2502 }
2503 case Work::kCubic_Segment: {
2504 pts = HCubicIntersect(wn.pts(), wt.left(),
2505 wt.right(), wt.y(), wt.xFlipped(), ts);
2506 break;
2507 }
2508 default:
2509 SkASSERT(0);
2510 }
2511 break;
2512 case Work::kVerticalLine_Segment:
2513 swap = true;
2514 switch (wn.segmentType()) {
2515 case Work::kHorizontalLine_Segment:
2516 case Work::kVerticalLine_Segment:
2517 case Work::kLine_Segment: {
2518 pts = VLineIntersect(wn.pts(), wt.top(),
2519 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002520 debugShowLineIntersection(pts, wt, wn,
2521 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002522 break;
2523 }
2524 case Work::kQuad_Segment: {
2525 pts = VQuadIntersect(wn.pts(), wt.top(),
2526 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2527 break;
2528 }
2529 case Work::kCubic_Segment: {
2530 pts = VCubicIntersect(wn.pts(), wt.top(),
2531 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2532 break;
2533 }
2534 default:
2535 SkASSERT(0);
2536 }
2537 break;
2538 case Work::kLine_Segment:
2539 switch (wn.segmentType()) {
2540 case Work::kHorizontalLine_Segment:
2541 pts = HLineIntersect(wt.pts(), wn.left(),
2542 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002543 debugShowLineIntersection(pts, wt, wn,
2544 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002545 break;
2546 case Work::kVerticalLine_Segment:
2547 pts = VLineIntersect(wt.pts(), wn.top(),
2548 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002549 debugShowLineIntersection(pts, wt, wn,
2550 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002551 break;
2552 case Work::kLine_Segment: {
2553 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2554 debugShowLineIntersection(pts, wt, wn,
2555 ts.fT[1], ts.fT[0]);
2556 break;
2557 }
2558 case Work::kQuad_Segment: {
2559 swap = true;
2560 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2561 break;
2562 }
2563 case Work::kCubic_Segment: {
2564 swap = true;
2565 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2566 break;
2567 }
2568 default:
2569 SkASSERT(0);
2570 }
2571 break;
2572 case Work::kQuad_Segment:
2573 switch (wn.segmentType()) {
2574 case Work::kHorizontalLine_Segment:
2575 pts = HQuadIntersect(wt.pts(), wn.left(),
2576 wn.right(), wn.y(), wn.xFlipped(), ts);
2577 break;
2578 case Work::kVerticalLine_Segment:
2579 pts = VQuadIntersect(wt.pts(), wn.top(),
2580 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2581 break;
2582 case Work::kLine_Segment: {
2583 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2584 break;
2585 }
2586 case Work::kQuad_Segment: {
2587 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2588 break;
2589 }
2590 case Work::kCubic_Segment: {
2591 wt.promoteToCubic();
2592 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2593 break;
2594 }
2595 default:
2596 SkASSERT(0);
2597 }
2598 break;
2599 case Work::kCubic_Segment:
2600 switch (wn.segmentType()) {
2601 case Work::kHorizontalLine_Segment:
2602 pts = HCubicIntersect(wt.pts(), wn.left(),
2603 wn.right(), wn.y(), wn.xFlipped(), ts);
2604 break;
2605 case Work::kVerticalLine_Segment:
2606 pts = VCubicIntersect(wt.pts(), wn.top(),
2607 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2608 break;
2609 case Work::kLine_Segment: {
2610 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2611 break;
2612 }
2613 case Work::kQuad_Segment: {
2614 wn.promoteToCubic();
2615 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2616 break;
2617 }
2618 case Work::kCubic_Segment: {
2619 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2620 break;
2621 }
2622 default:
2623 SkASSERT(0);
2624 }
2625 break;
2626 default:
2627 SkASSERT(0);
2628 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002629 if (!foundCommonContour && pts > 0) {
2630 test->addCross(next);
2631 next->addCross(test);
2632 foundCommonContour = true;
2633 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002634 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002635 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
2636 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002637 wt.addCoincident(wn, ts, swap);
2638 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002639 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002640 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002641 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2642 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002643 int testTAt = wt.addT(ts.fT[swap][pt], wn);
2644 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002645 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
2646 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002647 }
2648 } while (wn.advance());
2649 } while (wt.advance());
2650 return true;
2651}
2652
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002653// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002654// see if coincidence is formed by clipping non-concident segments
2655static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
2656 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00002657 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002658 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002659 contour->findTooCloseToCall(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002660 }
2661 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
2662 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002663 contour->resolveCoincidence(winding);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002664 }
2665}
2666
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002667// project a ray from the top of the contour up and see if it hits anything
2668// note: when we compute line intersections, we keep track of whether
2669// two contours touch, so we need only look at contours not touching this one.
2670// OPTIMIZATION: sort contourList vertically to avoid linear walk
2671static int innerContourCheck(SkTDArray<Contour*>& contourList,
2672 Contour* baseContour, const SkPoint& basePt) {
2673 int contourCount = contourList.count();
2674 int winding = 0;
2675 SkScalar bestY = SK_ScalarMin;
2676 for (int cTest = 0; cTest < contourCount; ++cTest) {
2677 Contour* contour = contourList[cTest];
2678 if (basePt.fY < contour->bounds().fTop) {
2679 continue;
2680 }
2681 if (bestY > contour->bounds().fBottom) {
2682 continue;
2683 }
2684 if (baseContour->crosses(contour)) {
2685 continue;
2686 }
2687 int tIndex;
2688 double tHit;
2689 const Segment* test = contour->crossedSegment(basePt, bestY, tIndex,
2690 tHit);
2691 if (!test) {
2692 continue;
2693 }
2694 // If the ray hit the end of a span, we need to construct the wheel of
2695 // angles to find the span closest to the ray -- even if there are just
2696 // two spokes on the wheel.
2697 if (tHit == test->t(tIndex)) {
2698 SkTDArray<Angle> angles;
2699 int end = test->nextSpan(tIndex, 1);
2700 if (end < 0) {
2701 end = test->nextSpan(tIndex, -1);
2702 }
2703 test->addTwoAngles(tIndex, end, angles);
2704 // test->buildAnglesInner(tIndex, angles);
2705 test->buildAngles(tIndex, angles);
2706 SkTDArray<Angle*> sorted;
2707 sortAngles(angles, sorted);
2708 const Angle* angle = sorted[0];
2709 test = angle->segment();
2710 SkScalar testDx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2711 if (testDx == 0) {
2712 angle = *(sorted.end() - 1);
2713 test = angle->segment();
2714 SkASSERT((*SegmentDXAtT[test->verb()])(test->pts(), tHit) != 0);
2715 }
2716 tIndex = angle->start(); // lesser Y
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002717 winding = test->windSum(SkMin32(tIndex, angle->end()));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002718 #if DEBUG_WINDING
2719 SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
2720 #endif
2721 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002722 winding = test->windSum(tIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002723 #if DEBUG_WINDING
2724 SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
2725 #endif
2726 }
2727 // see if a + change in T results in a +/- change in X (compute x'(T))
2728 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2729 #if DEBUG_WINDING
2730 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
2731 #endif
2732 SkASSERT(dx != 0);
2733 if (winding * dx > 0) { // if same signs, result is negative
2734 winding += dx > 0 ? -1 : 1;
2735 #if DEBUG_WINDING
2736 SkDebugf("%s 3 winding=%d\n", __FUNCTION__, winding);
2737 #endif
2738 }
2739 }
2740 baseContour->setWinding(winding);
2741 return winding;
2742}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002743
2744// OPTIMIZATION: not crazy about linear search here to find top active y.
2745// seems like we should break down and do the sort, or maybe sort each
2746// contours' segments?
2747// Once the segment array is built, there's no reason I can think of not to
2748// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002749// FIXME: return the contour found to pass to inner contour check
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002750static Segment* findTopContour(SkTDArray<Contour*>& contourList,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002751 Contour*& topContour) {
2752 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002753 int cIndex = 0;
2754 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002755 SkScalar bestY = SK_ScalarMax;
2756 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002757 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002758 contour = contourList[cIndex];
2759 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002760 } while (!topStart && ++cIndex < contourCount);
2761 if (!topStart) {
2762 return NULL;
2763 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002764 topContour = contour;
2765 while (++cIndex < contourCount) {
2766 contour = contourList[cIndex];
2767 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002768 continue;
2769 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002770 SkScalar testY = SK_ScalarMax;
2771 Segment* test = contour->topSegment(testY);
2772 if (!test || bestY <= testY) {
2773 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002774 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002775 topContour = contour;
2776 topStart = test;
2777 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002778 }
2779 return topStart;
2780}
2781
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002782static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
2783 while (chase.count()) {
2784 Span* span;
2785 chase.pop(&span);
2786 const Span& backPtr = span->fOther->span(span->fOtherIndex);
2787 Segment* segment = backPtr.fOther;
2788 tIndex = backPtr.fOtherIndex;
2789 if (segment->activeAngles(tIndex)) {
2790 endIndex = segment->nextSpan(tIndex, 1);
2791 if (span->fDone) {
2792 SkTDArray<Angle> angles;
2793 segment->addTwoAngles(endIndex, tIndex, angles);
2794 segment->buildAngles(tIndex, angles);
2795 SkTDArray<Angle*> sorted;
2796 sortAngles(angles, sorted);
2797 // find first angle, initialize winding to computed fWindSum
2798 int winding = span->fWindSum;
2799 int firstIndex = segment->findStartingEdge(sorted, endIndex, tIndex);
2800 int firstSign = sorted[firstIndex]->sign();
2801 if (firstSign * winding > 0) {
2802 winding -= firstSign;
2803 }
2804 SkDebugf("%s firstSign=%d\n", __FUNCTION__, firstSign);
2805 // we care about first sign and whether wind sum indicates this
2806 // edge is inside or outside. Maybe need to pass span winding
2807 // or first winding or something into this function?
2808 SkASSERT(firstIndex >= 0);
2809 // advance to first undone angle, then return it and winding
2810 // (to set whether edges are active or not)
2811 int nextIndex = firstIndex + 1;
2812 int angleCount = sorted.count();
2813 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2814 do {
2815 SkASSERT(nextIndex != firstIndex);
2816 if (nextIndex == angleCount) {
2817 nextIndex = 0;
2818 }
2819 const Angle* angle = sorted[nextIndex];
2820 segment = angle->segment();
2821 int windValue = segment->windValue(angle);
2822 SkASSERT(windValue > 0);
2823 int maxWinding = winding;
2824 winding -= angle->sign() * windValue;
2825 if (maxWinding * winding < 0) {
2826 SkDebugf("%s flipped sign %d %d\n", __FUNCTION__, maxWinding, winding);
2827 }
2828 tIndex = angle->start();
2829 endIndex = angle->end();
2830 int lesser = SkMin32(tIndex, endIndex);
2831 const Span& nextSpan = segment->span(lesser);
2832 if (!nextSpan.fDone) {
2833 // FIXME: this be wrong. assign startWinding if edge is in
2834 // same direction. If the direction is opposite, winding to
2835 // assign is flipped sign or +/- 1?
2836 if (abs(maxWinding) < abs(winding)) {
2837 maxWinding = winding;
2838 }
2839 segment->markWinding(lesser, maxWinding);
2840 break;
2841 }
2842 } while (++nextIndex != lastIndex);
2843 } else {
2844 SkASSERT(endIndex > tIndex);
2845 }
2846 return segment;
2847 }
2848 }
2849 return NULL;
2850}
2851
caryclark@google.com027de222012-07-12 12:52:50 +00002852#if DEBUG_ACTIVE_SPANS
2853static void debugShowActiveSpans(SkTDArray<Contour*>& contourList) {
2854 for (int index = 0; index < contourList.count(); ++ index) {
2855 contourList[index]->debugShowActiveSpans();
2856 }
2857}
2858#endif
2859
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002860// Each segment may have an inside or an outside. Segments contained within
2861// winding may have insides on either side, and form a contour that should be
2862// ignored. Segments that are coincident with opposing direction segments may
2863// have outsides on either side, and should also disappear.
2864// 'Normal' segments will have one inside and one outside. Subsequent connections
2865// when winding should follow the intersection direction. If more than one edge
2866// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002867 // since we start with leftmost top edge, we'll traverse through a
2868 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002869static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002870 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002871 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002872 Contour* topContour;
2873 Segment* topStart = findTopContour(contourList, topContour);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002874 if (!topStart) {
2875 break;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002876 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002877 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00002878 // follow edges to intersection by changing the index by direction.
2879 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002880 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002881 int contourWinding;
2882 if (firstContour) {
2883 contourWinding = 0;
2884 firstContour = false;
2885 } else {
2886 const SkPoint& topPoint = current->xyAtT(endIndex);
2887 contourWinding = innerContourCheck(contourList, topContour, topPoint);
2888#if DEBUG_WINDING
2889 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
2890#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002891 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002892 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002893 bool firstTime = true;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002894 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002895 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002896 // int firstWinding = contourWinding + spanWinding;
2897 // FIXME: needs work. While it works in limited situations, it does
2898 // not always compute winding correctly. Active should be removed and instead
2899 // the initial winding should be correctly passed in so that if the
2900 // inner contour is wound the same way, it never finds an accumulated
2901 // winding of zero. Inside 'find next', we need to look for transitions
2902 // other than zero when resolving sorted angles.
2903 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002904 do {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002905 bool active = winding * spanWinding <= 0;
2906 const SkPoint* firstPt = NULL;
2907 do {
2908 SkASSERT(!current->done());
2909 int nextStart, nextEnd, flipped = 1;
2910 Segment* next = current->findNext(chaseArray,
2911 winding + spanWinding, index,
2912 endIndex, nextStart, nextEnd, flipped, firstTime);
2913 if (!next) {
2914 break;
2915 }
2916 if (!firstPt) {
2917 firstPt = &current->addMoveTo(index, simple, active);
2918 }
2919 lastPt = current->addCurveTo(index, endIndex, simple, active);
2920 current = next;
2921 index = nextStart;
2922 endIndex = nextEnd;
2923 spanWinding = SkSign32(spanWinding) * flipped * next->windValue(
2924 SkMin32(nextStart, nextEnd));
2925 #if DEBUG_WINDING
2926 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
2927 #endif
2928 firstTime = false;
2929 } while (*firstPt != lastPt && (active || !current->done()));
2930 if (firstPt && active) {
2931 #if DEBUG_PATH_CONSTRUCTION
2932 SkDebugf("%s close\n", __FUNCTION__);
2933 #endif
2934 simple.close();
2935 }
2936 current = findChase(chaseArray, index, endIndex);
caryclark@google.com027de222012-07-12 12:52:50 +00002937#if DEBUG_ACTIVE_SPANS
2938 debugShowActiveSpans(contourList);
2939#endif
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002940 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002941 break;
2942 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002943 int lesser = SkMin32(index, endIndex);
2944 spanWinding = current->windSum(lesser);
2945 int spanValue = current->windValue(lesser);
2946 SkASSERT(spanWinding != SK_MinS32);
2947 int spanSign = current->spanSign(index, endIndex);
2948 #if DEBUG_WINDING
2949 SkDebugf("%s spanWinding=%d spanSign=%d winding=%d spanValue=%d\n",
2950 __FUNCTION__, spanWinding, spanSign, winding, spanValue);
2951 #endif
2952 if (spanWinding * spanSign < 0) {
2953 #if DEBUG_WINDING
2954 SkDebugf("%s spanWinding * spanSign < 0\n", __FUNCTION__);
2955 #endif
2956 SkTSwap<int>(index, endIndex);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002957 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002958 if (abs(spanWinding) > spanValue) {
2959 #if DEBUG_WINDING
2960 SkDebugf("%s abs(spanWinding) > spanValue\n", __FUNCTION__);
2961 #endif
2962 winding = spanWinding;
2963 spanWinding = spanValue * SkSign32(spanWinding);
2964 winding -= spanWinding;
2965 }
2966 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002967 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002968}
2969
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002970static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
2971 int contourCount = contourList.count();
2972 for (int cTest = 0; cTest < contourCount; ++cTest) {
2973 Contour* contour = contourList[cTest];
2974 contour->fixOtherTIndex();
2975 }
2976}
2977
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002978static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002979 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002980 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002981 if (count == 0) {
2982 return;
2983 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002984 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002985 *list.append() = &contours[index];
2986 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002987 QSort<Contour>(list.begin(), list.end() - 1);
2988}
2989
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002990void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002991 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002992 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002993 simple.reset();
2994 simple.setFillType(SkPath::kEvenOdd_FillType);
2995
2996 // turn path into list of segments
2997 SkTArray<Contour> contours;
2998 // FIXME: add self-intersecting cubics' T values to segment
2999 EdgeBuilder builder(path, contours);
3000 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003001 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003002 Contour** currentPtr = contourList.begin();
3003 if (!currentPtr) {
3004 return;
3005 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003006 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003007 // find all intersections between segments
3008 do {
3009 Contour** nextPtr = currentPtr;
3010 Contour* current = *currentPtr++;
3011 Contour* next;
3012 do {
3013 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00003014 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003015 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00003016 // eat through coincident edges
3017 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00003018 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003019 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00003020 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00003021}
3022