blob: 69469213638e0789a2b6d3eb308613d101d2c328 [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.com8dcf1142012-07-02 20:27:02 +000031#define DEBUG_WINDING 0
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000032#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.com8dcf1142012-07-02 20:27:02 +000033#define DEBUG_MARK_DONE 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000034
35#else
36
37//const bool gRunTestsInOneThread = true;
38
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000039#define DEBUG_ADD_INTERSECTING_TS 0
caryclark@google.comfa0588f2012-04-26 21:01:06 +000040#define DEBUG_BRIDGE 1
caryclark@google.com8dcf1142012-07-02 20:27:02 +000041#define DEBUG_CROSS 1
caryclark@google.comfa0588f2012-04-26 21:01:06 +000042#define DEBUG_DUMP 1
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000043#define DEBUG_PATH_CONSTRUCTION 1
caryclark@google.comfa4a6e92012-07-11 17:52:32 +000044#define DEBUG_WINDING 01
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000045#define DEBUG_UNUSED 0 // set to expose unused functions
caryclark@google.comfa4a6e92012-07-11 17:52:32 +000046#define DEBUG_MARK_DONE 01
caryclark@google.comfa0588f2012-04-26 21:01:06 +000047
48#endif
49
50#if DEBUG_DUMP
51static const char* kLVerbStr[] = {"", "line", "quad", "cubic"};
caryclark@google.com65f9f0a2012-05-23 18:09:25 +000052// static const char* kUVerbStr[] = {"", "Line", "Quad", "Cubic"};
caryclark@google.comfa0588f2012-04-26 21:01:06 +000053static int gContourID;
54static int gSegmentID;
55#endif
56
caryclark@google.com8dcf1142012-07-02 20:27:02 +000057#ifndef DEBUG_TEST
58#define DEBUG_TEST 0
59#endif
60
caryclark@google.comfa0588f2012-04-26 21:01:06 +000061static int LineIntersect(const SkPoint a[2], const SkPoint b[2],
62 Intersections& intersections) {
63 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
64 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
65 return intersect(aLine, bLine, intersections.fT[0], intersections.fT[1]);
66}
67
68static int QuadLineIntersect(const SkPoint a[3], const SkPoint b[2],
69 Intersections& intersections) {
70 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
71 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
72 intersect(aQuad, bLine, intersections);
73 return intersections.fUsed;
74}
75
76static int CubicLineIntersect(const SkPoint a[2], const SkPoint b[3],
77 Intersections& intersections) {
78 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
79 {a[3].fX, a[3].fY}};
80 const _Line bLine = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}};
81 return intersect(aCubic, bLine, intersections.fT[0], intersections.fT[1]);
82}
83
84static int QuadIntersect(const SkPoint a[3], const SkPoint b[3],
85 Intersections& intersections) {
86 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
87 const Quadratic bQuad = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY}};
88 intersect(aQuad, bQuad, intersections);
89 return intersections.fUsed;
90}
91
92static int CubicIntersect(const SkPoint a[4], const SkPoint b[4],
93 Intersections& intersections) {
94 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
95 {a[3].fX, a[3].fY}};
96 const Cubic bCubic = {{b[0].fX, b[0].fY}, {b[1].fX, b[1].fY}, {b[2].fX, b[2].fY},
97 {b[3].fX, b[3].fY}};
98 intersect(aCubic, bCubic, intersections);
99 return intersections.fUsed;
100}
101
102static int HLineIntersect(const SkPoint a[2], SkScalar left, SkScalar right,
103 SkScalar y, bool flipped, Intersections& intersections) {
104 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
105 return horizontalIntersect(aLine, left, right, y, flipped, intersections);
106}
107
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000108static int HQuadIntersect(const SkPoint a[3], SkScalar left, SkScalar right,
109 SkScalar y, bool flipped, Intersections& intersections) {
110 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
111 return horizontalIntersect(aQuad, left, right, y, flipped, intersections);
112}
113
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000114static int HCubicIntersect(const SkPoint a[4], SkScalar left, SkScalar right,
115 SkScalar y, bool flipped, Intersections& intersections) {
116 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
117 {a[3].fX, a[3].fY}};
118 return horizontalIntersect(aCubic, left, right, y, flipped, intersections);
119}
120
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000121static int VLineIntersect(const SkPoint a[2], SkScalar top, SkScalar bottom,
122 SkScalar x, bool flipped, Intersections& intersections) {
123 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
124 return verticalIntersect(aLine, top, bottom, x, flipped, intersections);
125}
126
127static int VQuadIntersect(const SkPoint a[3], SkScalar top, SkScalar bottom,
128 SkScalar x, bool flipped, Intersections& intersections) {
129 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
130 return verticalIntersect(aQuad, top, bottom, x, flipped, intersections);
131}
132
133static int VCubicIntersect(const SkPoint a[4], SkScalar top, SkScalar bottom,
134 SkScalar x, bool flipped, Intersections& intersections) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000135 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
136 {a[3].fX, a[3].fY}};
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000137 return verticalIntersect(aCubic, top, bottom, x, flipped, intersections);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000138}
139
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000140static int (* const VSegmentIntersect[])(const SkPoint [], SkScalar ,
141 SkScalar , SkScalar , bool , Intersections& ) = {
142 NULL,
143 VLineIntersect,
144 VQuadIntersect,
145 VCubicIntersect
146};
147
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000148static void LineXYAtT(const SkPoint a[2], double t, SkPoint* out) {
149 const _Line line = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
150 double x, y;
151 xy_at_t(line, t, x, y);
152 out->fX = SkDoubleToScalar(x);
153 out->fY = SkDoubleToScalar(y);
154}
155
156static void QuadXYAtT(const SkPoint a[3], double t, SkPoint* out) {
157 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
158 double x, y;
159 xy_at_t(quad, t, x, y);
160 out->fX = SkDoubleToScalar(x);
161 out->fY = SkDoubleToScalar(y);
162}
163
164static void CubicXYAtT(const SkPoint a[4], double t, SkPoint* out) {
165 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
166 {a[3].fX, a[3].fY}};
167 double x, y;
168 xy_at_t(cubic, t, x, y);
169 out->fX = SkDoubleToScalar(x);
170 out->fY = SkDoubleToScalar(y);
171}
172
173static void (* const SegmentXYAtT[])(const SkPoint [], double , SkPoint* ) = {
174 NULL,
175 LineXYAtT,
176 QuadXYAtT,
177 CubicXYAtT
178};
179
180static SkScalar LineXAtT(const SkPoint a[2], double t) {
181 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
182 double x;
183 xy_at_t(aLine, t, x, *(double*) 0);
184 return SkDoubleToScalar(x);
185}
186
187static SkScalar QuadXAtT(const SkPoint a[3], double t) {
188 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
189 double x;
190 xy_at_t(quad, t, x, *(double*) 0);
191 return SkDoubleToScalar(x);
192}
193
194static SkScalar CubicXAtT(const SkPoint a[4], double t) {
195 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
196 {a[3].fX, a[3].fY}};
197 double x;
198 xy_at_t(cubic, t, x, *(double*) 0);
199 return SkDoubleToScalar(x);
200}
201
202static SkScalar (* const SegmentXAtT[])(const SkPoint [], double ) = {
203 NULL,
204 LineXAtT,
205 QuadXAtT,
206 CubicXAtT
207};
208
209static SkScalar LineYAtT(const SkPoint a[2], double t) {
210 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
211 double y;
212 xy_at_t(aLine, t, *(double*) 0, y);
213 return SkDoubleToScalar(y);
214}
215
216static SkScalar QuadYAtT(const SkPoint a[3], double t) {
217 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
218 double y;
219 xy_at_t(quad, t, *(double*) 0, y);
220 return SkDoubleToScalar(y);
221}
222
223static SkScalar CubicYAtT(const SkPoint a[4], double t) {
224 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
225 {a[3].fX, a[3].fY}};
226 double y;
227 xy_at_t(cubic, t, *(double*) 0, y);
228 return SkDoubleToScalar(y);
229}
230
231static SkScalar (* const SegmentYAtT[])(const SkPoint [], double ) = {
232 NULL,
233 LineYAtT,
234 QuadYAtT,
235 CubicYAtT
236};
237
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000238static SkScalar LineDXAtT(const SkPoint a[2], double ) {
239 return a[1].fX - a[0].fX;
240}
241
242static SkScalar QuadDXAtT(const SkPoint a[3], double t) {
243 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY}};
244 double x;
245 dxdy_at_t(quad, t, x, *(double*) 0);
246 return SkDoubleToScalar(x);
247}
248
249static SkScalar CubicDXAtT(const SkPoint a[4], double t) {
250 const Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}, {a[2].fX, a[2].fY},
251 {a[3].fX, a[3].fY}};
252 double x;
253 dxdy_at_t(cubic, t, x, *(double*) 0);
254 return SkDoubleToScalar(x);
255}
256
257static SkScalar (* const SegmentDXAtT[])(const SkPoint [], double ) = {
258 NULL,
259 LineDXAtT,
260 QuadDXAtT,
261 CubicDXAtT
262};
263
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000264static void LineSubDivide(const SkPoint a[2], double startT, double endT,
265 SkPoint sub[2]) {
266 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
267 _Line dst;
268 sub_divide(aLine, startT, endT, dst);
269 sub[0].fX = SkDoubleToScalar(dst[0].x);
270 sub[0].fY = SkDoubleToScalar(dst[0].y);
271 sub[1].fX = SkDoubleToScalar(dst[1].x);
272 sub[1].fY = SkDoubleToScalar(dst[1].y);
273}
274
275static void QuadSubDivide(const SkPoint a[3], double startT, double endT,
276 SkPoint sub[3]) {
277 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
278 {a[2].fX, a[2].fY}};
279 Quadratic dst;
280 sub_divide(aQuad, startT, endT, dst);
281 sub[0].fX = SkDoubleToScalar(dst[0].x);
282 sub[0].fY = SkDoubleToScalar(dst[0].y);
283 sub[1].fX = SkDoubleToScalar(dst[1].x);
284 sub[1].fY = SkDoubleToScalar(dst[1].y);
285 sub[2].fX = SkDoubleToScalar(dst[2].x);
286 sub[2].fY = SkDoubleToScalar(dst[2].y);
287}
288
289static void CubicSubDivide(const SkPoint a[4], double startT, double endT,
290 SkPoint sub[4]) {
291 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
292 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
293 Cubic dst;
294 sub_divide(aCubic, startT, endT, dst);
295 sub[0].fX = SkDoubleToScalar(dst[0].x);
296 sub[0].fY = SkDoubleToScalar(dst[0].y);
297 sub[1].fX = SkDoubleToScalar(dst[1].x);
298 sub[1].fY = SkDoubleToScalar(dst[1].y);
299 sub[2].fX = SkDoubleToScalar(dst[2].x);
300 sub[2].fY = SkDoubleToScalar(dst[2].y);
301 sub[3].fX = SkDoubleToScalar(dst[3].x);
302 sub[3].fY = SkDoubleToScalar(dst[3].y);
303}
304
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000305static void (* const SegmentSubDivide[])(const SkPoint [], double , double ,
306 SkPoint []) = {
307 NULL,
308 LineSubDivide,
309 QuadSubDivide,
310 CubicSubDivide
311};
312
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000313#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000314static void QuadSubBounds(const SkPoint a[3], double startT, double endT,
315 SkRect& bounds) {
316 SkPoint dst[3];
317 QuadSubDivide(a, startT, endT, dst);
318 bounds.fLeft = bounds.fRight = dst[0].fX;
319 bounds.fTop = bounds.fBottom = dst[0].fY;
320 for (int index = 1; index < 3; ++index) {
321 bounds.growToInclude(dst[index].fX, dst[index].fY);
322 }
323}
324
325static void CubicSubBounds(const SkPoint a[4], double startT, double endT,
326 SkRect& bounds) {
327 SkPoint dst[4];
328 CubicSubDivide(a, startT, endT, dst);
329 bounds.fLeft = bounds.fRight = dst[0].fX;
330 bounds.fTop = bounds.fBottom = dst[0].fY;
331 for (int index = 1; index < 4; ++index) {
332 bounds.growToInclude(dst[index].fX, dst[index].fY);
333 }
334}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000335#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000336
caryclark@google.com15fa1382012-05-07 20:49:36 +0000337static SkPath::Verb QuadReduceOrder(const SkPoint a[3],
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000338 SkTDArray<SkPoint>& reducePts) {
339 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
340 {a[2].fX, a[2].fY}};
341 Quadratic dst;
342 int order = reduceOrder(aQuad, dst);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000343 if (order == 3) {
344 return SkPath::kQuad_Verb;
345 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000346 for (int index = 0; index < order; ++index) {
347 SkPoint* pt = reducePts.append();
348 pt->fX = SkDoubleToScalar(dst[index].x);
349 pt->fY = SkDoubleToScalar(dst[index].y);
350 }
351 return (SkPath::Verb) (order - 1);
352}
353
354static SkPath::Verb CubicReduceOrder(const SkPoint a[4],
355 SkTDArray<SkPoint>& reducePts) {
356 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
357 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
358 Cubic dst;
359 int order = reduceOrder(aCubic, dst, kReduceOrder_QuadraticsAllowed);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000360 if (order == 4) {
361 return SkPath::kCubic_Verb;
362 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000363 for (int index = 0; index < order; ++index) {
364 SkPoint* pt = reducePts.append();
365 pt->fX = SkDoubleToScalar(dst[index].x);
366 pt->fY = SkDoubleToScalar(dst[index].y);
367 }
368 return (SkPath::Verb) (order - 1);
369}
370
caryclark@google.com15fa1382012-05-07 20:49:36 +0000371static bool QuadIsLinear(const SkPoint a[3]) {
372 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
373 {a[2].fX, a[2].fY}};
374 return isLinear(aQuad, 0, 2);
375}
376
377static bool CubicIsLinear(const SkPoint a[4]) {
378 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
379 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
380 return isLinear(aCubic, 0, 3);
381}
382
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000383static SkScalar LineLeftMost(const SkPoint a[2], double startT, double endT) {
384 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
385 double x[2];
386 xy_at_t(aLine, startT, x[0], *(double*) 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +0000387 xy_at_t(aLine, endT, x[1], *(double*) 0);
388 return SkMinScalar((float) x[0], (float) x[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000389}
390
391static SkScalar QuadLeftMost(const SkPoint a[3], double startT, double endT) {
392 const Quadratic aQuad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
393 {a[2].fX, a[2].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000394 return (float) leftMostT(aQuad, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000395}
396
397static SkScalar CubicLeftMost(const SkPoint a[4], double startT, double endT) {
398 const Cubic aCubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
399 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000400 return (float) leftMostT(aCubic, startT, endT);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000401}
402
403static SkScalar (* const SegmentLeftMost[])(const SkPoint [], double , double) = {
404 NULL,
405 LineLeftMost,
406 QuadLeftMost,
407 CubicLeftMost
408};
409
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000410#if DEBUG_UNUSED
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000411static bool IsCoincident(const SkPoint a[2], const SkPoint& above,
412 const SkPoint& below) {
413 const _Line aLine = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY}};
414 const _Line bLine = {{above.fX, above.fY}, {below.fX, below.fY}};
415 return implicit_matches_ulps(aLine, bLine, 32);
416}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000417#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000418
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000419class Segment;
420
caryclark@google.com15fa1382012-05-07 20:49:36 +0000421// sorting angles
422// given angles of {dx dy ddx ddy dddx dddy} sort them
423class Angle {
424public:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000425 // FIXME: this is bogus for quads and cubics
426 // if the quads and cubics' line from end pt to ctrl pt are coincident,
427 // there's no obvious way to determine the curve ordering from the
428 // derivatives alone. In particular, if one quadratic's coincident tangent
429 // is longer than the other curve, the final control point can place the
430 // longer curve on either side of the shorter one.
431 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf
432 // may provide some help, but nothing has been figured out yet.
caryclark@google.com15fa1382012-05-07 20:49:36 +0000433 bool operator<(const Angle& rh) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000434 if ((fDy < 0) ^ (rh.fDy < 0)) {
435 return fDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000436 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000437 if (fDy == 0 && rh.fDy == 0 && fDx != rh.fDx) {
438 return fDx < rh.fDx;
439 }
440 SkScalar cmp = fDx * rh.fDy - rh.fDx * fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000441 if (cmp) {
442 return cmp < 0;
443 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000444 if ((fDDy < 0) ^ (rh.fDDy < 0)) {
445 return fDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000446 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000447 if (fDDy == 0 && rh.fDDy == 0 && fDDx != rh.fDDx) {
448 return fDDx < rh.fDDx;
449 }
450 cmp = fDDx * rh.fDDy - rh.fDDx * fDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000451 if (cmp) {
452 return cmp < 0;
453 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000454 if ((fDDDy < 0) ^ (rh.fDDDy < 0)) {
455 return fDDDy < 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000456 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000457 if (fDDDy == 0 && rh.fDDDy == 0) {
458 return fDDDx < rh.fDDDx;
459 }
460 return fDDDx * rh.fDDDy < rh.fDDDx * fDDDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000461 }
462
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000463 int end() const {
464 return fEnd;
465 }
466
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000467 bool isHorizontal() const {
468 return fDy == 0 && fDDy == 0 && fDDDy == 0;
469 }
470
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000471 // since all angles share a point, this needs to know which point
472 // is the common origin, i.e., whether the center is at pts[0] or pts[verb]
473 // practically, this should only be called by addAngle
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000474 void set(const SkPoint* pts, SkPath::Verb verb, const Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000475 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000476 SkASSERT(start != end);
477 fSegment = segment;
478 fStart = start;
479 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000480 fDx = pts[1].fX - pts[0].fX; // b - a
481 fDy = pts[1].fY - pts[0].fY;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000482 if (verb == SkPath::kLine_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000483 fDDx = fDDy = fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000484 return;
485 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000486 fDDx = pts[2].fX - pts[1].fX - fDx; // a - 2b + c
487 fDDy = pts[2].fY - pts[1].fY - fDy;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000488 if (verb == SkPath::kQuad_Verb) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000489 fDDDx = fDDDy = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000490 return;
491 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000492 fDDDx = pts[3].fX + 3 * (pts[1].fX - pts[2].fX) - pts[0].fX;
493 fDDDy = pts[3].fY + 3 * (pts[1].fY - pts[2].fY) - pts[0].fY;
494 }
495
496 // noncoincident quads/cubics may have the same initial angle
497 // as lines, so must sort by derivatives as well
498 // if flatness turns out to be a reasonable way to sort, use the below:
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000499 void setFlat(const SkPoint* pts, SkPath::Verb verb, Segment* segment,
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000500 int start, int end) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000501 fSegment = segment;
502 fStart = start;
503 fEnd = end;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000504 fDx = pts[1].fX - pts[0].fX; // b - a
505 fDy = pts[1].fY - pts[0].fY;
506 if (verb == SkPath::kLine_Verb) {
507 fDDx = fDDy = fDDDx = fDDDy = 0;
508 return;
509 }
510 if (verb == SkPath::kQuad_Verb) {
511 int uplsX = FloatAsInt(pts[2].fX - pts[1].fY - fDx);
512 int uplsY = FloatAsInt(pts[2].fY - pts[1].fY - fDy);
513 int larger = std::max(abs(uplsX), abs(uplsY));
514 int shift = 0;
515 double flatT;
516 SkPoint ddPt; // FIXME: get rid of copy (change fDD_ to point)
517 LineParameters implicitLine;
518 _Line tangent = {{pts[0].fX, pts[0].fY}, {pts[1].fX, pts[1].fY}};
519 implicitLine.lineEndPoints(tangent);
520 implicitLine.normalize();
521 while (larger > UlpsEpsilon * 1024) {
522 larger >>= 2;
523 ++shift;
524 flatT = 0.5 / (1 << shift);
525 QuadXYAtT(pts, flatT, &ddPt);
526 _Point _pt = {ddPt.fX, ddPt.fY};
527 double distance = implicitLine.pointDistance(_pt);
528 if (approximately_zero(distance)) {
529 SkDebugf("%s ulps too small %1.9g\n", __FUNCTION__, distance);
530 break;
531 }
532 }
533 flatT = 0.5 / (1 << shift);
534 QuadXYAtT(pts, flatT, &ddPt);
535 fDDx = ddPt.fX - pts[0].fX;
536 fDDy = ddPt.fY - pts[0].fY;
537 SkASSERT(fDDx != 0 || fDDy != 0);
538 fDDDx = fDDDy = 0;
539 return;
540 }
541 SkASSERT(0); // FIXME: add cubic case
542 }
543
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000544 Segment* segment() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000545 return const_cast<Segment*>(fSegment);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000546 }
547
548 int sign() const {
caryclark@google.com495f8e42012-05-31 13:13:11 +0000549 return SkSign32(fStart - fEnd);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000550 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000551
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000552 int start() const {
553 return fStart;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000554 }
555
556private:
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000557 SkScalar fDx;
558 SkScalar fDy;
559 SkScalar fDDx;
560 SkScalar fDDy;
561 SkScalar fDDDx;
562 SkScalar fDDDy;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000563 const Segment* fSegment;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000564 int fStart;
565 int fEnd;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000566};
567
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000568static void sortAngles(SkTDArray<Angle>& angles, SkTDArray<Angle*>& angleList) {
569 int angleCount = angles.count();
570 int angleIndex;
571 angleList.setReserve(angleCount);
572 for (angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
573 *angleList.append() = &angles[angleIndex];
574 }
575 QSort<Angle>(angleList.begin(), angleList.end() - 1);
576}
577
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000578// Bounds, unlike Rect, does not consider a line to be empty.
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000579struct Bounds : public SkRect {
580 static bool Intersects(const Bounds& a, const Bounds& b) {
581 return a.fLeft <= b.fRight && b.fLeft <= a.fRight &&
582 a.fTop <= b.fBottom && b.fTop <= a.fBottom;
583 }
584
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000585 void add(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom) {
586 if (left < fLeft) {
587 fLeft = left;
588 }
589 if (top < fTop) {
590 fTop = top;
591 }
592 if (right > fRight) {
593 fRight = right;
594 }
595 if (bottom > fBottom) {
596 fBottom = bottom;
597 }
598 }
599
600 void add(const Bounds& toAdd) {
601 add(toAdd.fLeft, toAdd.fTop, toAdd.fRight, toAdd.fBottom);
602 }
603
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000604 bool isEmpty() {
605 return fLeft > fRight || fTop > fBottom
606 || fLeft == fRight && fTop == fBottom
607 || isnan(fLeft) || isnan(fRight)
608 || isnan(fTop) || isnan(fBottom);
609 }
610
611 void setCubicBounds(const SkPoint a[4]) {
612 _Rect dRect;
613 Cubic cubic = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
614 {a[2].fX, a[2].fY}, {a[3].fX, a[3].fY}};
615 dRect.setBounds(cubic);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000616 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
617 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000618 }
619
620 void setQuadBounds(const SkPoint a[3]) {
621 const Quadratic quad = {{a[0].fX, a[0].fY}, {a[1].fX, a[1].fY},
622 {a[2].fX, a[2].fY}};
623 _Rect dRect;
624 dRect.setBounds(quad);
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000625 set((float) dRect.left, (float) dRect.top, (float) dRect.right,
626 (float) dRect.bottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000627 }
628};
629
caryclark@google.com15fa1382012-05-07 20:49:36 +0000630struct Span {
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000631 Segment* fOther;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000632 mutable SkPoint const* fPt; // lazily computed as needed
633 double fT;
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000634 double fOtherT; // value at fOther[fOtherIndex].fT
635 int fOtherIndex; // can't be used during intersection
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000636 int fWindSum; // accumulated from contours surrounding this one
637 int fWindValue; // 0 == canceled; 1 == normal; >1 == coincident
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000638 bool fDone; // if set, this span to next higher T has been processed
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000639};
640
641class Segment {
642public:
643 Segment() {
644#if DEBUG_DUMP
645 fID = ++gSegmentID;
646#endif
647 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000648
649 bool activeAngles(int index) const {
650 double referenceT = fTs[index].fT;
651 int lesser = index;
652 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
653 if (activeAnglesInner(lesser)) {
654 return true;
655 }
656 }
657 do {
658 if (activeAnglesInner(index)) {
659 return true;
660 }
661 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
662 return false;
663 }
664
665 bool activeAnglesInner(int index) const {
666 Span* span = &fTs[index];
667 Segment* other = span->fOther;
668 int oIndex = span->fOtherIndex;
669 int next = other->nextSpan(oIndex, 1);
670 if (next > 0 && !other->fTs[oIndex].fDone) {
671 return true;
672 }
673 int prev = other->nextSpan(oIndex, -1);
674 // edge leading into junction
675 return prev >= 0 && !other->fTs[prev].fDone;
676 }
677
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000678 SkScalar activeTop() const {
679 SkASSERT(!done());
680 int count = fTs.count();
681 SkScalar result = SK_ScalarMax;
682 bool lastDone = true;
683 for (int index = 0; index < count; ++index) {
684 bool done = fTs[index].fDone;
685 if (!done || !lastDone) {
686 SkScalar y = yAtT(index);
687 if (result > y) {
688 result = y;
689 }
690 }
691 lastDone = done;
692 }
693 SkASSERT(result < SK_ScalarMax);
694 return result;
695 }
696
697 void addAngle(SkTDArray<Angle>& angles, int start, int end) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000698 SkASSERT(start != end);
699 SkPoint edge[4];
700 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
701 Angle* angle = angles.append();
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000702 angle->set(edge, fVerb, this, start, end);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000703 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000704
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000705 void addCubic(const SkPoint pts[4]) {
706 init(pts, SkPath::kCubic_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000707 fBounds.setCubicBounds(pts);
708 }
709
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000710 // FIXME: this needs to defer add for aligned consecutive line segments
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000711 SkPoint addCurveTo(int start, int end, SkPath& path, bool active) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000712 SkPoint edge[4];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000713 // OPTIMIZE? if not active, skip remainder and return xy_at_t(end)
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000714 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000715 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000716 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000717 SkDebugf("%s %s (%1.9g,%1.9g)", __FUNCTION__,
718 kLVerbStr[fVerb], edge[1].fX, edge[1].fY);
719 if (fVerb > 1) {
720 SkDebugf(" (%1.9g,%1.9g)", edge[2].fX, edge[2].fY);
721 }
722 if (fVerb > 2) {
723 SkDebugf(" (%1.9g,%1.9g)", edge[3].fX, edge[3].fY);
724 }
725 SkDebugf("\n");
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000726 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000727 switch (fVerb) {
728 case SkPath::kLine_Verb:
729 path.lineTo(edge[1].fX, edge[1].fY);
730 break;
731 case SkPath::kQuad_Verb:
732 path.quadTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY);
733 break;
734 case SkPath::kCubic_Verb:
735 path.cubicTo(edge[1].fX, edge[1].fY, edge[2].fX, edge[2].fY,
736 edge[3].fX, edge[3].fY);
737 break;
738 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000739 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000740 return edge[fVerb];
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000741 }
742
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000743 void addLine(const SkPoint pts[2]) {
744 init(pts, SkPath::kLine_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000745 fBounds.set(pts, 2);
746 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000747
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000748 const SkPoint& addMoveTo(int tIndex, SkPath& path, bool active) {
749 const SkPoint& pt = xyAtT(tIndex);
750 if (active) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000751 #if DEBUG_PATH_CONSTRUCTION
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000752 SkDebugf("%s (%1.9g,%1.9g)\n", __FUNCTION__, pt.fX, pt.fY);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000753 #endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000754 path.moveTo(pt.fX, pt.fY);
755 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +0000756 return pt;
caryclark@google.com1577e8f2012-05-22 17:01:14 +0000757 }
758
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000759 // add 2 to edge or out of range values to get T extremes
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000760 void addOtherT(int index, double otherT, int otherIndex) {
761 Span& span = fTs[index];
762 span.fOtherT = otherT;
763 span.fOtherIndex = otherIndex;
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000764 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000765
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000766 void addQuad(const SkPoint pts[3]) {
767 init(pts, SkPath::kQuad_Verb);
caryclark@google.comfa0588f2012-04-26 21:01:06 +0000768 fBounds.setQuadBounds(pts);
769 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000770
771 // Defer all coincident edge processing until
772 // after normal intersections have been computed
caryclark@google.coma833b5c2012-04-30 19:38:50 +0000773
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000774// no need to be tricky; insert in normal T order
775// resolve overlapping ts when considering coincidence later
776
777 // add non-coincident intersection. Resulting edges are sorted in T.
778 int addT(double newT, Segment* other) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000779 // FIXME: in the pathological case where there is a ton of intercepts,
780 // binary search?
781 int insertedAt = -1;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000782 size_t tCount = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000783 for (size_t index = 0; index < tCount; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +0000784 // OPTIMIZATION: if there are three or more identical Ts, then
785 // the fourth and following could be further insertion-sorted so
786 // that all the edges are clockwise or counterclockwise.
787 // This could later limit segment tests to the two adjacent
788 // neighbors, although it doesn't help with determining which
789 // circular direction to go in.
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000790 if (newT < fTs[index].fT) {
791 insertedAt = index;
792 break;
caryclark@google.com15fa1382012-05-07 20:49:36 +0000793 }
794 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000795 Span* span;
796 if (insertedAt >= 0) {
797 span = fTs.insert(insertedAt);
798 } else {
799 insertedAt = tCount;
800 span = fTs.append();
801 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000802 span->fT = newT;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000803 span->fOther = other;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +0000804 span->fPt = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000805 span->fWindSum = SK_MinS32;
806 span->fWindValue = 1;
807 if ((span->fDone = newT == 1)) {
caryclark@google.com65f9f0a2012-05-23 18:09:25 +0000808 ++fDoneSpans;
809 }
caryclark@google.com15fa1382012-05-07 20:49:36 +0000810 return insertedAt;
811 }
812
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000813 // set spans from start to end to decrement by one
814 // note this walks other backwards
815 // FIMXE: there's probably an edge case that can be constructed where
816 // two span in one segment are separated by float epsilon on one span but
817 // not the other, if one segment is very small. For this
818 // case the counts asserted below may or may not be enough to separate the
819 // spans. Even if the counts work out, what if the spanw aren't correctly
820 // sorted? It feels better in such a case to match the span's other span
821 // pointer since both coincident segments must contain the same spans.
822 void addTCancel(double startT, double endT, Segment& other,
823 double oStartT, double oEndT) {
824 SkASSERT(endT - startT >= FLT_EPSILON);
825 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
826 int index = 0;
827 while (startT - fTs[index].fT >= FLT_EPSILON) {
828 ++index;
829 }
caryclark@google.comb9738012012-07-03 19:53:30 +0000830 int oIndex = other.fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000831 while (other.fTs[--oIndex].fT - oEndT > -FLT_EPSILON)
832 ;
833 Span* test = &fTs[index];
834 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000835 do {
836 bool decrement = test->fWindValue && oTest->fWindValue;
837 Span* end = test;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000838 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000839 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000840 SkASSERT(end->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000841 if (--(end->fWindValue) == 0) {
842 end->fDone = true;
843 ++fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000844 }
845 }
846 end = &fTs[++index];
847 } while (end->fT - test->fT < FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000848 Span* oTestStart = oTest;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000849 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000850 if (decrement) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000851 SkASSERT(oTestStart->fWindValue > 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000852 if (--(oTestStart->fWindValue) == 0) {
853 oTestStart->fDone = true;
854 ++other.fDoneSpans;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000855 }
856 }
857 if (!oIndex) {
858 break;
859 }
860 oTestStart = &other.fTs[--oIndex];
861 } while (oTest->fT - oTestStart->fT < FLT_EPSILON);
862 test = end;
863 oTest = oTestStart;
864 } while (test->fT < endT - FLT_EPSILON);
865 SkASSERT(!oIndex || oTest->fT <= oStartT - FLT_EPSILON);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000866 }
867
868 // set spans from start to end to increment the greater by one and decrement
869 // the lesser
870 void addTCoincident(double startT, double endT, Segment& other,
871 double oStartT, double oEndT) {
872 SkASSERT(endT - startT >= FLT_EPSILON);
873 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
874 int index = 0;
875 while (startT - fTs[index].fT >= FLT_EPSILON) {
876 ++index;
877 }
878 int oIndex = 0;
879 while (oStartT - other.fTs[oIndex].fT >= FLT_EPSILON) {
880 ++oIndex;
881 }
882 Span* test = &fTs[index];
883 Span* oTest = &other.fTs[oIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000884 SkTDArray<double> outsideTs;
885 SkTDArray<double> oOutsideTs;
886 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000887 bool transfer = test->fWindValue && oTest->fWindValue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000888 bool decrementOther = test->fWindValue >= oTest->fWindValue;
889 Span* end = test;
890 double startT = end->fT;
891 double oStartT = oTest->fT;
892 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000893 if (transfer) {
894 if (decrementOther) {
895 ++(end->fWindValue);
896 } else {
897 SkASSERT(end->fWindValue > 0);
898 if (--(end->fWindValue) == 0) {
899 end->fDone = true;
900 ++fDoneSpans;
901 int outCount = outsideTs.count();
902 if (outCount == 0 || end->fT - outsideTs[outCount - 2]
903 >= FLT_EPSILON) {
904 *outsideTs.append() = end->fT;
905 *outsideTs.append() = oStartT;
906 }
907 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000908 }
909 }
910 end = &fTs[++index];
911 } while (end->fT - test->fT < FLT_EPSILON);
912 Span* oEnd = oTest;
913 do {
caryclark@google.comb9738012012-07-03 19:53:30 +0000914 if (transfer) {
915 if (decrementOther) {
916 SkASSERT(oEnd->fWindValue > 0);
917 if (--(oEnd->fWindValue) == 0) {
918 oEnd->fDone = true;
919 ++other.fDoneSpans;
920 int oOutCount = oOutsideTs.count();
921 if (oOutCount == 0 || oEnd->fT
922 - oOutsideTs[oOutCount - 2] >= FLT_EPSILON) {
923 *oOutsideTs.append() = oEnd->fT;
924 *oOutsideTs.append() = startT;
925 }
926 }
927 } else {
928 ++(oEnd->fWindValue);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000929 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000930 }
931 oEnd = &other.fTs[++oIndex];
932 } while (oEnd->fT - oTest->fT < FLT_EPSILON);
933 test = end;
934 oTest = oEnd;
935 } while (test->fT < endT - FLT_EPSILON);
936 SkASSERT(oTest->fT < oEndT + FLT_EPSILON);
937 SkASSERT(oTest->fT > oEndT - FLT_EPSILON);
938 if (!done() && outsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000939 addTOutsides(outsideTs, other, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000940 }
941 if (!other.done() && oOutsideTs.count()) {
caryclark@google.comb9738012012-07-03 19:53:30 +0000942 other.addTOutsides(oOutsideTs, *this, endT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000943 }
944 }
945
caryclark@google.comb9738012012-07-03 19:53:30 +0000946 void addTOutsides(const SkTDArray<double>& outsideTs, Segment& other,
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000947 double otherEnd) {
948 int count = outsideTs.count();
949 double endT = 0;
950 int endSpan = 0;
951 for (int index = 0; index < count; index += 2) {
952 double t = outsideTs[index];
953 double otherT = outsideTs[index + 1];
954 if (t > 1 - FLT_EPSILON) {
955 return;
956 }
957 if (t - endT > FLT_EPSILON) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000958 endSpan = addTDonePair(t, other, otherT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000959 }
960 do {
961 endT = fTs[++endSpan].fT;
962 } while (endT - t < FLT_EPSILON);
963 }
964 addTPair(endT, other, otherEnd);
965 }
966
caryclark@google.comfa4a6e92012-07-11 17:52:32 +0000967 // match the other.fWindValue to its mates
968 int addTDonePair(double t, Segment& other, double otherT) {
969 int insertedAt = addTPair(t, other, otherT);
970 Span& end = fTs[insertedAt];
971 SkASSERT(end.fWindValue == 1);
972 end.fWindValue = 0;
973 end.fDone = true;
974 ++fDoneSpans;
975 Span& otherEnd = other.fTs[end.fOtherIndex];
976 Span* match = NULL;
977 if (end.fOtherIndex > 0) {
978 match = &other.fTs[end.fOtherIndex - 1];
979 }
980 if (!match || match->fT < otherT) {
981 match = &other.fTs[end.fOtherIndex + 1];
982 }
983 otherEnd.fWindValue = match->fWindValue;
984 return insertedAt;
985 }
986
caryclark@google.comb9738012012-07-03 19:53:30 +0000987 int addTPair(double t, Segment& other, double otherT) {
988 int insertedAt = addT(t, &other);
989 int otherInsertedAt = other.addT(otherT, this);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000990 addOtherT(insertedAt, otherT, otherInsertedAt);
caryclark@google.comb9738012012-07-03 19:53:30 +0000991 other.addOtherT(otherInsertedAt, t, insertedAt);
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000992 return insertedAt;
993 }
994
995 void addTwoAngles(int start, int end, SkTDArray<Angle>& angles) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +0000996 // add edge leading into junction
caryclark@google.com8dcf1142012-07-02 20:27:02 +0000997 if (fTs[SkMin32(end, start)].fWindValue > 0) {
998 addAngle(angles, end, start);
999 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001000 // add edge leading away from junction
caryclark@google.com495f8e42012-05-31 13:13:11 +00001001 int step = SkSign32(end - start);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001002 int tIndex = nextSpan(end, step);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001003 if (tIndex >= 0 && fTs[SkMin32(end, tIndex)].fWindValue > 0) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001004 addAngle(angles, end, tIndex);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001005 }
1006 }
1007
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001008 const Bounds& bounds() const {
1009 return fBounds;
1010 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001011
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001012 void buildAngles(int index, SkTDArray<Angle>& angles) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001013 double referenceT = fTs[index].fT;
1014 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001015 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001016 buildAnglesInner(lesser, angles);
1017 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001018 do {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001019 buildAnglesInner(index, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001020 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001021 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001022
1023 void buildAnglesInner(int index, SkTDArray<Angle>& angles) const {
1024 Span* span = &fTs[index];
1025 Segment* other = span->fOther;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001026 // if there is only one live crossing, and no coincidence, continue
1027 // in the same direction
1028 // if there is coincidence, the only choice may be to reverse direction
1029 // find edge on either side of intersection
1030 int oIndex = span->fOtherIndex;
1031 // if done == -1, prior span has already been processed
1032 int step = 1;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001033 int next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001034 if (next < 0) {
1035 step = -step;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001036 next = other->nextSpan(oIndex, step);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001037 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001038 // add candidate into and away from junction
1039 other->addTwoAngles(next, oIndex, angles);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001040 }
1041
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001042 bool cancels(const Segment& other) const {
caryclark@google.comb9738012012-07-03 19:53:30 +00001043 SkASSERT(fVerb == SkPath::kLine_Verb);
1044 SkASSERT(other.fVerb == SkPath::kLine_Verb);
1045 SkPoint dxy = fPts[0] - fPts[1];
1046 SkPoint odxy = other.fPts[0] - other.fPts[1];
1047 return dxy.fX * odxy.fX < 0 || dxy.fY * odxy.fY < 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001048 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001049
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001050 // figure out if the segment's ascending T goes clockwise or not
1051 // not enough context to write this as shown
1052 // instead, add all segments meeting at the top
1053 // sort them using buildAngleList
1054 // find the first in the sort
1055 // see if ascendingT goes to top
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001056 bool clockwise(int /* tIndex */) const {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001057 SkASSERT(0); // incomplete
1058 return false;
1059 }
1060
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001061 int crossedSpan(const SkPoint& basePt, SkScalar& bestY, double& hitT) const {
1062 int start = 0;
1063 int bestT = -1;
1064 SkScalar top = bounds().fTop;
1065 SkScalar bottom = bounds().fBottom;
1066 int end;
1067 do {
1068 end = nextSpan(start, 1);
1069 SkPoint edge[4];
1070 // OPTIMIZE: wrap this so that if start==0 end==fTCount-1 we can
1071 // work with the original data directly
1072 (*SegmentSubDivide[fVerb])(fPts, fTs[start].fT, fTs[end].fT, edge);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001073 // intersect ray starting at basePt with edge
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001074 Intersections intersections;
1075 int pts = (*VSegmentIntersect[fVerb])(edge, top, bottom, basePt.fX,
1076 false, intersections);
1077 if (pts == 0) {
1078 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001079 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001080 if (pts > 1 && fVerb == SkPath::kLine_Verb) {
1081 // if the intersection is edge on, wait for another one
1082 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001083 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001084 SkASSERT(pts == 1); // FIXME: more code required to disambiguate
1085 SkPoint pt;
1086 double foundT = intersections.fT[0][0];
1087 (*SegmentXYAtT[fVerb])(fPts, foundT, &pt);
1088 if (bestY < pt.fY) {
1089 bestY = pt.fY;
1090 bestT = foundT < 1 ? start : end;
1091 hitT = foundT;
1092 }
1093 start = end;
1094 } while (fTs[end].fT != 1);
1095 return bestT;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001096 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001097
caryclark@google.com15fa1382012-05-07 20:49:36 +00001098 bool done() const {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001099 SkASSERT(fDoneSpans <= fTs.count());
1100 return fDoneSpans == fTs.count();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001101 }
1102
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001103 // so the span needs to contain the pairing info found here
1104 // this should include the winding computed for the edge, and
1105 // what edge it connects to, and whether it is discarded
1106 // (maybe discarded == abs(winding) > 1) ?
1107 // only need derivatives for duration of sorting, add a new struct
1108 // for pairings, remove extra spans that have zero length and
1109 // reference an unused other
1110 // for coincident, the last span on the other may be marked done
1111 // (always?)
1112
1113 // if loop is exhausted, contour may be closed.
1114 // FIXME: pass in close point so we can check for closure
1115
1116 // given a segment, and a sense of where 'inside' is, return the next
1117 // segment. If this segment has an intersection, or ends in multiple
1118 // segments, find the mate that continues the outside.
1119 // note that if there are multiples, but no coincidence, we can limit
1120 // choices to connections in the correct direction
1121
1122 // mark found segments as done
1123
caryclark@google.com15fa1382012-05-07 20:49:36 +00001124 // start is the index of the beginning T of this edge
1125 // it is guaranteed to have an end which describes a non-zero length (?)
1126 // winding -1 means ccw, 1 means cw
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001127 // firstFind allows coincident edges to be treated differently
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001128 Segment* findNext(SkTDArray<Span*>& chase, int winding, const int startIndex,
1129 const int endIndex,
1130 int& nextStart, int& nextEnd, int& flipped, bool firstFind) {
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001131 SkASSERT(startIndex != endIndex);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001132 int count = fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001133 SkASSERT(startIndex < endIndex ? startIndex < count - 1
1134 : startIndex > 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001135 int step = SkSign32(endIndex - startIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001136 int end = nextSpan(startIndex, step);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001137 SkASSERT(end >= 0);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001138 Span* endSpan = &fTs[end];
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001139 Segment* other;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001140 if (isSimple(end)) {
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001141 // mark the smaller of startIndex, endIndex done, and all adjacent
1142 // spans with the same T value (but not 'other' spans)
caryclark@google.com495f8e42012-05-31 13:13:11 +00001143 markDone(SkMin32(startIndex, endIndex), winding);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001144 other = endSpan->fOther;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001145 nextStart = endSpan->fOtherIndex;
1146 nextEnd = nextStart + step;
1147 SkASSERT(step < 0 ? nextEnd >= 0 : nextEnd < other->fTs.count());
caryclark@google.com15fa1382012-05-07 20:49:36 +00001148 return other;
1149 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001150 // more than one viable candidate -- measure angles to find best
caryclark@google.com15fa1382012-05-07 20:49:36 +00001151 SkTDArray<Angle> angles;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001152 SkASSERT(startIndex - endIndex != 0);
1153 SkASSERT((startIndex - endIndex < 0) ^ (step < 0));
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001154 addTwoAngles(startIndex, end, angles);
1155 buildAngles(end, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001156 SkTDArray<Angle*> sorted;
1157 sortAngles(angles, sorted);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001158 int angleCount = angles.count();
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001159 int firstIndex = findStartingEdge(sorted, startIndex, end);
1160
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001161 SkASSERT(firstIndex >= 0);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001162 int startWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001163 int nextIndex = firstIndex + 1;
1164 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
1165 const Angle* foundAngle = NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001166 // iterate through the angle, and compute everyone's winding
1167 bool firstEdge = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001168 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001169 if (nextIndex == angleCount) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001170 nextIndex = 0;
1171 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001172 const Angle* nextAngle = sorted[nextIndex];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001173 int maxWinding = winding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001174 Segment* nextSegment = nextAngle->segment();
1175 int windValue = nextSegment->windValue(nextAngle);
1176 SkASSERT(windValue > 0);
1177 winding -= nextAngle->sign() * windValue;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001178 #if DEBUG_WINDING
1179 SkDebugf("%s maxWinding=%d winding=%d\n", __FUNCTION__, maxWinding,
1180 winding);
1181 #endif
1182 if (maxWinding * winding < 0) {
1183 flipped = -flipped;
1184 SkDebugf("flipped sign %d %d\n", maxWinding, winding);
1185 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001186 firstEdge = false;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001187 if (!winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001188 if (!foundAngle) {
1189 foundAngle = nextAngle;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001190 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001191 continue;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001192 }
1193 if (nextSegment->done()) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001194 continue;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001195 }
1196 // if the winding is non-zero, nextAngle does not connect to
1197 // current chain. If we haven't done so already, mark the angle
1198 // as done, record the winding value, and mark connected unambiguous
1199 // segments as well.
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001200 if (nextSegment->windSum(nextAngle) == SK_MinS32) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001201 if (abs(maxWinding) < abs(winding)) {
1202 maxWinding = winding;
1203 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001204 Span* last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001205 if (foundAngle) {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001206 last = nextSegment->markAndChaseWinding(nextAngle, maxWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001207 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001208 last = nextSegment->markAndChaseDone(nextAngle, maxWinding);
1209 }
1210 if (last) {
1211 *chase.append() = last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001212 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001213 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001214 } while (++nextIndex != lastIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001215 sorted[firstIndex]->segment()->
1216 markDone(SkMin32(startIndex, endIndex), startWinding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001217 if (!foundAngle) {
1218 return NULL;
1219 }
1220 nextStart = foundAngle->start();
1221 nextEnd = foundAngle->end();
1222 return foundAngle->segment();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001223 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001224
1225 int findStartingEdge(SkTDArray<Angle*>& sorted, int start, int end) {
1226 int angleCount = sorted.count();
1227 int firstIndex = -1;
1228 for (int angleIndex = 0; angleIndex < angleCount; ++angleIndex) {
1229 const Angle* angle = sorted[angleIndex];
1230 if (angle->segment() == this && angle->start() == end &&
1231 angle->end() == start) {
1232 firstIndex = angleIndex;
1233 break;
1234 }
1235 }
1236 return firstIndex;
1237 }
1238
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001239 // FIXME: this is tricky code; needs its own unit test
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001240 void findTooCloseToCall(int /* winding */ ) { // FIXME: winding should be considered
caryclark@google.com15fa1382012-05-07 20:49:36 +00001241 int count = fTs.count();
1242 if (count < 3) { // require t=0, x, 1 at minimum
1243 return;
1244 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001245 int matchIndex = 0;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001246 int moCount;
1247 Span* match;
1248 Segment* mOther;
1249 do {
1250 match = &fTs[matchIndex];
1251 mOther = match->fOther;
1252 moCount = mOther->fTs.count();
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001253 if (moCount >= 3) {
1254 break;
1255 }
1256 if (++matchIndex >= count) {
1257 return;
1258 }
1259 } while (true); // require t=0, x, 1 at minimum
caryclark@google.com15fa1382012-05-07 20:49:36 +00001260 // OPTIMIZATION: defer matchPt until qualifying toCount is found?
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001261 const SkPoint* matchPt = &xyAtT(match);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001262 // look for a pair of nearby T values that map to the same (x,y) value
1263 // if found, see if the pair of other segments share a common point. If
1264 // so, the span from here to there is coincident.
caryclark@google.com15fa1382012-05-07 20:49:36 +00001265 for (int index = matchIndex + 1; index < count; ++index) {
1266 Span* test = &fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001267 if (test->fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001268 continue;
1269 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001270 Segment* tOther = test->fOther;
1271 int toCount = tOther->fTs.count();
1272 if (toCount < 3) { // require t=0, x, 1 at minimum
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001273 continue;
1274 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001275 const SkPoint* testPt = &xyAtT(test);
1276 if (*matchPt != *testPt) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001277 matchIndex = index;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001278 moCount = toCount;
1279 match = test;
1280 mOther = tOther;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001281 matchPt = testPt;
1282 continue;
1283 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001284 int moStart = -1;
1285 int moEnd = -1;
1286 double moStartT, moEndT;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001287 for (int moIndex = 0; moIndex < moCount; ++moIndex) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001288 Span& moSpan = mOther->fTs[moIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001289 if (moSpan.fDone) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001290 continue;
1291 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001292 if (moSpan.fOther == this) {
1293 if (moSpan.fOtherT == match->fT) {
1294 moStart = moIndex;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001295 moStartT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001296 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001297 continue;
1298 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001299 if (moSpan.fOther == tOther) {
1300 SkASSERT(moEnd == -1);
1301 moEnd = moIndex;
1302 moEndT = moSpan.fT;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001303 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001304 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001305 if (moStart < 0 || moEnd < 0) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001306 continue;
1307 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001308 // FIXME: if moStartT, moEndT are initialized to NaN, can skip this test
1309 if (moStartT == moEndT) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001310 continue;
1311 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001312 int toStart = -1;
1313 int toEnd = -1;
1314 double toStartT, toEndT;
1315 for (int toIndex = 0; toIndex < toCount; ++toIndex) {
1316 Span& toSpan = tOther->fTs[toIndex];
1317 if (toSpan.fOther == this) {
1318 if (toSpan.fOtherT == test->fT) {
1319 toStart = toIndex;
1320 toStartT = toSpan.fT;
1321 }
1322 continue;
1323 }
1324 if (toSpan.fOther == mOther && toSpan.fOtherT == moEndT) {
1325 SkASSERT(toEnd == -1);
1326 toEnd = toIndex;
1327 toEndT = toSpan.fT;
1328 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001329 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001330 // FIXME: if toStartT, toEndT are initialized to NaN, can skip this test
1331 if (toStart <= 0 || toEnd <= 0) {
1332 continue;
1333 }
1334 if (toStartT == toEndT) {
1335 continue;
1336 }
1337 // test to see if the segment between there and here is linear
1338 if (!mOther->isLinear(moStart, moEnd)
1339 || !tOther->isLinear(toStart, toEnd)) {
1340 continue;
1341 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001342 // FIXME: defer implementation until the rest works
1343 // this may share code with regular coincident detection
1344 SkASSERT(0);
1345 #if 0
1346 if (flipped) {
1347 mOther->addTCancel(moStart, moEnd, tOther, tStart, tEnd);
1348 } else {
1349 mOther->addTCoincident(moStart, moEnd, tOther, tStart, tEnd);
1350 }
1351 #endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001352 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001353 }
1354
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001355 // OPTIMIZATION : for a pair of lines, can we compute points at T (cached)
1356 // and use more concise logic like the old edge walker code?
1357 // FIXME: this needs to deal with coincident edges
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001358 Segment* findTop(int& tIndex, int& endIndex) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001359 // iterate through T intersections and return topmost
1360 // topmost tangent from y-min to first pt is closer to horizontal
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001361 SkASSERT(!done());
1362 int firstT;
1363 int lastT;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001364 SkPoint topPt;
1365 topPt.fY = SK_ScalarMax;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001366 int count = fTs.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001367 // see if either end is not done since we want smaller Y of the pair
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001368 bool lastDone = true;
1369 for (int index = 0; index < count; ++index) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001370 const Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001371 if (!span.fDone || !lastDone) {
1372 const SkPoint& intercept = xyAtT(&span);
1373 if (topPt.fY > intercept.fY || (topPt.fY == intercept.fY
1374 && topPt.fX > intercept.fX)) {
1375 topPt = intercept;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001376 firstT = lastT = index;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001377 } else if (topPt == intercept) {
1378 lastT = index;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001379 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001380 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001381 lastDone = span.fDone;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001382 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001383 // sort the edges to find the leftmost
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001384 int step = 1;
1385 int end = nextSpan(firstT, step);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001386 if (end == -1) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001387 step = -1;
1388 end = nextSpan(firstT, step);
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001389 SkASSERT(end != -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001390 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001391 // if the topmost T is not on end, or is three-way or more, find left
1392 // look for left-ness from tLeft to firstT (matching y of other)
1393 SkTDArray<Angle> angles;
1394 SkASSERT(firstT - end != 0);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001395 addTwoAngles(end, firstT, angles);
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001396 buildAngles(firstT, angles);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001397 SkTDArray<Angle*> sorted;
1398 sortAngles(angles, sorted);
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001399 // skip edges that have already been processed
1400 firstT = -1;
1401 Segment* leftSegment;
1402 do {
1403 const Angle* angle = sorted[++firstT];
1404 leftSegment = angle->segment();
1405 tIndex = angle->end();
1406 endIndex = angle->start();
1407 } while (leftSegment->fTs[SkMin32(tIndex, endIndex)].fDone);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001408 return leftSegment;
1409 }
1410
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001411 // FIXME: not crazy about this
1412 // when the intersections are performed, the other index is into an
1413 // incomplete array. as the array grows, the indices become incorrect
1414 // while the following fixes the indices up again, it isn't smart about
1415 // skipping segments whose indices are already correct
1416 // assuming we leave the code that wrote the index in the first place
1417 void fixOtherTIndex() {
1418 int iCount = fTs.count();
1419 for (int i = 0; i < iCount; ++i) {
1420 Span& iSpan = fTs[i];
1421 double oT = iSpan.fOtherT;
1422 Segment* other = iSpan.fOther;
1423 int oCount = other->fTs.count();
1424 for (int o = 0; o < oCount; ++o) {
1425 Span& oSpan = other->fTs[o];
1426 if (oT == oSpan.fT && this == oSpan.fOther) {
1427 iSpan.fOtherIndex = o;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001428 break;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001429 }
1430 }
1431 }
1432 }
1433
caryclark@google.com495f8e42012-05-31 13:13:11 +00001434 // OPTIMIZATION: uses tail recursion. Unwise?
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001435 Span* innerChaseDone(int index, int step, int winding) {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001436 int end = nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001437 if (multipleSpans(index, end)) {
1438 return index >= 0 ? &fTs[index] : NULL;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001439 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001440 const Span& endSpan = fTs[end];
1441 Segment* other = endSpan.fOther;
1442 index = endSpan.fOtherIndex;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001443 int otherEnd = other->nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001444 Span* last = other->innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001445 other->markDone(SkMin32(index, otherEnd), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001446 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001447 }
1448
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001449 Span* innerChaseWinding(int index, int step, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001450 int end = nextSpan(index, step);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001451 if (multipleSpans(index, end)) {
1452 return index >= 0 ? &fTs[index] : NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001453 }
1454 const Span& endSpan = fTs[end];
1455 Segment* other = endSpan.fOther;
1456 index = endSpan.fOtherIndex;
1457 int otherEnd = other->nextSpan(index, step);
1458 int min = SkMin32(index, otherEnd);
1459 if (other->fTs[min].fWindSum != SK_MinS32) {
1460 SkASSERT(other->fTs[index].fWindSum == winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001461 return NULL;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001462 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001463 Span* last = other->innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001464 other->markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001465 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001466 }
1467
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001468 void init(const SkPoint pts[], SkPath::Verb verb) {
1469 fPts = pts;
1470 fVerb = verb;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001471 fDoneSpans = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001472 }
1473
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001474 bool intersected() const {
1475 return fTs.count() > 0;
1476 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001477
1478 bool isLinear(int start, int end) const {
1479 if (fVerb == SkPath::kLine_Verb) {
1480 return true;
1481 }
1482 if (fVerb == SkPath::kQuad_Verb) {
1483 SkPoint qPart[3];
1484 QuadSubDivide(fPts, fTs[start].fT, fTs[end].fT, qPart);
1485 return QuadIsLinear(qPart);
1486 } else {
1487 SkASSERT(fVerb == SkPath::kCubic_Verb);
1488 SkPoint cPart[4];
1489 CubicSubDivide(fPts, fTs[start].fT, fTs[end].fT, cPart);
1490 return CubicIsLinear(cPart);
1491 }
1492 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001493
1494 // OPTIMIZE: successive calls could start were the last leaves off
1495 // or calls could specialize to walk forwards or backwards
1496 bool isMissing(double startT) const {
1497 size_t tCount = fTs.count();
1498 for (size_t index = 0; index < tCount; ++index) {
1499 if (fabs(startT - fTs[index].fT) < FLT_EPSILON) {
1500 return false;
1501 }
1502 }
1503 return true;
1504 }
1505
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001506 bool isSimple(int end) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001507 int count = fTs.count();
1508 if (count == 2) {
1509 return true;
1510 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001511 double t = fTs[end].fT;
1512 if (t < FLT_EPSILON) {
1513 return fTs[1].fT >= FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001514 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001515 if (t > 1 - FLT_EPSILON) {
1516 return fTs[count - 2].fT <= 1 - FLT_EPSILON;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001517 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001518 return false;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001519 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001520
1521 bool isHorizontal() const {
1522 return fBounds.fTop == fBounds.fBottom;
1523 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001524
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001525 bool isVertical() const {
1526 return fBounds.fLeft == fBounds.fRight;
1527 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001528
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001529 SkScalar leftMost(int start, int end) const {
1530 return (*SegmentLeftMost[fVerb])(fPts, fTs[start].fT, fTs[end].fT);
1531 }
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001532
caryclark@google.com495f8e42012-05-31 13:13:11 +00001533 // this span is excluded by the winding rule -- chase the ends
1534 // as long as they are unambiguous to mark connections as done
1535 // and give them the same winding value
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001536 Span* markAndChaseDone(const Angle* angle, int winding) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001537 int index = angle->start();
1538 int endIndex = angle->end();
1539 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001540 Span* last = innerChaseDone(index, step, winding);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001541 markDone(SkMin32(index, endIndex), winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001542 return last;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001543 }
1544
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001545 Span* markAndChaseWinding(const Angle* angle, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001546 int index = angle->start();
1547 int endIndex = angle->end();
1548 int min = SkMin32(index, endIndex);
1549 int step = SkSign32(endIndex - index);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001550 Span* last = innerChaseWinding(index, step, winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001551 markWinding(min, winding);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001552 return last;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001553 }
1554
caryclark@google.com495f8e42012-05-31 13:13:11 +00001555 // FIXME: this should also mark spans with equal (x,y)
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001556 // This may be called when the segment is already marked done. While this
1557 // wastes time, it shouldn't do any more than spin through the T spans.
1558 // OPTIMIZATION: abort on first done found (assuming that this code is
1559 // always called to mark segments done).
caryclark@google.com495f8e42012-05-31 13:13:11 +00001560 void markDone(int index, int winding) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001561 // SkASSERT(!done());
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001562 double referenceT = fTs[index].fT;
1563 int lesser = index;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001564 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001565 Span& span = fTs[lesser];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001566 if (span.fDone) {
1567 continue;
1568 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001569 #if DEBUG_MARK_DONE
1570 const SkPoint& pt = xyAtT(&span);
1571 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1572 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1573 #endif
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001574 span.fDone = true;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001575 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1576 span.fWindSum = winding;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00001577 fDoneSpans++;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001578 }
1579 do {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001580 Span& span = fTs[index];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001581 // SkASSERT(!span.fDone);
1582 if (span.fDone) {
1583 continue;
1584 }
1585 #if DEBUG_MARK_DONE
1586 const SkPoint& pt = xyAtT(&span);
1587 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1588 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1589 #endif
1590 span.fDone = true;
1591 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1592 span.fWindSum = winding;
1593 fDoneSpans++;
1594 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
1595 }
1596
1597 void markWinding(int index, int winding) {
1598 SkASSERT(!done());
1599 double referenceT = fTs[index].fT;
1600 int lesser = index;
1601 while (--lesser >= 0 && referenceT - fTs[lesser].fT < FLT_EPSILON) {
1602 Span& span = fTs[lesser];
1603 if (span.fDone) {
1604 continue;
1605 }
1606 SkASSERT(span.fWindValue == 1 || winding == 0);
1607 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1608 #if DEBUG_MARK_DONE
1609 const SkPoint& pt = xyAtT(&span);
1610 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1611 __FUNCTION__, fID, lesser, span.fT, pt.fX, pt.fY, winding);
1612 #endif
1613 span.fWindSum = winding;
1614 }
1615 do {
1616 Span& span = fTs[index];
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001617 // SkASSERT(!span.fDone || span.fCoincident);
1618 if (span.fDone) {
1619 continue;
1620 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001621 SkASSERT(span.fWindValue == 1 || winding == 0);
1622 SkASSERT(span.fWindSum == SK_MinS32 || span.fWindSum == winding);
1623 #if DEBUG_MARK_DONE
1624 const SkPoint& pt = xyAtT(&span);
1625 SkDebugf("%s segment=%d index=%d t=%1.9g pt=(%1.9g,%1.9g) wind=%d\n",
1626 __FUNCTION__, fID, index, span.fT, pt.fX, pt.fY, winding);
1627 #endif
1628 span.fWindSum = winding;
1629 } while (++index < fTs.count() && fTs[index].fT - referenceT < FLT_EPSILON);
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001630 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001631
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001632 bool multipleSpans(int& index, int end) const {
1633 if (end > index ? end + 1 >= fTs.count() : end <= 0) {
1634 return false;
1635 }
1636 // return span if when chasing, two or more radiating spans are not done
1637 int lesser = SkMin32(index, end);
1638 if (!activeAngles(lesser)) {
1639 index = -1;
1640 }
1641 index = lesser;
1642 return true;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00001643 }
1644
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001645 // This has callers for two different situations: one establishes the end
1646 // of the current span, and one establishes the beginning of the next span
1647 // (thus the name). When this is looking for the end of the current span,
1648 // coincidence is found when the beginning Ts contain -step and the end
1649 // contains step. When it is looking for the beginning of the next, the
1650 // first Ts found can be ignored and the last Ts should contain -step.
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001651 // OPTIMIZATION: probably should split into two functions
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001652 int nextSpan(int from, int step) const {
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001653 const Span& fromSpan = fTs[from];
caryclark@google.com495f8e42012-05-31 13:13:11 +00001654 int count = fTs.count();
1655 int to = from;
caryclark@google.com495f8e42012-05-31 13:13:11 +00001656 while (step > 0 ? ++to < count : --to >= 0) {
1657 const Span& span = fTs[to];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001658 if ((step > 0 ? span.fT - fromSpan.fT : fromSpan.fT - span.fT) < FLT_EPSILON) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001659 continue;
1660 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001661 return to;
1662 }
1663 return -1;
1664 }
1665
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001666 const SkPoint* pts() const {
1667 return fPts;
1668 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001669
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001670 void reset() {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001671 init(NULL, (SkPath::Verb) -1);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001672 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
1673 fTs.reset();
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001674 }
1675
caryclark@google.com1577e8f2012-05-22 17:01:14 +00001676 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001677 const Span& span(int tIndex) const {
1678 return fTs[tIndex];
1679 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001680
1681 int spanSign(int startIndex, int endIndex) const {
1682 return startIndex < endIndex ? -fTs[startIndex].fWindValue :
1683 fTs[endIndex].fWindValue;
1684 }
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001685
1686 // OPTIMIZATION: mark as debugging only if used solely by tests
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001687 double t(int tIndex) const {
1688 return fTs[tIndex].fT;
1689 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001690
1691 void updatePts(const SkPoint pts[]) {
1692 fPts = pts;
1693 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001694
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001695 SkPath::Verb verb() const {
1696 return fVerb;
1697 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001698
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001699 int windSum(int tIndex) const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001700 return fTs[tIndex].fWindSum;
1701 }
caryclark@google.com495f8e42012-05-31 13:13:11 +00001702
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001703 int windSum(const Angle* angle) const {
caryclark@google.com495f8e42012-05-31 13:13:11 +00001704 int start = angle->start();
1705 int end = angle->end();
1706 int index = SkMin32(start, end);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00001707 return windSum(index);
caryclark@google.com495f8e42012-05-31 13:13:11 +00001708 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001709
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001710 int windValue(int tIndex) const {
1711 return fTs[tIndex].fWindValue;
1712 }
1713
1714 int windValue(const Angle* angle) const {
1715 int start = angle->start();
1716 int end = angle->end();
1717 int index = SkMin32(start, end);
1718 return windValue(index);
1719 }
1720
1721 SkScalar xAtT(const Span* span) const {
1722 return xyAtT(span).fX;
1723 }
1724
1725 const SkPoint& xyAtT(int index) const {
1726 return xyAtT(&fTs[index]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001727 }
1728
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001729 const SkPoint& xyAtT(const Span* span) const {
1730 if (!span->fPt) {
1731 if (span->fT == 0) {
1732 span->fPt = &fPts[0];
1733 } else if (span->fT == 1) {
1734 span->fPt = &fPts[fVerb];
1735 } else {
1736 SkPoint* pt = fIntersections.append();
1737 (*SegmentXYAtT[fVerb])(fPts, span->fT, pt);
1738 span->fPt = pt;
1739 }
1740 }
1741 return *span->fPt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001742 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001743
1744 SkScalar yAtT(int index) const {
1745 return yAtT(&fTs[index]);
1746 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001747
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001748 SkScalar yAtT(const Span* span) const {
1749 return xyAtT(span).fY;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001750 }
1751
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001752#if DEBUG_DUMP
1753 void dump() const {
1754 const char className[] = "Segment";
1755 const int tab = 4;
1756 for (int i = 0; i < fTs.count(); ++i) {
1757 SkPoint out;
1758 (*SegmentXYAtT[fVerb])(fPts, t(i), &out);
1759 SkDebugf("%*s [%d] %s.fTs[%d]=%1.9g (%1.9g,%1.9g) other=%d"
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001760 " otherT=%1.9g windSum=%d\n",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001761 tab + sizeof(className), className, fID,
1762 kLVerbStr[fVerb], i, fTs[i].fT, out.fX, out.fY,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001763 fTs[i].fOther->fID, fTs[i].fOtherT, fTs[i].fWindSum);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001764 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00001765 SkDebugf("%*s [%d] fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001766 tab + sizeof(className), className, fID,
caryclark@google.com15fa1382012-05-07 20:49:36 +00001767 fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001768 }
1769#endif
1770
1771private:
1772 const SkPoint* fPts;
1773 SkPath::Verb fVerb;
1774 Bounds fBounds;
caryclark@google.com15fa1382012-05-07 20:49:36 +00001775 SkTDArray<Span> fTs; // two or more (always includes t=0 t=1)
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00001776 // OPTIMIZATION:if intersections array is a pointer, the it could only
1777 // be allocated as needed instead of always initialized -- though maybe
1778 // the initialization is lightweight enough that it hardly matters
1779 mutable SkTDArray<SkPoint> fIntersections;
caryclark@google.comaf46cff2012-05-22 21:12:00 +00001780 int fDoneSpans; // used for quick check that segment is finished
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001781#if DEBUG_DUMP
1782 int fID;
1783#endif
1784};
1785
caryclark@google.comb9738012012-07-03 19:53:30 +00001786class Contour;
1787
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001788struct Coincidence {
caryclark@google.comb9738012012-07-03 19:53:30 +00001789 Contour* fContours[2];
1790 int fSegments[2];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001791 double fTs[2][2];
1792};
1793
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001794class Contour {
1795public:
1796 Contour() {
1797 reset();
1798#if DEBUG_DUMP
1799 fID = ++gContourID;
1800#endif
1801 }
1802
1803 bool operator<(const Contour& rh) const {
1804 return fBounds.fTop == rh.fBounds.fTop
1805 ? fBounds.fLeft < rh.fBounds.fLeft
1806 : fBounds.fTop < rh.fBounds.fTop;
1807 }
1808
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001809 void addCoincident(int index, Contour* other, int otherIndex,
1810 const Intersections& ts, bool swap) {
1811 Coincidence& coincidence = *fCoincidences.append();
caryclark@google.comb9738012012-07-03 19:53:30 +00001812 coincidence.fContours[0] = this;
1813 coincidence.fContours[1] = other;
1814 coincidence.fSegments[0] = index;
1815 coincidence.fSegments[1] = otherIndex;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001816 coincidence.fTs[swap][0] = ts.fT[0][0];
1817 coincidence.fTs[swap][1] = ts.fT[0][1];
1818 coincidence.fTs[!swap][0] = ts.fT[1][0];
1819 coincidence.fTs[!swap][1] = ts.fT[1][1];
1820 }
1821
1822 void addCross(const Contour* crosser) {
1823#ifdef DEBUG_CROSS
1824 for (int index = 0; index < fCrosses.count(); ++index) {
1825 SkASSERT(fCrosses[index] != crosser);
1826 }
1827#endif
1828 *fCrosses.append() = crosser;
1829 }
1830
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001831 void addCubic(const SkPoint pts[4]) {
1832 fSegments.push_back().addCubic(pts);
1833 fContainsCurves = true;
1834 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001835
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001836 int addLine(const SkPoint pts[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001837 fSegments.push_back().addLine(pts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001838 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001839 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001840
1841 void addOtherT(int segIndex, int tIndex, double otherT, int otherIndex) {
1842 fSegments[segIndex].addOtherT(tIndex, otherT, otherIndex);
1843 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001844
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001845 int addQuad(const SkPoint pts[3]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001846 fSegments.push_back().addQuad(pts);
1847 fContainsCurves = true;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001848 return fSegments.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001849 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001850
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001851 int addT(int segIndex, double newT, Contour* other, int otherIndex) {
1852 containsIntercepts();
1853 return fSegments[segIndex].addT(newT, &other->fSegments[otherIndex]);
1854 }
1855
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001856 const Bounds& bounds() const {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001857 return fBounds;
1858 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001859
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001860 void complete() {
1861 setBounds();
1862 fContainsIntercepts = false;
1863 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001864
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001865 void containsIntercepts() {
1866 fContainsIntercepts = true;
1867 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001868
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001869 const Segment* crossedSegment(const SkPoint& basePt, SkScalar& bestY,
1870 int &tIndex, double& hitT) {
1871 int segmentCount = fSegments.count();
1872 const Segment* bestSegment = NULL;
1873 for (int test = 0; test < segmentCount; ++test) {
1874 Segment* testSegment = &fSegments[test];
1875 const SkRect& bounds = testSegment->bounds();
1876 if (bounds.fTop < bestY) {
1877 continue;
1878 }
1879 if (bounds.fTop > basePt.fY) {
1880 continue;
1881 }
1882 if (bounds.fLeft > basePt.fX) {
1883 continue;
1884 }
1885 if (bounds.fRight < basePt.fX) {
1886 continue;
1887 }
1888 double testHitT;
1889 int testT = testSegment->crossedSpan(basePt, bestY, testHitT);
1890 if (testT >= 0) {
1891 bestSegment = testSegment;
1892 tIndex = testT;
1893 hitT = testHitT;
1894 }
1895 }
1896 return bestSegment;
1897 }
1898
1899 bool crosses(const Contour* crosser) const {
1900 if (this == crosser) {
1901 return true;
1902 }
1903 for (int index = 0; index < fCrosses.count(); ++index) {
1904 if (fCrosses[index] == crosser) {
1905 return true;
1906 }
1907 }
1908 return false;
1909 }
1910
caryclark@google.coma833b5c2012-04-30 19:38:50 +00001911 void findTooCloseToCall(int winding) {
1912 int segmentCount = fSegments.count();
1913 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1914 fSegments[sIndex].findTooCloseToCall(winding);
1915 }
1916 }
1917
caryclark@google.comb45a1b42012-05-18 20:50:33 +00001918 void fixOtherTIndex() {
1919 int segmentCount = fSegments.count();
1920 for (int sIndex = 0; sIndex < segmentCount; ++sIndex) {
1921 fSegments[sIndex].fixOtherTIndex();
1922 }
1923 }
1924
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001925 void reset() {
1926 fSegments.reset();
1927 fBounds.set(SK_ScalarMax, SK_ScalarMax, SK_ScalarMax, SK_ScalarMax);
caryclark@google.com15fa1382012-05-07 20:49:36 +00001928 fContainsCurves = fContainsIntercepts = false;
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00001929 fWindingSum = SK_MinS32;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00001930 }
caryclark@google.comb9738012012-07-03 19:53:30 +00001931
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001932 void resolveCoincidence(int winding) {
1933 int count = fCoincidences.count();
1934 for (int index = 0; index < count; ++index) {
1935 Coincidence& coincidence = fCoincidences[index];
caryclark@google.comb9738012012-07-03 19:53:30 +00001936 Contour* thisContour = coincidence.fContours[0];
1937 Contour* otherContour = coincidence.fContours[1];
1938 int thisIndex = coincidence.fSegments[0];
1939 int otherIndex = coincidence.fSegments[1];
1940 Segment& thisOne = thisContour->fSegments[thisIndex];
1941 Segment& other = otherContour->fSegments[otherIndex];
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001942 double startT = coincidence.fTs[0][0];
1943 double endT = coincidence.fTs[0][1];
1944 if (startT > endT) {
1945 SkTSwap<double>(startT, endT);
1946 }
1947 SkASSERT(endT - startT >= FLT_EPSILON);
1948 double oStartT = coincidence.fTs[1][0];
1949 double oEndT = coincidence.fTs[1][1];
1950 if (oStartT > oEndT) {
1951 SkTSwap<double>(oStartT, oEndT);
1952 }
1953 SkASSERT(oEndT - oStartT >= FLT_EPSILON);
caryclark@google.comb9738012012-07-03 19:53:30 +00001954 if (winding > 0 || thisOne.cancels(other)) {
1955 // make sure startT and endT have t entries
1956 if (thisOne.isMissing(startT) || other.isMissing(oEndT)) {
1957 thisOne.addTPair(startT, other, oEndT);
1958 }
1959 if (thisOne.isMissing(endT) || other.isMissing(oStartT)) {
1960 other.addTPair(oStartT, thisOne, endT);
1961 }
1962 thisOne.addTCancel(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001963 } else {
caryclark@google.comb9738012012-07-03 19:53:30 +00001964 if (thisOne.isMissing(startT) || other.isMissing(oStartT)) {
1965 thisOne.addTPair(startT, other, oStartT);
1966 }
1967 if (thisOne.isMissing(endT) || other.isMissing(oEndT)) {
1968 other.addTPair(oEndT, thisOne, endT);
1969 }
1970 thisOne.addTCoincident(startT, endT, other, oStartT, oEndT);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001971 }
1972 }
1973 }
1974
1975 const SkTArray<Segment>& segments() {
1976 return fSegments;
1977 }
1978
1979 void setWinding(int winding) {
1980 SkASSERT(fWindingSum < 0);
1981 fWindingSum = winding;
1982 }
1983
caryclark@google.com15fa1382012-05-07 20:49:36 +00001984 // OPTIMIZATION: feel pretty uneasy about this. It seems like once again
1985 // we need to sort and walk edges in y, but that on the surface opens the
1986 // same can of worms as before. But then, this is a rough sort based on
1987 // segments' top, and not a true sort, so it could be ameniable to regular
1988 // sorting instead of linear searching. Still feel like I'm missing something
caryclark@google.com8dcf1142012-07-02 20:27:02 +00001989 Segment* topSegment(SkScalar& bestY) {
caryclark@google.com15fa1382012-05-07 20:49:36 +00001990 int segmentCount = fSegments.count();
1991 SkASSERT(segmentCount > 0);
1992 int best = -1;
1993 Segment* bestSegment = NULL;
1994 while (++best < segmentCount) {
1995 Segment* testSegment = &fSegments[best];
1996 if (testSegment->done()) {
1997 continue;
1998 }
1999 bestSegment = testSegment;
2000 break;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002001 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002002 if (!bestSegment) {
2003 return NULL;
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002004 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002005 SkScalar bestTop = bestSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002006 for (int test = best + 1; test < segmentCount; ++test) {
2007 Segment* testSegment = &fSegments[test];
2008 if (testSegment->done()) {
2009 continue;
2010 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002011 if (testSegment->bounds().fTop > bestTop) {
2012 continue;
2013 }
2014 SkScalar testTop = testSegment->activeTop();
caryclark@google.com15fa1382012-05-07 20:49:36 +00002015 if (bestTop > testTop) {
2016 bestTop = testTop;
2017 bestSegment = testSegment;
2018 }
2019 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002020 bestY = bestTop;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002021 return bestSegment;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002022 }
2023
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002024 int updateSegment(int index, const SkPoint* pts) {
2025 Segment& segment = fSegments[index];
2026 segment.updatePts(pts);
2027 return segment.verb() + 1;
2028 }
2029
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002030 int windSum() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002031 if (fWindingSum >= 0) {
2032 return fWindingSum;
2033 }
2034 // check peers
2035 int count = fCrosses.count();
2036 for (int index = 0; index < count; ++index) {
2037 const Contour* crosser = fCrosses[index];
2038 if (0 <= crosser->fWindingSum) {
2039 fWindingSum = crosser->fWindingSum;
2040 break;
2041 }
2042 }
2043 return fWindingSum;
2044 }
2045
2046#if DEBUG_TEST
2047 SkTArray<Segment>& debugSegments() {
2048 return fSegments;
2049 }
2050#endif
2051
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002052#if DEBUG_DUMP
2053 void dump() {
2054 int i;
2055 const char className[] = "Contour";
2056 const int tab = 4;
2057 SkDebugf("%s %p (contour=%d)\n", className, this, fID);
2058 for (i = 0; i < fSegments.count(); ++i) {
2059 SkDebugf("%*s.fSegments[%d]:\n", tab + sizeof(className),
2060 className, i);
2061 fSegments[i].dump();
2062 }
2063 SkDebugf("%*s.fBounds=(l:%1.9g, t:%1.9g r:%1.9g, b:%1.9g)\n",
2064 tab + sizeof(className), className,
2065 fBounds.fLeft, fBounds.fTop,
2066 fBounds.fRight, fBounds.fBottom);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002067 SkDebugf("%*s.fContainsIntercepts=%d\n", tab + sizeof(className),
2068 className, fContainsIntercepts);
2069 SkDebugf("%*s.fContainsCurves=%d\n", tab + sizeof(className),
2070 className, fContainsCurves);
2071 }
2072#endif
2073
2074protected:
2075 void setBounds() {
2076 int count = fSegments.count();
2077 if (count == 0) {
2078 SkDebugf("%s empty contour\n", __FUNCTION__);
2079 SkASSERT(0);
2080 // FIXME: delete empty contour?
2081 return;
2082 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002083 fBounds = fSegments.front().bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002084 for (int index = 1; index < count; ++index) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002085 fBounds.add(fSegments[index].bounds());
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002086 }
2087 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002088
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002089private:
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002090 SkTArray<Segment> fSegments;
2091 SkTDArray<Coincidence> fCoincidences;
2092 SkTDArray<const Contour*> fCrosses;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002093 Bounds fBounds;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002094 bool fContainsIntercepts;
2095 bool fContainsCurves;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002096 int fWindingSum; // initial winding number outside
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002097#if DEBUG_DUMP
2098 int fID;
2099#endif
2100};
2101
2102class EdgeBuilder {
2103public:
2104
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002105EdgeBuilder(const SkPath& path, SkTArray<Contour>& contours)
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002106 : fPath(path)
2107 , fCurrentContour(NULL)
2108 , fContours(contours)
2109{
2110#if DEBUG_DUMP
2111 gContourID = 0;
2112 gSegmentID = 0;
2113#endif
2114 walk();
2115}
2116
2117protected:
2118
2119void complete() {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002120 if (fCurrentContour && fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002121 fCurrentContour->complete();
2122 fCurrentContour = NULL;
2123 }
2124}
2125
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002126void walk() {
2127 // FIXME:remove once we can access path pts directly
2128 SkPath::RawIter iter(fPath); // FIXME: access path directly when allowed
2129 SkPoint pts[4];
2130 SkPath::Verb verb;
2131 do {
2132 verb = iter.next(pts);
2133 *fPathVerbs.append() = verb;
2134 if (verb == SkPath::kMove_Verb) {
2135 *fPathPts.append() = pts[0];
2136 } else if (verb >= SkPath::kLine_Verb && verb <= SkPath::kCubic_Verb) {
2137 fPathPts.append(verb, &pts[1]);
2138 }
2139 } while (verb != SkPath::kDone_Verb);
2140 // FIXME: end of section to remove once path pts are accessed directly
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002141
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002142 SkPath::Verb reducedVerb;
2143 uint8_t* verbPtr = fPathVerbs.begin();
2144 const SkPoint* pointsPtr = fPathPts.begin();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002145 const SkPoint* finalCurveStart = NULL;
2146 const SkPoint* finalCurveEnd = NULL;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002147 while ((verb = (SkPath::Verb) *verbPtr++) != SkPath::kDone_Verb) {
2148 switch (verb) {
2149 case SkPath::kMove_Verb:
2150 complete();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002151 if (!fCurrentContour) {
2152 fCurrentContour = fContours.push_back_n(1);
2153 finalCurveEnd = pointsPtr++;
2154 *fExtra.append() = -1; // start new contour
2155 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002156 continue;
2157 case SkPath::kLine_Verb:
2158 // skip degenerate points
2159 if (pointsPtr[-1].fX != pointsPtr[0].fX
2160 || pointsPtr[-1].fY != pointsPtr[0].fY) {
2161 fCurrentContour->addLine(&pointsPtr[-1]);
2162 }
2163 break;
2164 case SkPath::kQuad_Verb:
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002165
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002166 reducedVerb = QuadReduceOrder(&pointsPtr[-1], fReducePts);
2167 if (reducedVerb == 0) {
2168 break; // skip degenerate points
2169 }
2170 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002171 *fExtra.append() =
2172 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002173 break;
2174 }
2175 fCurrentContour->addQuad(&pointsPtr[-1]);
2176 break;
2177 case SkPath::kCubic_Verb:
2178 reducedVerb = CubicReduceOrder(&pointsPtr[-1], fReducePts);
2179 if (reducedVerb == 0) {
2180 break; // skip degenerate points
2181 }
2182 if (reducedVerb == 1) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002183 *fExtra.append() =
2184 fCurrentContour->addLine(fReducePts.end() - 2);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002185 break;
2186 }
2187 if (reducedVerb == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002188 *fExtra.append() =
2189 fCurrentContour->addQuad(fReducePts.end() - 3);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002190 break;
2191 }
2192 fCurrentContour->addCubic(&pointsPtr[-1]);
2193 break;
2194 case SkPath::kClose_Verb:
2195 SkASSERT(fCurrentContour);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002196 if (finalCurveStart && finalCurveEnd
2197 && *finalCurveStart != *finalCurveEnd) {
2198 *fReducePts.append() = *finalCurveStart;
2199 *fReducePts.append() = *finalCurveEnd;
2200 *fExtra.append() =
2201 fCurrentContour->addLine(fReducePts.end() - 2);
2202 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002203 complete();
2204 continue;
2205 default:
2206 SkDEBUGFAIL("bad verb");
2207 return;
2208 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002209 finalCurveStart = &pointsPtr[verb - 1];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002210 pointsPtr += verb;
2211 SkASSERT(fCurrentContour);
2212 }
2213 complete();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002214 if (fCurrentContour && !fCurrentContour->segments().count()) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002215 fContours.pop_back();
2216 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002217 // correct pointers in contours since fReducePts may have moved as it grew
2218 int cIndex = 0;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002219 int extraCount = fExtra.count();
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002220 SkASSERT(extraCount == 0 || fExtra[0] == -1);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002221 int eIndex = 0;
2222 int rIndex = 0;
2223 while (++eIndex < extraCount) {
2224 int offset = fExtra[eIndex];
2225 if (offset < 0) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002226 ++cIndex;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002227 continue;
2228 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002229 fCurrentContour = &fContours[cIndex];
2230 rIndex += fCurrentContour->updateSegment(offset - 1,
2231 &fReducePts[rIndex]);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002232 }
2233 fExtra.reset(); // we're done with this
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002234}
2235
2236private:
2237 const SkPath& fPath;
2238 SkTDArray<SkPoint> fPathPts; // FIXME: point directly to path pts instead
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002239 SkTDArray<uint8_t> fPathVerbs; // FIXME: remove
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002240 Contour* fCurrentContour;
2241 SkTArray<Contour>& fContours;
2242 SkTDArray<SkPoint> fReducePts; // segments created on the fly
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002243 SkTDArray<int> fExtra; // -1 marks new contour, > 0 offsets into contour
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002244};
2245
2246class Work {
2247public:
2248 enum SegmentType {
2249 kHorizontalLine_Segment = -1,
2250 kVerticalLine_Segment = 0,
2251 kLine_Segment = SkPath::kLine_Verb,
2252 kQuad_Segment = SkPath::kQuad_Verb,
2253 kCubic_Segment = SkPath::kCubic_Verb,
2254 };
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002255
2256 void addCoincident(Work& other, const Intersections& ts, bool swap) {
2257 fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
2258 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002259
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002260 // FIXME: does it make sense to write otherIndex now if we're going to
2261 // fix it up later?
2262 void addOtherT(int index, double otherT, int otherIndex) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002263 fContour->addOtherT(fIndex, index, otherT, otherIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002264 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002265
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002266 // Avoid collapsing t values that are close to the same since
2267 // we walk ts to describe consecutive intersections. Since a pair of ts can
2268 // be nearly equal, any problems caused by this should be taken care
2269 // of later.
2270 // On the edge or out of range values are negative; add 2 to get end
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002271 int addT(double newT, const Work& other) {
2272 return fContour->addT(fIndex, newT, other.fContour, other.fIndex);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002273 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002274
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002275 bool advance() {
2276 return ++fIndex < fLast;
2277 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002278
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002279 SkScalar bottom() const {
2280 return bounds().fBottom;
2281 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002282
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002283 const Bounds& bounds() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002284 return fContour->segments()[fIndex].bounds();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002285 }
2286
2287 const SkPoint* cubic() const {
2288 return fCubic;
2289 }
2290
2291 void init(Contour* contour) {
2292 fContour = contour;
2293 fIndex = 0;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002294 fLast = contour->segments().count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002295 }
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002296
2297 bool isAdjacent(const Work& next) {
2298 return fContour == next.fContour && fIndex + 1 == next.fIndex;
2299 }
2300
2301 bool isFirstLast(const Work& next) {
2302 return fContour == next.fContour && fIndex == 0
2303 && next.fIndex == fLast - 1;
2304 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002305
2306 SkScalar left() const {
2307 return bounds().fLeft;
2308 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002309
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002310 void promoteToCubic() {
2311 fCubic[0] = pts()[0];
2312 fCubic[2] = pts()[1];
2313 fCubic[3] = pts()[2];
2314 fCubic[1].fX = (fCubic[0].fX + fCubic[2].fX * 2) / 3;
2315 fCubic[1].fY = (fCubic[0].fY + fCubic[2].fY * 2) / 3;
2316 fCubic[2].fX = (fCubic[3].fX + fCubic[2].fX * 2) / 3;
2317 fCubic[2].fY = (fCubic[3].fY + fCubic[2].fY * 2) / 3;
2318 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002319
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002320 const SkPoint* pts() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002321 return fContour->segments()[fIndex].pts();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002322 }
2323
2324 SkScalar right() const {
2325 return bounds().fRight;
2326 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002327
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002328 ptrdiff_t segmentIndex() const {
2329 return fIndex;
2330 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002331
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002332 SegmentType segmentType() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002333 const Segment& segment = fContour->segments()[fIndex];
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002334 SegmentType type = (SegmentType) segment.verb();
2335 if (type != kLine_Segment) {
2336 return type;
2337 }
2338 if (segment.isHorizontal()) {
2339 return kHorizontalLine_Segment;
2340 }
2341 if (segment.isVertical()) {
2342 return kVerticalLine_Segment;
2343 }
2344 return kLine_Segment;
2345 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002346
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002347 bool startAfter(const Work& after) {
2348 fIndex = after.fIndex;
2349 return advance();
2350 }
2351
2352 SkScalar top() const {
2353 return bounds().fTop;
2354 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002355
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002356 SkPath::Verb verb() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002357 return fContour->segments()[fIndex].verb();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002358 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002359
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002360 SkScalar x() const {
2361 return bounds().fLeft;
2362 }
2363
2364 bool xFlipped() const {
2365 return x() != pts()[0].fX;
2366 }
2367
2368 SkScalar y() const {
2369 return bounds().fTop;
2370 }
2371
2372 bool yFlipped() const {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002373 return y() != pts()[0].fY;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002374 }
2375
2376protected:
2377 Contour* fContour;
2378 SkPoint fCubic[4];
2379 int fIndex;
2380 int fLast;
2381};
2382
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002383#if DEBUG_ADD_INTERSECTING_TS
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002384static void debugShowLineIntersection(int pts, const Work& wt,
2385 const Work& wn, const double wtTs[2], const double wnTs[2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002386 if (!pts) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002387 SkDebugf("%s no intersect (%1.9g,%1.9g %1.9g,%1.9g) (%1.9g,%1.9g %1.9g,%1.9g)\n",
2388 __FUNCTION__, wt.pts()[0].fX, wt.pts()[0].fY,
2389 wt.pts()[1].fX, wt.pts()[1].fY, wn.pts()[0].fX, wn.pts()[0].fY,
2390 wn.pts()[1].fX, wn.pts()[1].fY);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002391 return;
2392 }
2393 SkPoint wtOutPt, wnOutPt;
2394 LineXYAtT(wt.pts(), wtTs[0], &wtOutPt);
2395 LineXYAtT(wn.pts(), wnTs[0], &wnOutPt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002396 SkDebugf("%s wtTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002397 __FUNCTION__,
2398 wtTs[0], wt.pts()[0].fX, wt.pts()[0].fY,
2399 wt.pts()[1].fX, wt.pts()[1].fY, wtOutPt.fX, wtOutPt.fY);
2400 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002401 SkDebugf(" wtTs[1]=%g", wtTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002402 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002403 SkDebugf(" wnTs[0]=%g (%g,%g, %g,%g) (%g,%g)",
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002404 wnTs[0], wn.pts()[0].fX, wn.pts()[0].fY,
2405 wn.pts()[1].fX, wn.pts()[1].fY, wnOutPt.fX, wnOutPt.fY);
2406 if (pts == 2) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002407 SkDebugf(" wnTs[1]=%g", wnTs[1]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002408 }
caryclark@google.comb9738012012-07-03 19:53:30 +00002409 SkDebugf("\n");
2410}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002411#else
2412static void debugShowLineIntersection(int , const Work& ,
2413 const Work& , const double [2], const double [2]) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002414}
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002415#endif
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002416
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002417static bool addIntersectTs(Contour* test, Contour* next) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002418
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002419 if (test != next) {
2420 if (test->bounds().fBottom < next->bounds().fTop) {
2421 return false;
2422 }
2423 if (!Bounds::Intersects(test->bounds(), next->bounds())) {
2424 return true;
2425 }
2426 }
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002427 Work wt;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002428 wt.init(test);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002429 bool foundCommonContour = test == next;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002430 do {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002431 Work wn;
2432 wn.init(next);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002433 if (test == next && !wn.startAfter(wt)) {
2434 continue;
2435 }
2436 do {
2437 if (!Bounds::Intersects(wt.bounds(), wn.bounds())) {
2438 continue;
2439 }
2440 int pts;
2441 Intersections ts;
2442 bool swap = false;
2443 switch (wt.segmentType()) {
2444 case Work::kHorizontalLine_Segment:
2445 swap = true;
2446 switch (wn.segmentType()) {
2447 case Work::kHorizontalLine_Segment:
2448 case Work::kVerticalLine_Segment:
2449 case Work::kLine_Segment: {
2450 pts = HLineIntersect(wn.pts(), wt.left(),
2451 wt.right(), wt.y(), wt.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002452 debugShowLineIntersection(pts, wt, wn,
2453 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002454 break;
2455 }
2456 case Work::kQuad_Segment: {
2457 pts = HQuadIntersect(wn.pts(), wt.left(),
2458 wt.right(), wt.y(), wt.xFlipped(), ts);
2459 break;
2460 }
2461 case Work::kCubic_Segment: {
2462 pts = HCubicIntersect(wn.pts(), wt.left(),
2463 wt.right(), wt.y(), wt.xFlipped(), ts);
2464 break;
2465 }
2466 default:
2467 SkASSERT(0);
2468 }
2469 break;
2470 case Work::kVerticalLine_Segment:
2471 swap = true;
2472 switch (wn.segmentType()) {
2473 case Work::kHorizontalLine_Segment:
2474 case Work::kVerticalLine_Segment:
2475 case Work::kLine_Segment: {
2476 pts = VLineIntersect(wn.pts(), wt.top(),
2477 wt.bottom(), wt.x(), wt.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002478 debugShowLineIntersection(pts, wt, wn,
2479 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002480 break;
2481 }
2482 case Work::kQuad_Segment: {
2483 pts = VQuadIntersect(wn.pts(), wt.top(),
2484 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2485 break;
2486 }
2487 case Work::kCubic_Segment: {
2488 pts = VCubicIntersect(wn.pts(), wt.top(),
2489 wt.bottom(), wt.x(), wt.yFlipped(), ts);
2490 break;
2491 }
2492 default:
2493 SkASSERT(0);
2494 }
2495 break;
2496 case Work::kLine_Segment:
2497 switch (wn.segmentType()) {
2498 case Work::kHorizontalLine_Segment:
2499 pts = HLineIntersect(wt.pts(), wn.left(),
2500 wn.right(), wn.y(), wn.xFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002501 debugShowLineIntersection(pts, wt, wn,
2502 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002503 break;
2504 case Work::kVerticalLine_Segment:
2505 pts = VLineIntersect(wt.pts(), wn.top(),
2506 wn.bottom(), wn.x(), wn.yFlipped(), ts);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002507 debugShowLineIntersection(pts, wt, wn,
2508 ts.fT[1], ts.fT[0]);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002509 break;
2510 case Work::kLine_Segment: {
2511 pts = LineIntersect(wt.pts(), wn.pts(), ts);
2512 debugShowLineIntersection(pts, wt, wn,
2513 ts.fT[1], ts.fT[0]);
2514 break;
2515 }
2516 case Work::kQuad_Segment: {
2517 swap = true;
2518 pts = QuadLineIntersect(wn.pts(), wt.pts(), ts);
2519 break;
2520 }
2521 case Work::kCubic_Segment: {
2522 swap = true;
2523 pts = CubicLineIntersect(wn.pts(), wt.pts(), ts);
2524 break;
2525 }
2526 default:
2527 SkASSERT(0);
2528 }
2529 break;
2530 case Work::kQuad_Segment:
2531 switch (wn.segmentType()) {
2532 case Work::kHorizontalLine_Segment:
2533 pts = HQuadIntersect(wt.pts(), wn.left(),
2534 wn.right(), wn.y(), wn.xFlipped(), ts);
2535 break;
2536 case Work::kVerticalLine_Segment:
2537 pts = VQuadIntersect(wt.pts(), wn.top(),
2538 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2539 break;
2540 case Work::kLine_Segment: {
2541 pts = QuadLineIntersect(wt.pts(), wn.pts(), ts);
2542 break;
2543 }
2544 case Work::kQuad_Segment: {
2545 pts = QuadIntersect(wt.pts(), wn.pts(), ts);
2546 break;
2547 }
2548 case Work::kCubic_Segment: {
2549 wt.promoteToCubic();
2550 pts = CubicIntersect(wt.cubic(), wn.pts(), ts);
2551 break;
2552 }
2553 default:
2554 SkASSERT(0);
2555 }
2556 break;
2557 case Work::kCubic_Segment:
2558 switch (wn.segmentType()) {
2559 case Work::kHorizontalLine_Segment:
2560 pts = HCubicIntersect(wt.pts(), wn.left(),
2561 wn.right(), wn.y(), wn.xFlipped(), ts);
2562 break;
2563 case Work::kVerticalLine_Segment:
2564 pts = VCubicIntersect(wt.pts(), wn.top(),
2565 wn.bottom(), wn.x(), wn.yFlipped(), ts);
2566 break;
2567 case Work::kLine_Segment: {
2568 pts = CubicLineIntersect(wt.pts(), wn.pts(), ts);
2569 break;
2570 }
2571 case Work::kQuad_Segment: {
2572 wn.promoteToCubic();
2573 pts = CubicIntersect(wt.pts(), wn.cubic(), ts);
2574 break;
2575 }
2576 case Work::kCubic_Segment: {
2577 pts = CubicIntersect(wt.pts(), wn.pts(), ts);
2578 break;
2579 }
2580 default:
2581 SkASSERT(0);
2582 }
2583 break;
2584 default:
2585 SkASSERT(0);
2586 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002587 if (!foundCommonContour && pts > 0) {
2588 test->addCross(next);
2589 next->addCross(test);
2590 foundCommonContour = true;
2591 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002592 // in addition to recording T values, record matching segment
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002593 if (pts == 2 && wn.segmentType() <= Work::kLine_Segment
2594 && wt.segmentType() <= Work::kLine_Segment) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002595 wt.addCoincident(wn, ts, swap);
2596 continue;
caryclark@google.coma3f05fa2012-06-01 17:44:28 +00002597 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002598 for (int pt = 0; pt < pts; ++pt) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002599 SkASSERT(ts.fT[0][pt] >= 0 && ts.fT[0][pt] <= 1);
2600 SkASSERT(ts.fT[1][pt] >= 0 && ts.fT[1][pt] <= 1);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002601 int testTAt = wt.addT(ts.fT[swap][pt], wn);
2602 int nextTAt = wn.addT(ts.fT[!swap][pt], wt);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002603 wt.addOtherT(testTAt, ts.fT[!swap][pt], nextTAt);
2604 wn.addOtherT(nextTAt, ts.fT[swap][pt], testTAt);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002605 }
2606 } while (wn.advance());
2607 } while (wt.advance());
2608 return true;
2609}
2610
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002611// resolve any coincident pairs found while intersecting, and
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002612// see if coincidence is formed by clipping non-concident segments
2613static void coincidenceCheck(SkTDArray<Contour*>& contourList, int winding) {
2614 int contourCount = contourList.count();
caryclark@google.comf25edfe2012-06-01 18:20:10 +00002615 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002616 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002617 contour->findTooCloseToCall(winding);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002618 }
2619 for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
2620 Contour* contour = contourList[cIndex];
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002621 contour->resolveCoincidence(winding);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002622 }
2623}
2624
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002625// project a ray from the top of the contour up and see if it hits anything
2626// note: when we compute line intersections, we keep track of whether
2627// two contours touch, so we need only look at contours not touching this one.
2628// OPTIMIZATION: sort contourList vertically to avoid linear walk
2629static int innerContourCheck(SkTDArray<Contour*>& contourList,
2630 Contour* baseContour, const SkPoint& basePt) {
2631 int contourCount = contourList.count();
2632 int winding = 0;
2633 SkScalar bestY = SK_ScalarMin;
2634 for (int cTest = 0; cTest < contourCount; ++cTest) {
2635 Contour* contour = contourList[cTest];
2636 if (basePt.fY < contour->bounds().fTop) {
2637 continue;
2638 }
2639 if (bestY > contour->bounds().fBottom) {
2640 continue;
2641 }
2642 if (baseContour->crosses(contour)) {
2643 continue;
2644 }
2645 int tIndex;
2646 double tHit;
2647 const Segment* test = contour->crossedSegment(basePt, bestY, tIndex,
2648 tHit);
2649 if (!test) {
2650 continue;
2651 }
2652 // If the ray hit the end of a span, we need to construct the wheel of
2653 // angles to find the span closest to the ray -- even if there are just
2654 // two spokes on the wheel.
2655 if (tHit == test->t(tIndex)) {
2656 SkTDArray<Angle> angles;
2657 int end = test->nextSpan(tIndex, 1);
2658 if (end < 0) {
2659 end = test->nextSpan(tIndex, -1);
2660 }
2661 test->addTwoAngles(tIndex, end, angles);
2662 // test->buildAnglesInner(tIndex, angles);
2663 test->buildAngles(tIndex, angles);
2664 SkTDArray<Angle*> sorted;
2665 sortAngles(angles, sorted);
2666 const Angle* angle = sorted[0];
2667 test = angle->segment();
2668 SkScalar testDx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2669 if (testDx == 0) {
2670 angle = *(sorted.end() - 1);
2671 test = angle->segment();
2672 SkASSERT((*SegmentDXAtT[test->verb()])(test->pts(), tHit) != 0);
2673 }
2674 tIndex = angle->start(); // lesser Y
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002675 winding = test->windSum(SkMin32(tIndex, angle->end()));
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002676 #if DEBUG_WINDING
2677 SkDebugf("%s 1 winding=%d\n", __FUNCTION__, winding);
2678 #endif
2679 } else {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002680 winding = test->windSum(tIndex);
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002681 #if DEBUG_WINDING
2682 SkDebugf("%s 2 winding=%d\n", __FUNCTION__, winding);
2683 #endif
2684 }
2685 // see if a + change in T results in a +/- change in X (compute x'(T))
2686 SkScalar dx = (*SegmentDXAtT[test->verb()])(test->pts(), tHit);
2687 #if DEBUG_WINDING
2688 SkDebugf("%s dx=%1.9g\n", __FUNCTION__, dx);
2689 #endif
2690 SkASSERT(dx != 0);
2691 if (winding * dx > 0) { // if same signs, result is negative
2692 winding += dx > 0 ? -1 : 1;
2693 #if DEBUG_WINDING
2694 SkDebugf("%s 3 winding=%d\n", __FUNCTION__, winding);
2695 #endif
2696 }
2697 }
2698 baseContour->setWinding(winding);
2699 return winding;
2700}
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002701
2702// OPTIMIZATION: not crazy about linear search here to find top active y.
2703// seems like we should break down and do the sort, or maybe sort each
2704// contours' segments?
2705// Once the segment array is built, there's no reason I can think of not to
2706// sort it in Y. hmmm
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002707// FIXME: return the contour found to pass to inner contour check
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002708static Segment* findTopContour(SkTDArray<Contour*>& contourList,
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002709 Contour*& topContour) {
2710 int contourCount = contourList.count();
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002711 int cIndex = 0;
2712 Segment* topStart;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002713 SkScalar bestY = SK_ScalarMax;
2714 Contour* contour;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002715 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002716 contour = contourList[cIndex];
2717 topStart = contour->topSegment(bestY);
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002718 } while (!topStart && ++cIndex < contourCount);
2719 if (!topStart) {
2720 return NULL;
2721 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002722 topContour = contour;
2723 while (++cIndex < contourCount) {
2724 contour = contourList[cIndex];
2725 if (bestY < contour->bounds().fTop) {
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002726 continue;
2727 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002728 SkScalar testY = SK_ScalarMax;
2729 Segment* test = contour->topSegment(testY);
2730 if (!test || bestY <= testY) {
2731 continue;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002732 }
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002733 topContour = contour;
2734 topStart = test;
2735 bestY = testY;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002736 }
2737 return topStart;
2738}
2739
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002740static Segment* findChase(SkTDArray<Span*>& chase, int& tIndex, int& endIndex) {
2741 while (chase.count()) {
2742 Span* span;
2743 chase.pop(&span);
2744 const Span& backPtr = span->fOther->span(span->fOtherIndex);
2745 Segment* segment = backPtr.fOther;
2746 tIndex = backPtr.fOtherIndex;
2747 if (segment->activeAngles(tIndex)) {
2748 endIndex = segment->nextSpan(tIndex, 1);
2749 if (span->fDone) {
2750 SkTDArray<Angle> angles;
2751 segment->addTwoAngles(endIndex, tIndex, angles);
2752 segment->buildAngles(tIndex, angles);
2753 SkTDArray<Angle*> sorted;
2754 sortAngles(angles, sorted);
2755 // find first angle, initialize winding to computed fWindSum
2756 int winding = span->fWindSum;
2757 int firstIndex = segment->findStartingEdge(sorted, endIndex, tIndex);
2758 int firstSign = sorted[firstIndex]->sign();
2759 if (firstSign * winding > 0) {
2760 winding -= firstSign;
2761 }
2762 SkDebugf("%s firstSign=%d\n", __FUNCTION__, firstSign);
2763 // we care about first sign and whether wind sum indicates this
2764 // edge is inside or outside. Maybe need to pass span winding
2765 // or first winding or something into this function?
2766 SkASSERT(firstIndex >= 0);
2767 // advance to first undone angle, then return it and winding
2768 // (to set whether edges are active or not)
2769 int nextIndex = firstIndex + 1;
2770 int angleCount = sorted.count();
2771 int lastIndex = firstIndex != 0 ? firstIndex : angleCount;
2772 do {
2773 SkASSERT(nextIndex != firstIndex);
2774 if (nextIndex == angleCount) {
2775 nextIndex = 0;
2776 }
2777 const Angle* angle = sorted[nextIndex];
2778 segment = angle->segment();
2779 int windValue = segment->windValue(angle);
2780 SkASSERT(windValue > 0);
2781 int maxWinding = winding;
2782 winding -= angle->sign() * windValue;
2783 if (maxWinding * winding < 0) {
2784 SkDebugf("%s flipped sign %d %d\n", __FUNCTION__, maxWinding, winding);
2785 }
2786 tIndex = angle->start();
2787 endIndex = angle->end();
2788 int lesser = SkMin32(tIndex, endIndex);
2789 const Span& nextSpan = segment->span(lesser);
2790 if (!nextSpan.fDone) {
2791 // FIXME: this be wrong. assign startWinding if edge is in
2792 // same direction. If the direction is opposite, winding to
2793 // assign is flipped sign or +/- 1?
2794 if (abs(maxWinding) < abs(winding)) {
2795 maxWinding = winding;
2796 }
2797 segment->markWinding(lesser, maxWinding);
2798 break;
2799 }
2800 } while (++nextIndex != lastIndex);
2801 } else {
2802 SkASSERT(endIndex > tIndex);
2803 }
2804 return segment;
2805 }
2806 }
2807 return NULL;
2808}
2809
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002810// Each segment may have an inside or an outside. Segments contained within
2811// winding may have insides on either side, and form a contour that should be
2812// ignored. Segments that are coincident with opposing direction segments may
2813// have outsides on either side, and should also disappear.
2814// 'Normal' segments will have one inside and one outside. Subsequent connections
2815// when winding should follow the intersection direction. If more than one edge
2816// is an option, choose first edge that continues the inside.
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002817 // since we start with leftmost top edge, we'll traverse through a
2818 // smaller angle counterclockwise to get to the next edge.
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002819static void bridge(SkTDArray<Contour*>& contourList, SkPath& simple) {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002820 bool firstContour = true;
caryclark@google.com15fa1382012-05-07 20:49:36 +00002821 do {
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002822 Contour* topContour;
2823 Segment* topStart = findTopContour(contourList, topContour);
caryclark@google.com15fa1382012-05-07 20:49:36 +00002824 if (!topStart) {
2825 break;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002826 }
caryclark@google.com15fa1382012-05-07 20:49:36 +00002827 // Start at the top. Above the top is outside, below is inside.
caryclark@google.com495f8e42012-05-31 13:13:11 +00002828 // follow edges to intersection by changing the index by direction.
2829 int index, endIndex;
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002830 Segment* current = topStart->findTop(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002831 int contourWinding;
2832 if (firstContour) {
2833 contourWinding = 0;
2834 firstContour = false;
2835 } else {
2836 const SkPoint& topPoint = current->xyAtT(endIndex);
2837 contourWinding = innerContourCheck(contourList, topContour, topPoint);
2838#if DEBUG_WINDING
2839 SkDebugf("%s contourWinding=%d\n", __FUNCTION__, contourWinding);
2840#endif
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002841 }
caryclark@google.com88f7d0c2012-06-07 21:09:20 +00002842 SkPoint lastPt;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002843 bool firstTime = true;
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002844 int winding = contourWinding;
caryclark@google.com8dcf1142012-07-02 20:27:02 +00002845 int spanWinding = current->spanSign(index, endIndex);
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002846 // int firstWinding = contourWinding + spanWinding;
2847 // FIXME: needs work. While it works in limited situations, it does
2848 // not always compute winding correctly. Active should be removed and instead
2849 // the initial winding should be correctly passed in so that if the
2850 // inner contour is wound the same way, it never finds an accumulated
2851 // winding of zero. Inside 'find next', we need to look for transitions
2852 // other than zero when resolving sorted angles.
2853 SkTDArray<Span*> chaseArray;
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002854 do {
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002855 bool active = winding * spanWinding <= 0;
2856 const SkPoint* firstPt = NULL;
2857 do {
2858 SkASSERT(!current->done());
2859 int nextStart, nextEnd, flipped = 1;
2860 Segment* next = current->findNext(chaseArray,
2861 winding + spanWinding, index,
2862 endIndex, nextStart, nextEnd, flipped, firstTime);
2863 if (!next) {
2864 break;
2865 }
2866 if (!firstPt) {
2867 firstPt = &current->addMoveTo(index, simple, active);
2868 }
2869 lastPt = current->addCurveTo(index, endIndex, simple, active);
2870 current = next;
2871 index = nextStart;
2872 endIndex = nextEnd;
2873 spanWinding = SkSign32(spanWinding) * flipped * next->windValue(
2874 SkMin32(nextStart, nextEnd));
2875 #if DEBUG_WINDING
2876 SkDebugf("%s spanWinding=%d\n", __FUNCTION__, spanWinding);
2877 #endif
2878 firstTime = false;
2879 } while (*firstPt != lastPt && (active || !current->done()));
2880 if (firstPt && active) {
2881 #if DEBUG_PATH_CONSTRUCTION
2882 SkDebugf("%s close\n", __FUNCTION__);
2883 #endif
2884 simple.close();
2885 }
2886 current = findChase(chaseArray, index, endIndex);
2887 if (!current) {
caryclark@google.com495f8e42012-05-31 13:13:11 +00002888 break;
2889 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002890 int lesser = SkMin32(index, endIndex);
2891 spanWinding = current->windSum(lesser);
2892 int spanValue = current->windValue(lesser);
2893 SkASSERT(spanWinding != SK_MinS32);
2894 int spanSign = current->spanSign(index, endIndex);
2895 #if DEBUG_WINDING
2896 SkDebugf("%s spanWinding=%d spanSign=%d winding=%d spanValue=%d\n",
2897 __FUNCTION__, spanWinding, spanSign, winding, spanValue);
2898 #endif
2899 if (spanWinding * spanSign < 0) {
2900 #if DEBUG_WINDING
2901 SkDebugf("%s spanWinding * spanSign < 0\n", __FUNCTION__);
2902 #endif
2903 SkTSwap<int>(index, endIndex);
caryclark@google.com495f8e42012-05-31 13:13:11 +00002904 }
caryclark@google.comfa4a6e92012-07-11 17:52:32 +00002905 if (abs(spanWinding) > spanValue) {
2906 #if DEBUG_WINDING
2907 SkDebugf("%s abs(spanWinding) > spanValue\n", __FUNCTION__);
2908 #endif
2909 winding = spanWinding;
2910 spanWinding = spanValue * SkSign32(spanWinding);
2911 winding -= spanWinding;
2912 }
2913 } while (true);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002914 } while (true);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002915}
2916
caryclark@google.comb45a1b42012-05-18 20:50:33 +00002917static void fixOtherTIndex(SkTDArray<Contour*>& contourList) {
2918 int contourCount = contourList.count();
2919 for (int cTest = 0; cTest < contourCount; ++cTest) {
2920 Contour* contour = contourList[cTest];
2921 contour->fixOtherTIndex();
2922 }
2923}
2924
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002925static void makeContourList(SkTArray<Contour>& contours,
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002926 SkTDArray<Contour*>& list) {
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002927 int count = contours.count();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002928 if (count == 0) {
2929 return;
2930 }
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002931 for (int index = 0; index < count; ++index) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002932 *list.append() = &contours[index];
2933 }
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002934 QSort<Contour>(list.begin(), list.end() - 1);
2935}
2936
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002937void simplifyx(const SkPath& path, SkPath& simple) {
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002938 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002939 int winding = (path.getFillType() & 1) ? 1 : -1;
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002940 simple.reset();
2941 simple.setFillType(SkPath::kEvenOdd_FillType);
2942
2943 // turn path into list of segments
2944 SkTArray<Contour> contours;
2945 // FIXME: add self-intersecting cubics' T values to segment
2946 EdgeBuilder builder(path, contours);
2947 SkTDArray<Contour*> contourList;
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002948 makeContourList(contours, contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002949 Contour** currentPtr = contourList.begin();
2950 if (!currentPtr) {
2951 return;
2952 }
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002953 Contour** listEnd = contourList.end();
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002954 // find all intersections between segments
2955 do {
2956 Contour** nextPtr = currentPtr;
2957 Contour* current = *currentPtr++;
2958 Contour* next;
2959 do {
2960 next = *nextPtr++;
caryclark@google.com65f9f0a2012-05-23 18:09:25 +00002961 } while (addIntersectTs(current, next) && nextPtr != listEnd);
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002962 } while (currentPtr != listEnd);
caryclark@google.coma833b5c2012-04-30 19:38:50 +00002963 // eat through coincident edges
2964 coincidenceCheck(contourList, winding);
caryclark@google.com66ca2fb2012-07-03 14:30:08 +00002965 fixOtherTIndex(contourList);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002966 // construct closed contours
caryclark@google.com1577e8f2012-05-22 17:01:14 +00002967 bridge(contourList, simple);
caryclark@google.comfa0588f2012-04-26 21:01:06 +00002968}
2969